diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index a2215630a8526..d106a83a4fc9f 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -648,6 +648,9 @@ impl Archetypes { self.archetypes.get(id.index()) } + /// # Panics + /// + /// Panics if `a` and `b` are equal. #[inline] pub(crate) fn get_2_mut( &mut self, diff --git a/crates/bevy_ecs/src/storage/blob_vec.rs b/crates/bevy_ecs/src/storage/blob_vec.rs index 49b370b7b8e3a..1f7f636fb6c7f 100644 --- a/crates/bevy_ecs/src/storage/blob_vec.rs +++ b/crates/bevy_ecs/src/storage/blob_vec.rs @@ -10,7 +10,9 @@ use bevy_utils::OnDrop; /// A flat, type-erased data storage type /// -/// Used to densely store homogeneous ECS data. +/// Used to densely store homogeneous ECS data. A blob is usually just an arbitrary block of contiguous memory without any identity, and +/// could be used to represent any arbitrary data (i.e. string, arrays, etc). This type is an extendable and reallcatable blob, which makes +/// it a blobby Vec, a `BlobVec`. pub(super) struct BlobVec { item_layout: Layout, capacity: usize, @@ -35,6 +37,13 @@ impl std::fmt::Debug for BlobVec { } impl BlobVec { + /// Creates a new [`BlobVec`] with the specified `capacity`. + /// + /// `drop` is an optional function pointer that is meant to be invoked when any element in the [`BlobVec`] + /// should be dropped. For all Rust-based types, this should match 1:1 with the implementation of [`Drop`] + /// if present, and should be `None` if `T: !Drop`. For non-Rust based types, this should match any cleanup + /// processes typically associated with the stored element. + /// /// # Safety /// /// `drop` should be safe to call with an [`OwningPtr`] pointing to any item that's been pushed into this [`BlobVec`]. @@ -42,6 +51,7 @@ impl BlobVec { /// If `drop` is `None`, the items will be leaked. This should generally be set as None based on [`needs_drop`]. /// /// [`needs_drop`]: core::mem::needs_drop + /// [`Drop`]: core::ops::Drop pub unsafe fn new( item_layout: Layout, drop: Option)>, @@ -70,26 +80,36 @@ impl BlobVec { } } + /// Returns the number of elements in the vector. #[inline] pub fn len(&self) -> usize { self.len } + /// Returns `true` if the vector contains no elements. #[inline] pub fn is_empty(&self) -> bool { self.len == 0 } + /// Returns the total number of elements the vector can hold without reallocating. #[inline] pub fn capacity(&self) -> usize { self.capacity } + /// Returns the [`Layout`] of the element type stored in the vector. #[inline] pub fn layout(&self) -> Layout { self.item_layout } + /// Reserves the minimum capacity for at least `additional` more elements to be inserted in the given `BlobVec`. + /// After calling `reserve_exact`, capacity will be greater than or equal to `self.len() + additional`. Does nothing if + /// the capacity is already sufficient. + /// + /// Note that the allocator may give the collection more space than it requests. Therefore, capacity can not be relied upon + /// to be precisely minimal. pub fn reserve_exact(&mut self, additional: usize) { let available_space = self.capacity - self.len; if available_space < additional && self.item_layout.size() > 0 { @@ -134,6 +154,8 @@ impl BlobVec { self.capacity = new_capacity; } + /// Initializes the value at `index` to `value`. This function does not do any bounds checking. + /// /// # Safety /// - index must be in bounds /// - the memory in the [`BlobVec`] starting at index `index`, of a size matching this [`BlobVec`]'s @@ -145,6 +167,8 @@ impl BlobVec { std::ptr::copy_nonoverlapping::(value.as_ptr(), ptr.as_ptr(), self.item_layout.size()); } + /// Replaces the value at `index` with `value`. This function does not do any bounds checking. + /// /// # Safety /// - index must be in-bounds /// - the memory in the [`BlobVec`] starting at index `index`, of a size matching this @@ -201,10 +225,10 @@ impl BlobVec { std::ptr::copy_nonoverlapping::(source, destination.as_ptr(), self.item_layout.size()); } - /// Pushes a value to the [`BlobVec`]. + /// Appends an element to the back of the vector. /// /// # Safety - /// `value` must be valid to add to this [`BlobVec`] + /// The `value` must match the [`layout`](`BlobVec::layout`) of the elements in the [`BlobVec`]. #[inline] pub unsafe fn push(&mut self, value: OwningPtr<'_>) { self.reserve_exact(1); @@ -213,6 +237,8 @@ impl BlobVec { self.initialize_unchecked(index, value); } + /// Forces the length of the vector to `len`. + /// /// # Safety /// `len` must be <= `capacity`. if length is decreased, "out of bounds" items must be dropped. /// Newly added items must be immediately populated with valid values and length must be @@ -255,6 +281,7 @@ impl BlobVec { /// Removes the value at `index` and copies the value stored into `ptr`. /// Does not do any bounds checking on `index`. + /// The removed element is replaced by the last element of the `BlobVec`. /// /// # Safety /// It is the caller's responsibility to ensure that `index` is < `self.len()` @@ -274,6 +301,10 @@ impl BlobVec { self.len -= 1; } + /// Removes the value at `index` and drops it. + /// Does not do any bounds checking on `index`. + /// The removed element is replaced by the last element of the `BlobVec`. + /// /// # Safety /// It is the caller's responsibility to ensure that `index` is < self.len() #[inline] @@ -286,8 +317,10 @@ impl BlobVec { } } + /// Returns a reference to the element at `index`, without doing bounds checking. + /// /// # Safety - /// It is the caller's responsibility to ensure that `index` is < self.len() + /// It is the caller's responsibility to ensure that `index < self.len()`. #[inline] pub unsafe fn get_unchecked(&self, index: usize) -> Ptr<'_> { debug_assert!(index < self.len()); @@ -300,8 +333,10 @@ impl BlobVec { self.get_ptr().byte_add(index * size) } + /// Returns a mutable reference to the element at `index`, without doing bounds checking. + /// /// # Safety - /// It is the caller's responsibility to ensure that `index` is < self.len() + /// It is the caller's responsibility to ensure that `index < self.len()`. #[inline] pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> PtrMut<'_> { debug_assert!(index < self.len()); @@ -337,6 +372,9 @@ impl BlobVec { std::slice::from_raw_parts(self.data.as_ptr() as *const UnsafeCell, self.len) } + /// Clears the vector, removing (and dropping) all values. + /// + /// Note that this method has no effect on the allocated capacity of the vector. pub fn clear(&mut self) { let len = self.len; // We set len to 0 _before_ dropping elements for unwind safety. This ensures we don't diff --git a/crates/bevy_ecs/src/storage/mod.rs b/crates/bevy_ecs/src/storage/mod.rs index b2fab3fdcb590..2cabf44303040 100644 --- a/crates/bevy_ecs/src/storage/mod.rs +++ b/crates/bevy_ecs/src/storage/mod.rs @@ -1,4 +1,24 @@ //! Storage layouts for ECS data. +//! +//! This module implements the low-level collections that store data in a [`World`]. These all offer minimal and often +//! unsafe APIs, and have been made `pub` primarily for debugging and monitoring purposes. +//! +//! # Fetching Storages +//! Each of the below data stores can be fetched via [`Storages`], which can be fetched from a +//! [`World`] via [`World::storages`]. It exposes a top level container for each class of storage within +//! ECS: +//! +//! - [`Tables`] - columnar contiguous blocks of memory, optimized for fast iteration. +//! - [`SparseSets`] - sparse `HashMap`-like mappings from entities to components, optimized for random +//! lookup and regular insertion/removal of components. +//! - [`Resources`] - singleton storage for the resources in the world +//! +//! # Safety +//! To avoid trivially unsound use of the APIs in this module, it is explicitly impossible to get a mutable +//! reference to [`Storages`] from [`World`], and none of the types publicly expose a mutable interface. +//! +//! [`World`]: crate::world::World +//! [`World::storages`]: crate::world::World::storages mod blob_vec; mod resource; @@ -12,8 +32,12 @@ pub use table::*; /// The raw data stores of a [World](crate::world::World) #[derive(Default)] pub struct Storages { + /// Backing storage for [`SparseSet`] components. pub sparse_sets: SparseSets, + /// Backing storage for [`Table`] components. pub tables: Tables, + /// Backing storage for resources. pub resources: Resources, + /// Backing storage for `!Send` resources. pub non_send_resources: Resources, } diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index f1a0e9fb53552..bdebad152e716 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -42,6 +42,10 @@ impl ResourceData { /// The only row in the underlying column. const ROW: TableRow = TableRow::new(0); + /// Validates the access to `!Send` resources is only done on the thread they were created from. + /// + /// # Panics + /// If `SEND` is false, this will panic if called from a different thread than the one it was inserted from. #[inline] fn validate_access(&self) { if SEND { @@ -70,7 +74,7 @@ impl ResourceData { self.id } - /// Gets a read-only pointer to the underlying resource, if available. + /// Returns a reference to the resource, if it exists. /// /// # Panics /// If `SEND` is false, this will panic if a value is present and is not accessed from the @@ -83,12 +87,14 @@ impl ResourceData { }) } - /// Gets a read-only reference to the change ticks of the underlying resource, if available. + /// Returns a reference to the resource's change ticks, if it exists. #[inline] pub fn get_ticks(&self) -> Option { self.column.get_ticks(Self::ROW) } + /// Returns references to the resource and its change ticks, if it exists. + /// /// # Panics /// If `SEND` is false, this will panic if a value is present and is not accessed from the /// original thread it was inserted in. @@ -100,6 +106,11 @@ impl ResourceData { }) } + /// Returns a mutable reference to the resource, if it exists. + /// + /// # Panics + /// If `SEND` is false, this will panic if a value is present and is not accessed from the + /// original thread it was inserted in. pub(crate) fn get_mut( &mut self, last_change_tick: u32, diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index 54412b54c25dd..217609dc8c5ac 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -41,12 +41,16 @@ impl SparseArray { macro_rules! impl_sparse_array { ($ty:ident) => { impl $ty { + /// Returns `true` if the collection contains a value for the specified `index`. #[inline] pub fn contains(&self, index: I) -> bool { let index = index.sparse_set_index(); self.values.get(index).map(|v| v.is_some()).unwrap_or(false) } + /// Returns a reference to the value at `index`. + /// + /// Returns `None` if `index` does not have a value or if `index` is out of bounds. #[inline] pub fn get(&self, index: I) -> Option<&V> { let index = index.sparse_set_index(); @@ -60,6 +64,9 @@ impl_sparse_array!(SparseArray); impl_sparse_array!(ImmutableSparseArray); impl SparseArray { + /// Inserts `value` at `index` in the array. + /// + /// If `index` is out-of-bounds, this will enlarge the buffer to accommodate it. #[inline] pub fn insert(&mut self, index: I, value: V) { let index = index.sparse_set_index(); @@ -69,6 +76,9 @@ impl SparseArray { self.values[index] = Some(value); } + /// Returns a mutable reference to the value at `index`. + /// + /// Returns `None` if `index` does not have a value or if `index` is out of bounds. #[inline] pub fn get_mut(&mut self, index: I) -> Option<&mut V> { let index = index.sparse_set_index(); @@ -78,16 +88,21 @@ impl SparseArray { .unwrap_or(None) } + /// Removes and returns the value stored at `index`. + /// + /// Returns `None` if `index` did not have a value or if `index` is out of bounds. #[inline] pub fn remove(&mut self, index: I) -> Option { let index = index.sparse_set_index(); self.values.get_mut(index).and_then(|value| value.take()) } + /// Removes all of the values stored within. pub fn clear(&mut self) { self.values.clear(); } + /// Converts the [`SparseArray`] into an immutable variant. pub(crate) fn into_immutable(self) -> ImmutableSparseArray { ImmutableSparseArray { values: self.values.into_boxed_slice(), @@ -113,6 +128,8 @@ pub struct ComponentSparseSet { } impl ComponentSparseSet { + /// Creates a new [`ComponentSparseSet`] with a given component type layout and + /// initial `capacity`. pub(crate) fn new(component_info: &ComponentInfo, capacity: usize) -> Self { Self { dense: Column::with_capacity(component_info, capacity), @@ -121,17 +138,20 @@ impl ComponentSparseSet { } } + /// Removes all of the values stored within. pub(crate) fn clear(&mut self) { self.dense.clear(); self.entities.clear(); self.sparse.clear(); } + /// Returns the number of component values in the sparse set. #[inline] pub fn len(&self) -> usize { self.dense.len() } + /// Returns `true` if the sparse set contains no component values. #[inline] pub fn is_empty(&self) -> bool { self.dense.len() == 0 @@ -162,6 +182,7 @@ impl ComponentSparseSet { } } + /// Returns `true` if the sparse set has a component value for the provided `entity`. #[inline] pub fn contains(&self, entity: Entity) -> bool { #[cfg(debug_assertions)] @@ -178,6 +199,9 @@ impl ComponentSparseSet { self.sparse.contains(entity.index()) } + /// Returns a reference to the entity's component value. + /// + /// Returns `None` if `entity` does not have a component in the sparse set. #[inline] pub fn get(&self, entity: Entity) -> Option> { self.sparse.get(entity.index()).map(|dense_index| { @@ -189,6 +213,9 @@ impl ComponentSparseSet { }) } + /// Returns references to the entity's component value and its added and changed ticks. + /// + /// Returns `None` if `entity` does not have a component in the sparse set. #[inline] pub fn get_with_ticks(&self, entity: Entity) -> Option<(Ptr<'_>, TickCells<'_>)> { let dense_index = TableRow::new(*self.sparse.get(entity.index())? as usize); @@ -206,6 +233,9 @@ impl ComponentSparseSet { } } + /// Returns a reference to the "added" tick of the entity's component value. + /// + /// Returns `None` if `entity` does not have a component in the sparse set. #[inline] pub fn get_added_ticks(&self, entity: Entity) -> Option<&UnsafeCell> { let dense_index = *self.sparse.get(entity.index())? as usize; @@ -220,6 +250,9 @@ impl ComponentSparseSet { } } + /// Returns a reference to the "changed" tick of the entity's component value. + /// + /// Returns `None` if `entity` does not have a component in the sparse set. #[inline] pub fn get_changed_ticks(&self, entity: Entity) -> Option<&UnsafeCell> { let dense_index = *self.sparse.get(entity.index())? as usize; @@ -234,6 +267,9 @@ impl ComponentSparseSet { } } + /// Returns a reference to the "added" and "changed" ticks of the entity's component value. + /// + /// Returns `None` if `entity` does not have a component in the sparse set. #[inline] pub fn get_ticks(&self, entity: Entity) -> Option { let dense_index = *self.sparse.get(entity.index())? as usize; @@ -270,6 +306,9 @@ impl ComponentSparseSet { }) } + /// Removes (and drops) the entity's component value from the sparse set. + /// + /// Returns `true` if `entity` had a component value in the sparse set. pub(crate) fn remove(&mut self, entity: Entity) -> bool { if let Some(dense_index) = self.sparse.remove(entity.index()) { let dense_index = dense_index as usize; @@ -320,16 +359,21 @@ pub(crate) struct ImmutableSparseSet { macro_rules! impl_sparse_set { ($ty:ident) => { impl $ty { + /// Returns the number of elements in the sparse set. #[inline] pub fn len(&self) -> usize { self.dense.len() } + /// Returns `true` if the sparse set contains a value for `index`. #[inline] pub fn contains(&self, index: I) -> bool { self.sparse.contains(index) } + /// Returns a reference to the value for `index`. + /// + /// Returns `None` if `index` does not have a value in the sparse set. pub fn get(&self, index: I) -> Option<&V> { self.sparse.get(index).map(|dense_index| { // SAFETY: if the sparse index points to something in the dense vec, it exists @@ -337,6 +381,9 @@ macro_rules! impl_sparse_set { }) } + /// Returns a mutable reference to the value for `index`. + /// + /// Returns `None` if `index` does not have a value in the sparse set. pub fn get_mut(&mut self, index: I) -> Option<&mut V> { let dense = &mut self.dense; self.sparse.get(index).map(move |dense_index| { @@ -345,22 +392,27 @@ macro_rules! impl_sparse_set { }) } + /// Returns an iterator visiting all keys (indices) in arbitrary order. pub fn indices(&self) -> impl Iterator + '_ { self.indices.iter().cloned() } + /// Returns an iterator visiting all values in arbitrary order. pub fn values(&self) -> impl Iterator { self.dense.iter() } + /// Returns an iterator visiting all values mutably in arbitrary order. pub fn values_mut(&mut self) -> impl Iterator { self.dense.iter_mut() } + /// Returns an iterator visiting all key-value pairs in arbitrary order, with references to the values. pub fn iter(&self) -> impl Iterator { self.indices.iter().zip(self.dense.iter()) } + /// Returns an iterator visiting all key-value pairs in arbitrary order, with mutable references to the values. pub fn iter_mut(&mut self) -> impl Iterator { self.indices.iter().zip(self.dense.iter_mut()) } @@ -378,6 +430,7 @@ impl Default for SparseSet { } impl SparseSet { + /// Creates a new [`SparseSet`]. pub const fn new() -> Self { Self { dense: Vec::new(), @@ -388,6 +441,7 @@ impl SparseSet { } impl SparseSet { + /// Creates a new [`SparseSet`] with a specified initial capacity. pub fn with_capacity(capacity: usize) -> Self { Self { dense: Vec::with_capacity(capacity), @@ -396,11 +450,15 @@ impl SparseSet { } } + /// Returns the total number of elements the [`SparseSet`] can hold without needing to reallocate. #[inline] pub fn capacity(&self) -> usize { self.dense.capacity() } + /// Inserts `value` at `index`. + /// + /// If a value was already present at `index`, it will be overwritten. pub fn insert(&mut self, index: I, value: V) { if let Some(dense_index) = self.sparse.get(index.clone()).cloned() { // SAFETY: dense indices stored in self.sparse always exist @@ -414,6 +472,8 @@ impl SparseSet { } } + /// Returns a reference to the value for `index`, inserting one computed from `func` + /// if not already present. pub fn get_or_insert_with(&mut self, index: I, func: impl FnOnce() -> V) -> &mut V { if let Some(dense_index) = self.sparse.get(index.clone()).cloned() { // SAFETY: dense indices stored in self.sparse always exist @@ -429,11 +489,15 @@ impl SparseSet { } } + /// Returns `true` if the sparse set contains no elements. #[inline] pub fn is_empty(&self) -> bool { self.dense.len() == 0 } + /// Removes and returns the value for `index`. + /// + /// Returns `None` if `index` does not have a value in the sparse set. pub fn remove(&mut self, index: I) -> Option { self.sparse.remove(index).map(|dense_index| { let is_last = dense_index == self.dense.len() - 1; @@ -447,12 +511,14 @@ impl SparseSet { }) } + /// Clears all of the elements from the sparse set. pub fn clear(&mut self) { self.dense.clear(); self.indices.clear(); self.sparse.clear(); } + /// Converts the sparse set into its immutable variant. pub(crate) fn into_immutable(self) -> ImmutableSparseSet { ImmutableSparseSet { dense: self.dense.into_boxed_slice(), diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index a8077be288631..d456d1f900b56 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -33,16 +33,24 @@ pub struct TableId(u32); impl TableId { pub(crate) const INVALID: TableId = TableId(u32::MAX); + /// Creates a new [`TableId`]. + /// + /// `index` *must* be retrieved from calling [`TableId::index`] on a `TableId` you got + /// from a table of a given [`World`] or the created ID may be invalid. + /// + /// [`World`]: crate::world::World #[inline] pub fn new(index: usize) -> Self { TableId(index as u32) } + /// Gets the underlying table index from the ID. #[inline] pub fn index(self) -> usize { self.0 as usize } + /// The [`TableId`] of the [`Table`] without any components. #[inline] pub const fn empty() -> TableId { TableId(0) @@ -71,7 +79,7 @@ impl TableId { pub struct TableRow(u32); impl TableRow { - pub const INVALID: TableRow = TableRow(u32::MAX); + pub(crate) const INVALID: TableRow = TableRow(u32::MAX); /// Creates a `TableRow`. #[inline] @@ -86,6 +94,19 @@ impl TableRow { } } +/// A type-erased contiguous container for data of a homogenous type. +/// +/// Conceptually, a [`Column`] is very similar to a type-erased `Vec`. +/// It also stores the change detection ticks for its components, kept in two separate +/// contiguous buffers internally. An element shares its data across these buffers by using the +/// same index (i.e. the entity at row 3 has it's data at index 3 and its change detection ticks at +/// index 3). A slice to these contiguous blocks of memory can be fetched +/// via [`Column::get_data_slice`], [`Column::get_added_ticks_slice`], and +/// [`Column::get_changed_ticks_slice`]. +/// +/// Like many other low-level storage types, [`Column`] has a limited and highly unsafe +/// interface. It's highly advised to use higher level types and their safe abstractions +/// instead of working directly with [`Column`]. #[derive(Debug)] pub struct Column { data: BlobVec, @@ -94,6 +115,7 @@ pub struct Column { } impl Column { + /// Constructs a new [`Column`], configured with a component's layout and an initial `capacity`. #[inline] pub(crate) fn with_capacity(component_info: &ComponentInfo, capacity: usize) -> Self { Column { @@ -104,6 +126,7 @@ impl Column { } } + /// Fetches the [`Layout`] for the underlying type. #[inline] pub fn item_layout(&self) -> Layout { self.data.layout() @@ -150,18 +173,29 @@ impl Column { self.data.replace_unchecked(row.index(), data); } + /// Gets the current number of elements stored in the column. #[inline] pub fn len(&self) -> usize { self.data.len() } + /// Checks if the column is empty. Returns `true` if there are no elements, `false` otherwise. #[inline] pub fn is_empty(&self) -> bool { self.data.is_empty() } + /// Removes an element from the [`Column`]. + /// + /// - The value will be dropped if it implements [`Drop`]. + /// - This does not preserve ordering, but is O(1). + /// - This does not do any bounds checking. + /// - The element is replaced with the last element in the [`Column`]. + /// /// # Safety - /// index must be in-bounds + /// `row` must be within the range `[0, self.len())`. + /// + /// [`Drop`]: std::ops::Drop #[inline] pub(crate) unsafe fn swap_remove_unchecked(&mut self, row: TableRow) { self.data.swap_remove_and_drop_unchecked(row.index()); @@ -169,6 +203,16 @@ impl Column { self.changed_ticks.swap_remove(row.index()); } + /// Removes an element from the [`Column`] and returns it and its change detection ticks. + /// This does not preserve ordering, but is O(1). + /// + /// The element is replaced with the last element in the [`Column`]. + /// + /// It is the caller's responsibility to ensure that the removed value is dropped or used. + /// Failure to do so may result in resources not being released (i.e. files handles not being + /// released, memory leaks, etc.) + /// + /// Returns `None` if `row` is out of bounds. #[inline] #[must_use = "The returned pointer should be used to drop the removed component"] pub(crate) fn swap_remove_and_forget( @@ -184,8 +228,18 @@ impl Column { }) } + /// Removes an element from the [`Column`] and returns it and its change detection ticks. + /// This does not preserve ordering, but is O(1). Unlike [`Column::swap_remove_and_forget`] + /// this does not do any bounds checking. + /// + /// The element is replaced with the last element in the [`Column`]. + /// + /// It's the caller's responsibility to ensure that the removed value is dropped or used. + /// Failure to do so may result in resources not being released (i.e. files handles not being + /// released, memory leaks, etc.) + /// /// # Safety - /// index must be in-bounds + /// `row` must be within the range `[0, self.len())`. #[inline] #[must_use = "The returned pointer should be used to dropped the removed component"] pub(crate) unsafe fn swap_remove_and_forget_unchecked( @@ -225,8 +279,10 @@ impl Column { other.changed_ticks.swap_remove(src_row.index()); } - // # Safety - // - ptr must point to valid data of this column's component type + /// Pushes a new value onto the end of the [`Column`]. + /// + /// # Safety + /// `ptr` must point to valid data of this column's component type pub(crate) unsafe fn push(&mut self, ptr: OwningPtr<'_>, ticks: ComponentTicks) { self.data.push(ptr); self.added_ticks.push(UnsafeCell::new(ticks.added)); @@ -240,27 +296,57 @@ impl Column { self.changed_ticks.reserve_exact(additional); } + /// Fetches the data pointer to the first element of the [`Column`]. + /// + /// The pointer is type erased, so using this function to fetch anything + /// other than the first element will require computing the offset using + /// [`Column::item_layout`]. #[inline] pub fn get_data_ptr(&self) -> Ptr<'_> { self.data.get_ptr() } + /// Fetches the slice to the [`Column`]'s data cast to a given type. + /// + /// Note: The values stored within are [`UnsafeCell`]. + /// Users of this API must ensure that accesses to each individual element + /// adhere to the safety invariants of [`UnsafeCell`]. + /// /// # Safety /// The type `T` must be the type of the items in this column. + /// + /// [`UnsafeCell`]: std::cell::UnsafeCell pub unsafe fn get_data_slice(&self) -> &[UnsafeCell] { self.data.get_slice() } + /// Fetches the slice to the [`Column`]'s "added" change detection ticks. + /// + /// Note: The values stored within are [`UnsafeCell`]. + /// Users of this API must ensure that accesses to each individual element + /// adhere to the safety invariants of [`UnsafeCell`]. + /// + /// [`UnsafeCell`]: std::cell::UnsafeCell #[inline] pub fn get_added_ticks_slice(&self) -> &[UnsafeCell] { &self.added_ticks } + /// Fetches the slice to the [`Column`]'s "changed" change detection ticks. + /// + /// Note: The values stored within are [`UnsafeCell`]. + /// Users of this API must ensure that accesses to each individual element + /// adhere to the safety invariants of [`UnsafeCell`]. + /// + /// [`UnsafeCell`]: std::cell::UnsafeCell #[inline] pub fn get_changed_ticks_slice(&self) -> &[UnsafeCell] { &self.changed_ticks } + /// Fetches a reference to the data and change detection ticks at `row`. + /// + /// Returns `None` if `row` is out of bounds. #[inline] pub fn get(&self, row: TableRow) -> Option<(Ptr<'_>, TickCells<'_>)> { (row.index() < self.data.len()) @@ -277,6 +363,9 @@ impl Column { }) } + /// Fetches a read-only reference to the data at `row`. + /// + /// Returns `None` if `row` is out of bounds. #[inline] pub fn get_data(&self, row: TableRow) -> Option> { // SAFETY: The row is length checked before fetching the pointer. This is being @@ -284,15 +373,21 @@ impl Column { (row.index() < self.data.len()).then(|| unsafe { self.data.get_unchecked(row.index()) }) } + /// Fetches a read-only reference to the data at `row`. Unlike [`Column::get`] this does not + /// do any bounds checking. + /// /// # Safety - /// - index must be in-bounds - /// - no other reference to the data of the same row can exist at the same time + /// - `row` must be within the range `[0, self.len())`. + /// - no other mutable reference to the data of the same row can exist at the same time #[inline] pub unsafe fn get_data_unchecked(&self, row: TableRow) -> Ptr<'_> { debug_assert!(row.index() < self.data.len()); self.data.get_unchecked(row.index()) } + /// Fetches a mutable reference to the data at `row`. + /// + /// Returns `None` if `row` is out of bounds. #[inline] pub fn get_data_mut(&mut self, row: TableRow) -> Option> { // SAFETY: The row is length checked before fetching the pointer. This is being @@ -300,6 +395,9 @@ impl Column { (row.index() < self.data.len()).then(|| unsafe { self.data.get_unchecked_mut(row.index()) }) } + /// Fetches a mutable reference to the data at `row`. Unlike [`Column::get_data_mut`] this does not + /// do any bounds checking. + /// /// # Safety /// - index must be in-bounds /// - no other reference to the data of the same row can exist at the same time @@ -309,16 +407,37 @@ impl Column { self.data.get_unchecked_mut(row.index()) } + /// Fetches the "added" change detection ticks for the value at `row`. + /// + /// Returns `None` if `row` is out of bounds. + /// + /// Note: The values stored within are [`UnsafeCell`]. + /// Users of this API must ensure that accesses to each individual element + /// adhere to the safety invariants of [`UnsafeCell`]. + /// + /// [`UnsafeCell`]: std::cell::UnsafeCell #[inline] pub fn get_added_ticks(&self, row: TableRow) -> Option<&UnsafeCell> { self.added_ticks.get(row.index()) } + /// Fetches the "changed" change detection ticks for the value at `row`. + /// + /// Returns `None` if `row` is out of bounds. + /// + /// Note: The values stored within are [`UnsafeCell`]. + /// Users of this API must ensure that accesses to each individual element + /// adhere to the safety invariants of [`UnsafeCell`]. + /// + /// [`UnsafeCell`]: std::cell::UnsafeCell #[inline] pub fn get_changed_ticks(&self, row: TableRow) -> Option<&UnsafeCell> { self.changed_ticks.get(row.index()) } + /// Fetches the change detection ticks for the value at `row`. + /// + /// Returns `None` if `row` is out of bounds. #[inline] pub fn get_ticks(&self, row: TableRow) -> Option { if row.index() < self.data.len() { @@ -329,24 +448,33 @@ impl Column { } } + /// Fetches the "added" change detection ticks for the value at `row`. Unlike [`Column::get_added_ticks`] + /// this function does not do any bounds checking. + /// /// # Safety - /// index must be in-bounds + /// `row` must be within the range `[0, self.len())`. #[inline] pub unsafe fn get_added_ticks_unchecked(&self, row: TableRow) -> &UnsafeCell { debug_assert!(row.index() < self.added_ticks.len()); self.added_ticks.get_unchecked(row.index()) } + /// Fetches the "changed" change detection ticks for the value at `row`. Unlike [`Column::get_changed_ticks`] + /// this function does not do any bounds checking. + /// /// # Safety - /// index must be in-bounds + /// `row` must be within the range `[0, self.len())`. #[inline] pub unsafe fn get_changed_ticks_unchecked(&self, row: TableRow) -> &UnsafeCell { debug_assert!(row.index() < self.changed_ticks.len()); self.changed_ticks.get_unchecked(row.index()) } + /// Fetches the change detection ticks for the value at `row`. Unlike [`Column::get_ticks`] + /// this function does not do any bounds checking. + /// /// # Safety - /// index must be in-bounds + /// `row` must be within the range `[0, self.len())`. #[inline] pub unsafe fn get_ticks_unchecked(&self, row: TableRow) -> ComponentTicks { debug_assert!(row.index() < self.added_ticks.len()); @@ -357,6 +485,9 @@ impl Column { } } + /// Clears the column, removing all values. + /// + /// Note that this function has no effect on the allocated capacity of the [`Column`]> pub fn clear(&mut self) { self.data.clear(); self.added_ticks.clear(); @@ -417,10 +548,10 @@ impl TableBuilder { /// in a [`World`]. /// /// Conceptually, a `Table` can be thought of as an `HashMap`, where -/// each `Column` is a type-erased `Vec`. Each row corresponds to a single entity +/// each [`Column`] is a type-erased `Vec`. Each row corresponds to a single entity /// (i.e. index 3 in Column A and index 3 in Column B point to different components on the same /// entity). Fetching components from a table involves fetching the associated column for a -/// component type (via it's [`ComponentId`]), then fetching the entity's row within that column. +/// component type (via its [`ComponentId`]), then fetching the entity's row within that column. /// /// [structure-of-arrays]: https://en.wikipedia.org/wiki/AoS_and_SoA#Structure_of_arrays /// [`Component`]: crate::component::Component @@ -431,6 +562,7 @@ pub struct Table { } impl Table { + /// Fetches a read-only slice of the entities stored within the [`Table`]. #[inline] pub fn entities(&self) -> &[Entity] { &self.entities @@ -457,7 +589,8 @@ impl Table { /// Moves the `row` column values to `new_table`, for the columns shared between both tables. /// Returns the index of the new row in `new_table` and the entity in this table swapped in /// to replace it (if an entity was swapped in). missing columns will be "forgotten". It is - /// the caller's responsibility to drop them + /// the caller's responsibility to drop them. Failure to do so may result in resources not + /// being released (i.e. files handles not being released, memory leaks, etc.) /// /// # Safety /// Row must be in-bounds @@ -548,21 +681,39 @@ impl Table { } } + /// Fetches a read-only reference to the [`Column`] for a given [`Component`] within the + /// table. + /// + /// Returns `None` if the corresponding component does not belong to the table. + /// + /// [`Component`]: crate::component::Component #[inline] pub fn get_column(&self, component_id: ComponentId) -> Option<&Column> { self.columns.get(component_id) } + /// Fetches a mutable reference to the [`Column`] for a given [`Component`] within the + /// table. + /// + /// Returns `None` if the corresponding component does not belong to the table. + /// + /// [`Component`]: crate::component::Component #[inline] pub(crate) fn get_column_mut(&mut self, component_id: ComponentId) -> Option<&mut Column> { self.columns.get_mut(component_id) } + /// Checks if the table contains a [`Column`] for a given [`Component`]. + /// + /// Returns `true` if the column is present, `false` otherwise. + /// + /// [`Component`]: crate::component::Component #[inline] pub fn has_column(&self, component_id: ComponentId) -> bool { self.columns.contains(component_id) } + /// Reserves `additional` elements worth of capacity within the table. pub(crate) fn reserve(&mut self, additional: usize) { if self.entities.capacity() - self.entities.len() < additional { self.entities.reserve(additional); @@ -592,21 +743,28 @@ impl Table { TableRow::new(index) } + /// Gets the number of entities currently being stored in the table. #[inline] pub fn entity_count(&self) -> usize { self.entities.len() } + /// Gets the number of components being stored in the table. #[inline] pub fn component_count(&self) -> usize { self.columns.len() } + /// Gets the maximum number of entities the table can currently store + /// without reallocating the underlying memory. #[inline] pub fn entity_capacity(&self) -> usize { self.entities.capacity() } + /// Checks if the [`Table`] is empty or not. + /// + /// Returns `true` if the table contains no entities, `false` otherwise. #[inline] pub fn is_empty(&self) -> bool { self.entities.is_empty() @@ -618,10 +776,12 @@ impl Table { } } + /// Iterates over the [`Column`]s of the [`Table`]. pub fn iter(&self) -> impl Iterator { self.columns.values() } + /// Clears all of the stored components in the [`Table`]. pub(crate) fn clear(&mut self) { self.entities.clear(); for column in self.columns.values_mut() { @@ -666,11 +826,19 @@ impl Tables { self.tables.is_empty() } + /// Fetches a [`Table`] by its [`TableId`]. + /// + /// Returns `None` if `id` is invalid. #[inline] pub fn get(&self, id: TableId) -> Option<&Table> { self.tables.get(id.index()) } + /// Fetches mutable references to two different [`Table`]s. + /// + /// # Panics + /// + /// Panics if `a` and `b` are equal. #[inline] pub(crate) fn get_2_mut(&mut self, a: TableId, b: TableId) -> (&mut Table, &mut Table) { if a.index() > b.index() { @@ -682,6 +850,9 @@ impl Tables { } } + /// Attempts to fetch a table based on the provided components, + /// creating and returning a new [`Table`] if one did not already exist. + /// /// # Safety /// `component_ids` must contain components that exist in `components` pub(crate) unsafe fn get_id_or_insert( @@ -706,10 +877,12 @@ impl Tables { *value } + /// Iterates through all of the tables stored within in [`TableId`] order. pub fn iter(&self) -> std::slice::Iter<'_, Table> { self.tables.iter() } + /// Clears all data from all [`Table`]s stored within. pub(crate) fn clear(&mut self) { for table in &mut self.tables { table.clear();