Document bevy_ecs::storage (#7770)
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Rob Parrett <robparrett@gmail.com> Co-authored-by: Carter Weinberg <weinbergcarter@gmail.com> Co-authored-by: MinerSebas <66798382+MinerSebas@users.noreply.github.com> Co-authored-by: Cameron <51241057+maniwani@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									21ddc60372
								
							
						
					
					
						commit
						37df316219
					
				@ -648,6 +648,9 @@ impl Archetypes {
 | 
				
			|||||||
        self.archetypes.get(id.index())
 | 
					        self.archetypes.get(id.index())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// # Panics
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Panics if `a` and `b` are equal.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub(crate) fn get_2_mut(
 | 
					    pub(crate) fn get_2_mut(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,9 @@ use bevy_utils::OnDrop;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// A flat, type-erased data storage type
 | 
					/// 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 {
 | 
					pub(super) struct BlobVec {
 | 
				
			||||||
    item_layout: Layout,
 | 
					    item_layout: Layout,
 | 
				
			||||||
    capacity: usize,
 | 
					    capacity: usize,
 | 
				
			||||||
@ -35,6 +37,13 @@ impl std::fmt::Debug for BlobVec {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl 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
 | 
					    /// # Safety
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// `drop` should be safe to call with an [`OwningPtr`] pointing to any item that's been pushed into this [`BlobVec`].
 | 
					    /// `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`].
 | 
					    /// 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
 | 
					    /// [`needs_drop`]: core::mem::needs_drop
 | 
				
			||||||
 | 
					    /// [`Drop`]: core::ops::Drop
 | 
				
			||||||
    pub unsafe fn new(
 | 
					    pub unsafe fn new(
 | 
				
			||||||
        item_layout: Layout,
 | 
					        item_layout: Layout,
 | 
				
			||||||
        drop: Option<unsafe fn(OwningPtr<'_>)>,
 | 
					        drop: Option<unsafe fn(OwningPtr<'_>)>,
 | 
				
			||||||
@ -70,26 +80,36 @@ impl BlobVec {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the number of elements in the vector.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn len(&self) -> usize {
 | 
					    pub fn len(&self) -> usize {
 | 
				
			||||||
        self.len
 | 
					        self.len
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns `true` if the vector contains no elements.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn is_empty(&self) -> bool {
 | 
					    pub fn is_empty(&self) -> bool {
 | 
				
			||||||
        self.len == 0
 | 
					        self.len == 0
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the total number of elements the vector can hold without reallocating.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn capacity(&self) -> usize {
 | 
					    pub fn capacity(&self) -> usize {
 | 
				
			||||||
        self.capacity
 | 
					        self.capacity
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the [`Layout`] of the element type stored in the vector.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn layout(&self) -> Layout {
 | 
					    pub fn layout(&self) -> Layout {
 | 
				
			||||||
        self.item_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) {
 | 
					    pub fn reserve_exact(&mut self, additional: usize) {
 | 
				
			||||||
        let available_space = self.capacity - self.len;
 | 
					        let available_space = self.capacity - self.len;
 | 
				
			||||||
        if available_space < additional && self.item_layout.size() > 0 {
 | 
					        if available_space < additional && self.item_layout.size() > 0 {
 | 
				
			||||||
@ -134,6 +154,8 @@ impl BlobVec {
 | 
				
			|||||||
        self.capacity = new_capacity;
 | 
					        self.capacity = new_capacity;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Initializes the value at `index` to `value`. This function does not do any bounds checking.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
    /// # Safety
 | 
					    /// # Safety
 | 
				
			||||||
    /// - index must be in bounds
 | 
					    /// - index must be in bounds
 | 
				
			||||||
    /// - the memory in the [`BlobVec`] starting at index `index`, of a size matching this [`BlobVec`]'s
 | 
					    /// - 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::<u8>(value.as_ptr(), ptr.as_ptr(), self.item_layout.size());
 | 
					        std::ptr::copy_nonoverlapping::<u8>(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
 | 
					    /// # Safety
 | 
				
			||||||
    /// - index must be in-bounds
 | 
					    /// - index must be in-bounds
 | 
				
			||||||
    /// - the memory in the [`BlobVec`] starting at index `index`, of a size matching this
 | 
					    /// - the memory in the [`BlobVec`] starting at index `index`, of a size matching this
 | 
				
			||||||
@ -201,10 +225,10 @@ impl BlobVec {
 | 
				
			|||||||
        std::ptr::copy_nonoverlapping::<u8>(source, destination.as_ptr(), self.item_layout.size());
 | 
					        std::ptr::copy_nonoverlapping::<u8>(source, destination.as_ptr(), self.item_layout.size());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Pushes a value to the [`BlobVec`].
 | 
					    /// Appends an element to the back of the vector.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// # Safety
 | 
					    /// # 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]
 | 
					    #[inline]
 | 
				
			||||||
    pub unsafe fn push(&mut self, value: OwningPtr<'_>) {
 | 
					    pub unsafe fn push(&mut self, value: OwningPtr<'_>) {
 | 
				
			||||||
        self.reserve_exact(1);
 | 
					        self.reserve_exact(1);
 | 
				
			||||||
@ -213,6 +237,8 @@ impl BlobVec {
 | 
				
			|||||||
        self.initialize_unchecked(index, value);
 | 
					        self.initialize_unchecked(index, value);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Forces the length of the vector to `len`.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
    /// # Safety
 | 
					    /// # Safety
 | 
				
			||||||
    /// `len` must be <= `capacity`. if length is decreased, "out of bounds" items must be dropped.
 | 
					    /// `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
 | 
					    /// 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`.
 | 
					    /// Removes the value at `index` and copies the value stored into `ptr`.
 | 
				
			||||||
    /// Does not do any bounds checking on `index`.
 | 
					    /// Does not do any bounds checking on `index`.
 | 
				
			||||||
 | 
					    /// The removed element is replaced by the last element of the `BlobVec`.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// # Safety
 | 
					    /// # Safety
 | 
				
			||||||
    /// It is the caller's responsibility to ensure that `index` is < `self.len()`
 | 
					    /// It is the caller's responsibility to ensure that `index` is < `self.len()`
 | 
				
			||||||
@ -274,6 +301,10 @@ impl BlobVec {
 | 
				
			|||||||
        self.len -= 1;
 | 
					        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
 | 
					    /// # Safety
 | 
				
			||||||
    /// It is the caller's responsibility to ensure that `index` is < self.len()
 | 
					    /// It is the caller's responsibility to ensure that `index` is < self.len()
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
@ -286,8 +317,10 @@ impl BlobVec {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns a reference to the element at `index`, without doing bounds checking.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
    /// # Safety
 | 
					    /// # 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]
 | 
					    #[inline]
 | 
				
			||||||
    pub unsafe fn get_unchecked(&self, index: usize) -> Ptr<'_> {
 | 
					    pub unsafe fn get_unchecked(&self, index: usize) -> Ptr<'_> {
 | 
				
			||||||
        debug_assert!(index < self.len());
 | 
					        debug_assert!(index < self.len());
 | 
				
			||||||
@ -300,8 +333,10 @@ impl BlobVec {
 | 
				
			|||||||
        self.get_ptr().byte_add(index * size)
 | 
					        self.get_ptr().byte_add(index * size)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns a mutable reference to the element at `index`, without doing bounds checking.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
    /// # Safety
 | 
					    /// # 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]
 | 
					    #[inline]
 | 
				
			||||||
    pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> PtrMut<'_> {
 | 
					    pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> PtrMut<'_> {
 | 
				
			||||||
        debug_assert!(index < self.len());
 | 
					        debug_assert!(index < self.len());
 | 
				
			||||||
@ -337,6 +372,9 @@ impl BlobVec {
 | 
				
			|||||||
        std::slice::from_raw_parts(self.data.as_ptr() as *const UnsafeCell<T>, self.len)
 | 
					        std::slice::from_raw_parts(self.data.as_ptr() as *const UnsafeCell<T>, 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) {
 | 
					    pub fn clear(&mut self) {
 | 
				
			||||||
        let len = self.len;
 | 
					        let len = self.len;
 | 
				
			||||||
        // We set len to 0 _before_ dropping elements for unwind safety. This ensures we don't
 | 
					        // We set len to 0 _before_ dropping elements for unwind safety. This ensures we don't
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,24 @@
 | 
				
			|||||||
//! Storage layouts for ECS data.
 | 
					//! 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 blob_vec;
 | 
				
			||||||
mod resource;
 | 
					mod resource;
 | 
				
			||||||
@ -12,8 +32,12 @@ pub use table::*;
 | 
				
			|||||||
/// The raw data stores of a [World](crate::world::World)
 | 
					/// The raw data stores of a [World](crate::world::World)
 | 
				
			||||||
#[derive(Default)]
 | 
					#[derive(Default)]
 | 
				
			||||||
pub struct Storages {
 | 
					pub struct Storages {
 | 
				
			||||||
 | 
					    /// Backing storage for [`SparseSet`] components.
 | 
				
			||||||
    pub sparse_sets: SparseSets,
 | 
					    pub sparse_sets: SparseSets,
 | 
				
			||||||
 | 
					    /// Backing storage for [`Table`] components.
 | 
				
			||||||
    pub tables: Tables,
 | 
					    pub tables: Tables,
 | 
				
			||||||
 | 
					    /// Backing storage for resources.
 | 
				
			||||||
    pub resources: Resources<true>,
 | 
					    pub resources: Resources<true>,
 | 
				
			||||||
 | 
					    /// Backing storage for `!Send` resources.
 | 
				
			||||||
    pub non_send_resources: Resources<false>,
 | 
					    pub non_send_resources: Resources<false>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -42,6 +42,10 @@ impl<const SEND: bool> ResourceData<SEND> {
 | 
				
			|||||||
    /// The only row in the underlying column.
 | 
					    /// The only row in the underlying column.
 | 
				
			||||||
    const ROW: TableRow = TableRow::new(0);
 | 
					    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]
 | 
					    #[inline]
 | 
				
			||||||
    fn validate_access(&self) {
 | 
					    fn validate_access(&self) {
 | 
				
			||||||
        if SEND {
 | 
					        if SEND {
 | 
				
			||||||
@ -70,7 +74,7 @@ impl<const SEND: bool> ResourceData<SEND> {
 | 
				
			|||||||
        self.id
 | 
					        self.id
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Gets a read-only pointer to the underlying resource, if available.
 | 
					    /// Returns a reference to the resource, if it exists.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// # Panics
 | 
					    /// # Panics
 | 
				
			||||||
    /// If `SEND` is false, this will panic if a value is present and is not accessed from the
 | 
					    /// If `SEND` is false, this will panic if a value is present and is not accessed from the
 | 
				
			||||||
@ -83,12 +87,14 @@ impl<const SEND: bool> ResourceData<SEND> {
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// 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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get_ticks(&self) -> Option<ComponentTicks> {
 | 
					    pub fn get_ticks(&self) -> Option<ComponentTicks> {
 | 
				
			||||||
        self.column.get_ticks(Self::ROW)
 | 
					        self.column.get_ticks(Self::ROW)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns references to the resource and its change ticks, if it exists.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
    /// # Panics
 | 
					    /// # Panics
 | 
				
			||||||
    /// If `SEND` is false, this will panic if a value is present and is not accessed from the
 | 
					    /// If `SEND` is false, this will panic if a value is present and is not accessed from the
 | 
				
			||||||
    /// original thread it was inserted in.
 | 
					    /// original thread it was inserted in.
 | 
				
			||||||
@ -100,6 +106,11 @@ impl<const SEND: bool> ResourceData<SEND> {
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// 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(
 | 
					    pub(crate) fn get_mut(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        last_change_tick: u32,
 | 
					        last_change_tick: u32,
 | 
				
			||||||
 | 
				
			|||||||
@ -41,12 +41,16 @@ impl<I, V> SparseArray<I, V> {
 | 
				
			|||||||
macro_rules! impl_sparse_array {
 | 
					macro_rules! impl_sparse_array {
 | 
				
			||||||
    ($ty:ident) => {
 | 
					    ($ty:ident) => {
 | 
				
			||||||
        impl<I: SparseSetIndex, V> $ty<I, V> {
 | 
					        impl<I: SparseSetIndex, V> $ty<I, V> {
 | 
				
			||||||
 | 
					            /// Returns `true` if the collection contains a value for the specified `index`.
 | 
				
			||||||
            #[inline]
 | 
					            #[inline]
 | 
				
			||||||
            pub fn contains(&self, index: I) -> bool {
 | 
					            pub fn contains(&self, index: I) -> bool {
 | 
				
			||||||
                let index = index.sparse_set_index();
 | 
					                let index = index.sparse_set_index();
 | 
				
			||||||
                self.values.get(index).map(|v| v.is_some()).unwrap_or(false)
 | 
					                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]
 | 
					            #[inline]
 | 
				
			||||||
            pub fn get(&self, index: I) -> Option<&V> {
 | 
					            pub fn get(&self, index: I) -> Option<&V> {
 | 
				
			||||||
                let index = index.sparse_set_index();
 | 
					                let index = index.sparse_set_index();
 | 
				
			||||||
@ -60,6 +64,9 @@ impl_sparse_array!(SparseArray);
 | 
				
			|||||||
impl_sparse_array!(ImmutableSparseArray);
 | 
					impl_sparse_array!(ImmutableSparseArray);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<I: SparseSetIndex, V> SparseArray<I, V> {
 | 
					impl<I: SparseSetIndex, V> SparseArray<I, V> {
 | 
				
			||||||
 | 
					    /// Inserts `value` at `index` in the array.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// If `index` is out-of-bounds, this will enlarge the buffer to accommodate it.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn insert(&mut self, index: I, value: V) {
 | 
					    pub fn insert(&mut self, index: I, value: V) {
 | 
				
			||||||
        let index = index.sparse_set_index();
 | 
					        let index = index.sparse_set_index();
 | 
				
			||||||
@ -69,6 +76,9 @@ impl<I: SparseSetIndex, V> SparseArray<I, V> {
 | 
				
			|||||||
        self.values[index] = Some(value);
 | 
					        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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get_mut(&mut self, index: I) -> Option<&mut V> {
 | 
					    pub fn get_mut(&mut self, index: I) -> Option<&mut V> {
 | 
				
			||||||
        let index = index.sparse_set_index();
 | 
					        let index = index.sparse_set_index();
 | 
				
			||||||
@ -78,16 +88,21 @@ impl<I: SparseSetIndex, V> SparseArray<I, V> {
 | 
				
			|||||||
            .unwrap_or(None)
 | 
					            .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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn remove(&mut self, index: I) -> Option<V> {
 | 
					    pub fn remove(&mut self, index: I) -> Option<V> {
 | 
				
			||||||
        let index = index.sparse_set_index();
 | 
					        let index = index.sparse_set_index();
 | 
				
			||||||
        self.values.get_mut(index).and_then(|value| value.take())
 | 
					        self.values.get_mut(index).and_then(|value| value.take())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Removes all of the values stored within.
 | 
				
			||||||
    pub fn clear(&mut self) {
 | 
					    pub fn clear(&mut self) {
 | 
				
			||||||
        self.values.clear();
 | 
					        self.values.clear();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Converts the [`SparseArray`] into an immutable variant.
 | 
				
			||||||
    pub(crate) fn into_immutable(self) -> ImmutableSparseArray<I, V> {
 | 
					    pub(crate) fn into_immutable(self) -> ImmutableSparseArray<I, V> {
 | 
				
			||||||
        ImmutableSparseArray {
 | 
					        ImmutableSparseArray {
 | 
				
			||||||
            values: self.values.into_boxed_slice(),
 | 
					            values: self.values.into_boxed_slice(),
 | 
				
			||||||
@ -113,6 +128,8 @@ pub struct ComponentSparseSet {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl 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 {
 | 
					    pub(crate) fn new(component_info: &ComponentInfo, capacity: usize) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            dense: Column::with_capacity(component_info, capacity),
 | 
					            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) {
 | 
					    pub(crate) fn clear(&mut self) {
 | 
				
			||||||
        self.dense.clear();
 | 
					        self.dense.clear();
 | 
				
			||||||
        self.entities.clear();
 | 
					        self.entities.clear();
 | 
				
			||||||
        self.sparse.clear();
 | 
					        self.sparse.clear();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the number of component values in the sparse set.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn len(&self) -> usize {
 | 
					    pub fn len(&self) -> usize {
 | 
				
			||||||
        self.dense.len()
 | 
					        self.dense.len()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns `true` if the sparse set contains no component values.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn is_empty(&self) -> bool {
 | 
					    pub fn is_empty(&self) -> bool {
 | 
				
			||||||
        self.dense.len() == 0
 | 
					        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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn contains(&self, entity: Entity) -> bool {
 | 
					    pub fn contains(&self, entity: Entity) -> bool {
 | 
				
			||||||
        #[cfg(debug_assertions)]
 | 
					        #[cfg(debug_assertions)]
 | 
				
			||||||
@ -178,6 +199,9 @@ impl ComponentSparseSet {
 | 
				
			|||||||
        self.sparse.contains(entity.index())
 | 
					        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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get(&self, entity: Entity) -> Option<Ptr<'_>> {
 | 
					    pub fn get(&self, entity: Entity) -> Option<Ptr<'_>> {
 | 
				
			||||||
        self.sparse.get(entity.index()).map(|dense_index| {
 | 
					        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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get_with_ticks(&self, entity: Entity) -> Option<(Ptr<'_>, TickCells<'_>)> {
 | 
					    pub fn get_with_ticks(&self, entity: Entity) -> Option<(Ptr<'_>, TickCells<'_>)> {
 | 
				
			||||||
        let dense_index = TableRow::new(*self.sparse.get(entity.index())? as usize);
 | 
					        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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get_added_ticks(&self, entity: Entity) -> Option<&UnsafeCell<Tick>> {
 | 
					    pub fn get_added_ticks(&self, entity: Entity) -> Option<&UnsafeCell<Tick>> {
 | 
				
			||||||
        let dense_index = *self.sparse.get(entity.index())? as usize;
 | 
					        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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get_changed_ticks(&self, entity: Entity) -> Option<&UnsafeCell<Tick>> {
 | 
					    pub fn get_changed_ticks(&self, entity: Entity) -> Option<&UnsafeCell<Tick>> {
 | 
				
			||||||
        let dense_index = *self.sparse.get(entity.index())? as usize;
 | 
					        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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get_ticks(&self, entity: Entity) -> Option<ComponentTicks> {
 | 
					    pub fn get_ticks(&self, entity: Entity) -> Option<ComponentTicks> {
 | 
				
			||||||
        let dense_index = *self.sparse.get(entity.index())? as usize;
 | 
					        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 {
 | 
					    pub(crate) fn remove(&mut self, entity: Entity) -> bool {
 | 
				
			||||||
        if let Some(dense_index) = self.sparse.remove(entity.index()) {
 | 
					        if let Some(dense_index) = self.sparse.remove(entity.index()) {
 | 
				
			||||||
            let dense_index = dense_index as usize;
 | 
					            let dense_index = dense_index as usize;
 | 
				
			||||||
@ -320,16 +359,21 @@ pub(crate) struct ImmutableSparseSet<I, V: 'static> {
 | 
				
			|||||||
macro_rules! impl_sparse_set {
 | 
					macro_rules! impl_sparse_set {
 | 
				
			||||||
    ($ty:ident) => {
 | 
					    ($ty:ident) => {
 | 
				
			||||||
        impl<I: SparseSetIndex, V> $ty<I, V> {
 | 
					        impl<I: SparseSetIndex, V> $ty<I, V> {
 | 
				
			||||||
 | 
					            /// Returns the number of elements in the sparse set.
 | 
				
			||||||
            #[inline]
 | 
					            #[inline]
 | 
				
			||||||
            pub fn len(&self) -> usize {
 | 
					            pub fn len(&self) -> usize {
 | 
				
			||||||
                self.dense.len()
 | 
					                self.dense.len()
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /// Returns `true` if the sparse set contains a value for `index`.
 | 
				
			||||||
            #[inline]
 | 
					            #[inline]
 | 
				
			||||||
            pub fn contains(&self, index: I) -> bool {
 | 
					            pub fn contains(&self, index: I) -> bool {
 | 
				
			||||||
                self.sparse.contains(index)
 | 
					                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> {
 | 
					            pub fn get(&self, index: I) -> Option<&V> {
 | 
				
			||||||
                self.sparse.get(index).map(|dense_index| {
 | 
					                self.sparse.get(index).map(|dense_index| {
 | 
				
			||||||
                    // SAFETY: if the sparse index points to something in the dense vec, it exists
 | 
					                    // 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> {
 | 
					            pub fn get_mut(&mut self, index: I) -> Option<&mut V> {
 | 
				
			||||||
                let dense = &mut self.dense;
 | 
					                let dense = &mut self.dense;
 | 
				
			||||||
                self.sparse.get(index).map(move |dense_index| {
 | 
					                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<Item = I> + '_ {
 | 
					            pub fn indices(&self) -> impl Iterator<Item = I> + '_ {
 | 
				
			||||||
                self.indices.iter().cloned()
 | 
					                self.indices.iter().cloned()
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /// Returns an iterator visiting all values in arbitrary order.
 | 
				
			||||||
            pub fn values(&self) -> impl Iterator<Item = &V> {
 | 
					            pub fn values(&self) -> impl Iterator<Item = &V> {
 | 
				
			||||||
                self.dense.iter()
 | 
					                self.dense.iter()
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /// Returns an iterator visiting all values mutably in arbitrary order.
 | 
				
			||||||
            pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
 | 
					            pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
 | 
				
			||||||
                self.dense.iter_mut()
 | 
					                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<Item = (&I, &V)> {
 | 
					            pub fn iter(&self) -> impl Iterator<Item = (&I, &V)> {
 | 
				
			||||||
                self.indices.iter().zip(self.dense.iter())
 | 
					                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<Item = (&I, &mut V)> {
 | 
					            pub fn iter_mut(&mut self) -> impl Iterator<Item = (&I, &mut V)> {
 | 
				
			||||||
                self.indices.iter().zip(self.dense.iter_mut())
 | 
					                self.indices.iter().zip(self.dense.iter_mut())
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -378,6 +430,7 @@ impl<I: SparseSetIndex, V> Default for SparseSet<I, V> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<I, V> SparseSet<I, V> {
 | 
					impl<I, V> SparseSet<I, V> {
 | 
				
			||||||
 | 
					    /// Creates a new [`SparseSet`].
 | 
				
			||||||
    pub const fn new() -> Self {
 | 
					    pub const fn new() -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            dense: Vec::new(),
 | 
					            dense: Vec::new(),
 | 
				
			||||||
@ -388,6 +441,7 @@ impl<I, V> SparseSet<I, V> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<I: SparseSetIndex, V> SparseSet<I, V> {
 | 
					impl<I: SparseSetIndex, V> SparseSet<I, V> {
 | 
				
			||||||
 | 
					    /// Creates a new [`SparseSet`] with a specified initial capacity.
 | 
				
			||||||
    pub fn with_capacity(capacity: usize) -> Self {
 | 
					    pub fn with_capacity(capacity: usize) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            dense: Vec::with_capacity(capacity),
 | 
					            dense: Vec::with_capacity(capacity),
 | 
				
			||||||
@ -396,11 +450,15 @@ impl<I: SparseSetIndex, V> SparseSet<I, V> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the total number of elements the [`SparseSet`] can hold without needing to reallocate.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn capacity(&self) -> usize {
 | 
					    pub fn capacity(&self) -> usize {
 | 
				
			||||||
        self.dense.capacity()
 | 
					        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) {
 | 
					    pub fn insert(&mut self, index: I, value: V) {
 | 
				
			||||||
        if let Some(dense_index) = self.sparse.get(index.clone()).cloned() {
 | 
					        if let Some(dense_index) = self.sparse.get(index.clone()).cloned() {
 | 
				
			||||||
            // SAFETY: dense indices stored in self.sparse always exist
 | 
					            // SAFETY: dense indices stored in self.sparse always exist
 | 
				
			||||||
@ -414,6 +472,8 @@ impl<I: SparseSetIndex, V> SparseSet<I, V> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// 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 {
 | 
					    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() {
 | 
					        if let Some(dense_index) = self.sparse.get(index.clone()).cloned() {
 | 
				
			||||||
            // SAFETY: dense indices stored in self.sparse always exist
 | 
					            // SAFETY: dense indices stored in self.sparse always exist
 | 
				
			||||||
@ -429,11 +489,15 @@ impl<I: SparseSetIndex, V> SparseSet<I, V> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns `true` if the sparse set contains no elements.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn is_empty(&self) -> bool {
 | 
					    pub fn is_empty(&self) -> bool {
 | 
				
			||||||
        self.dense.len() == 0
 | 
					        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<V> {
 | 
					    pub fn remove(&mut self, index: I) -> Option<V> {
 | 
				
			||||||
        self.sparse.remove(index).map(|dense_index| {
 | 
					        self.sparse.remove(index).map(|dense_index| {
 | 
				
			||||||
            let is_last = dense_index == self.dense.len() - 1;
 | 
					            let is_last = dense_index == self.dense.len() - 1;
 | 
				
			||||||
@ -447,12 +511,14 @@ impl<I: SparseSetIndex, V> SparseSet<I, V> {
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Clears all of the elements from the sparse set.
 | 
				
			||||||
    pub fn clear(&mut self) {
 | 
					    pub fn clear(&mut self) {
 | 
				
			||||||
        self.dense.clear();
 | 
					        self.dense.clear();
 | 
				
			||||||
        self.indices.clear();
 | 
					        self.indices.clear();
 | 
				
			||||||
        self.sparse.clear();
 | 
					        self.sparse.clear();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Converts the sparse set into its immutable variant.
 | 
				
			||||||
    pub(crate) fn into_immutable(self) -> ImmutableSparseSet<I, V> {
 | 
					    pub(crate) fn into_immutable(self) -> ImmutableSparseSet<I, V> {
 | 
				
			||||||
        ImmutableSparseSet {
 | 
					        ImmutableSparseSet {
 | 
				
			||||||
            dense: self.dense.into_boxed_slice(),
 | 
					            dense: self.dense.into_boxed_slice(),
 | 
				
			||||||
 | 
				
			|||||||
@ -33,16 +33,24 @@ pub struct TableId(u32);
 | 
				
			|||||||
impl TableId {
 | 
					impl TableId {
 | 
				
			||||||
    pub(crate) const INVALID: TableId = TableId(u32::MAX);
 | 
					    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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn new(index: usize) -> Self {
 | 
					    pub fn new(index: usize) -> Self {
 | 
				
			||||||
        TableId(index as u32)
 | 
					        TableId(index as u32)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gets the underlying table index from the ID.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn index(self) -> usize {
 | 
					    pub fn index(self) -> usize {
 | 
				
			||||||
        self.0 as usize
 | 
					        self.0 as usize
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// The [`TableId`] of the [`Table`] without any components.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub const fn empty() -> TableId {
 | 
					    pub const fn empty() -> TableId {
 | 
				
			||||||
        TableId(0)
 | 
					        TableId(0)
 | 
				
			||||||
@ -71,7 +79,7 @@ impl TableId {
 | 
				
			|||||||
pub struct TableRow(u32);
 | 
					pub struct TableRow(u32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl TableRow {
 | 
					impl TableRow {
 | 
				
			||||||
    pub const INVALID: TableRow = TableRow(u32::MAX);
 | 
					    pub(crate) const INVALID: TableRow = TableRow(u32::MAX);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Creates a `TableRow`.
 | 
					    /// Creates a `TableRow`.
 | 
				
			||||||
    #[inline]
 | 
					    #[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<T>`.
 | 
				
			||||||
 | 
					/// 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)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub struct Column {
 | 
					pub struct Column {
 | 
				
			||||||
    data: BlobVec,
 | 
					    data: BlobVec,
 | 
				
			||||||
@ -94,6 +115,7 @@ pub struct Column {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Column {
 | 
					impl Column {
 | 
				
			||||||
 | 
					    /// Constructs a new [`Column`], configured with a component's layout and an initial `capacity`.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub(crate) fn with_capacity(component_info: &ComponentInfo, capacity: usize) -> Self {
 | 
					    pub(crate) fn with_capacity(component_info: &ComponentInfo, capacity: usize) -> Self {
 | 
				
			||||||
        Column {
 | 
					        Column {
 | 
				
			||||||
@ -104,6 +126,7 @@ impl Column {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Fetches the [`Layout`] for the underlying type.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn item_layout(&self) -> Layout {
 | 
					    pub fn item_layout(&self) -> Layout {
 | 
				
			||||||
        self.data.layout()
 | 
					        self.data.layout()
 | 
				
			||||||
@ -150,18 +173,29 @@ impl Column {
 | 
				
			|||||||
        self.data.replace_unchecked(row.index(), data);
 | 
					        self.data.replace_unchecked(row.index(), data);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gets the current number of elements stored in the column.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn len(&self) -> usize {
 | 
					    pub fn len(&self) -> usize {
 | 
				
			||||||
        self.data.len()
 | 
					        self.data.len()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Checks if the column is empty. Returns `true` if there are no elements, `false` otherwise.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn is_empty(&self) -> bool {
 | 
					    pub fn is_empty(&self) -> bool {
 | 
				
			||||||
        self.data.is_empty()
 | 
					        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
 | 
					    /// # Safety
 | 
				
			||||||
    /// index must be in-bounds
 | 
					    /// `row` must be within the range `[0, self.len())`.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// [`Drop`]: std::ops::Drop
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub(crate) unsafe fn swap_remove_unchecked(&mut self, row: TableRow) {
 | 
					    pub(crate) unsafe fn swap_remove_unchecked(&mut self, row: TableRow) {
 | 
				
			||||||
        self.data.swap_remove_and_drop_unchecked(row.index());
 | 
					        self.data.swap_remove_and_drop_unchecked(row.index());
 | 
				
			||||||
@ -169,6 +203,16 @@ impl Column {
 | 
				
			|||||||
        self.changed_ticks.swap_remove(row.index());
 | 
					        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]
 | 
					    #[inline]
 | 
				
			||||||
    #[must_use = "The returned pointer should be used to drop the removed component"]
 | 
					    #[must_use = "The returned pointer should be used to drop the removed component"]
 | 
				
			||||||
    pub(crate) fn swap_remove_and_forget(
 | 
					    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
 | 
					    /// # Safety
 | 
				
			||||||
    /// index must be in-bounds
 | 
					    /// `row` must be within the range `[0, self.len())`.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    #[must_use = "The returned pointer should be used to dropped the removed component"]
 | 
					    #[must_use = "The returned pointer should be used to dropped the removed component"]
 | 
				
			||||||
    pub(crate) unsafe fn swap_remove_and_forget_unchecked(
 | 
					    pub(crate) unsafe fn swap_remove_and_forget_unchecked(
 | 
				
			||||||
@ -225,8 +279,10 @@ impl Column {
 | 
				
			|||||||
            other.changed_ticks.swap_remove(src_row.index());
 | 
					            other.changed_ticks.swap_remove(src_row.index());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // # Safety
 | 
					    /// Pushes a new value onto the end of the [`Column`].
 | 
				
			||||||
    // - ptr must point to valid data of this column's component type
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Safety
 | 
				
			||||||
 | 
					    /// `ptr` must point to valid data of this column's component type
 | 
				
			||||||
    pub(crate) unsafe fn push(&mut self, ptr: OwningPtr<'_>, ticks: ComponentTicks) {
 | 
					    pub(crate) unsafe fn push(&mut self, ptr: OwningPtr<'_>, ticks: ComponentTicks) {
 | 
				
			||||||
        self.data.push(ptr);
 | 
					        self.data.push(ptr);
 | 
				
			||||||
        self.added_ticks.push(UnsafeCell::new(ticks.added));
 | 
					        self.added_ticks.push(UnsafeCell::new(ticks.added));
 | 
				
			||||||
@ -240,27 +296,57 @@ impl Column {
 | 
				
			|||||||
        self.changed_ticks.reserve_exact(additional);
 | 
					        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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get_data_ptr(&self) -> Ptr<'_> {
 | 
					    pub fn get_data_ptr(&self) -> Ptr<'_> {
 | 
				
			||||||
        self.data.get_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
 | 
					    /// # Safety
 | 
				
			||||||
    /// The type `T` must be the type of the items in this column.
 | 
					    /// The type `T` must be the type of the items in this column.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// [`UnsafeCell`]: std::cell::UnsafeCell
 | 
				
			||||||
    pub unsafe fn get_data_slice<T>(&self) -> &[UnsafeCell<T>] {
 | 
					    pub unsafe fn get_data_slice<T>(&self) -> &[UnsafeCell<T>] {
 | 
				
			||||||
        self.data.get_slice()
 | 
					        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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get_added_ticks_slice(&self) -> &[UnsafeCell<Tick>] {
 | 
					    pub fn get_added_ticks_slice(&self) -> &[UnsafeCell<Tick>] {
 | 
				
			||||||
        &self.added_ticks
 | 
					        &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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get_changed_ticks_slice(&self) -> &[UnsafeCell<Tick>] {
 | 
					    pub fn get_changed_ticks_slice(&self) -> &[UnsafeCell<Tick>] {
 | 
				
			||||||
        &self.changed_ticks
 | 
					        &self.changed_ticks
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Fetches a reference to the data and change detection ticks at `row`.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns `None` if `row` is out of bounds.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get(&self, row: TableRow) -> Option<(Ptr<'_>, TickCells<'_>)> {
 | 
					    pub fn get(&self, row: TableRow) -> Option<(Ptr<'_>, TickCells<'_>)> {
 | 
				
			||||||
        (row.index() < self.data.len())
 | 
					        (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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get_data(&self, row: TableRow) -> Option<Ptr<'_>> {
 | 
					    pub fn get_data(&self, row: TableRow) -> Option<Ptr<'_>> {
 | 
				
			||||||
        // SAFETY: The row is length checked before fetching the pointer. This is being
 | 
					        // 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()) })
 | 
					        (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
 | 
					    /// # Safety
 | 
				
			||||||
    /// - index must be in-bounds
 | 
					    /// - `row` must be within the range `[0, self.len())`.
 | 
				
			||||||
    /// - no other reference to the data of the same row can exist at the same time
 | 
					    /// - no other mutable reference to the data of the same row can exist at the same time
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub unsafe fn get_data_unchecked(&self, row: TableRow) -> Ptr<'_> {
 | 
					    pub unsafe fn get_data_unchecked(&self, row: TableRow) -> Ptr<'_> {
 | 
				
			||||||
        debug_assert!(row.index() < self.data.len());
 | 
					        debug_assert!(row.index() < self.data.len());
 | 
				
			||||||
        self.data.get_unchecked(row.index())
 | 
					        self.data.get_unchecked(row.index())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Fetches a mutable reference to the data at `row`.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns `None` if `row` is out of bounds.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get_data_mut(&mut self, row: TableRow) -> Option<PtrMut<'_>> {
 | 
					    pub fn get_data_mut(&mut self, row: TableRow) -> Option<PtrMut<'_>> {
 | 
				
			||||||
        // SAFETY: The row is length checked before fetching the pointer. This is being
 | 
					        // 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()) })
 | 
					        (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
 | 
					    /// # Safety
 | 
				
			||||||
    /// - index must be in-bounds
 | 
					    /// - index must be in-bounds
 | 
				
			||||||
    /// - no other reference to the data of the same row can exist at the same time
 | 
					    /// - 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())
 | 
					        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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get_added_ticks(&self, row: TableRow) -> Option<&UnsafeCell<Tick>> {
 | 
					    pub fn get_added_ticks(&self, row: TableRow) -> Option<&UnsafeCell<Tick>> {
 | 
				
			||||||
        self.added_ticks.get(row.index())
 | 
					        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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get_changed_ticks(&self, row: TableRow) -> Option<&UnsafeCell<Tick>> {
 | 
					    pub fn get_changed_ticks(&self, row: TableRow) -> Option<&UnsafeCell<Tick>> {
 | 
				
			||||||
        self.changed_ticks.get(row.index())
 | 
					        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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get_ticks(&self, row: TableRow) -> Option<ComponentTicks> {
 | 
					    pub fn get_ticks(&self, row: TableRow) -> Option<ComponentTicks> {
 | 
				
			||||||
        if row.index() < self.data.len() {
 | 
					        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
 | 
					    /// # Safety
 | 
				
			||||||
    /// index must be in-bounds
 | 
					    /// `row` must be within the range `[0, self.len())`.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub unsafe fn get_added_ticks_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
 | 
					    pub unsafe fn get_added_ticks_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
 | 
				
			||||||
        debug_assert!(row.index() < self.added_ticks.len());
 | 
					        debug_assert!(row.index() < self.added_ticks.len());
 | 
				
			||||||
        self.added_ticks.get_unchecked(row.index())
 | 
					        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
 | 
					    /// # Safety
 | 
				
			||||||
    /// index must be in-bounds
 | 
					    /// `row` must be within the range `[0, self.len())`.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub unsafe fn get_changed_ticks_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
 | 
					    pub unsafe fn get_changed_ticks_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
 | 
				
			||||||
        debug_assert!(row.index() < self.changed_ticks.len());
 | 
					        debug_assert!(row.index() < self.changed_ticks.len());
 | 
				
			||||||
        self.changed_ticks.get_unchecked(row.index())
 | 
					        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
 | 
					    /// # Safety
 | 
				
			||||||
    /// index must be in-bounds
 | 
					    /// `row` must be within the range `[0, self.len())`.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub unsafe fn get_ticks_unchecked(&self, row: TableRow) -> ComponentTicks {
 | 
					    pub unsafe fn get_ticks_unchecked(&self, row: TableRow) -> ComponentTicks {
 | 
				
			||||||
        debug_assert!(row.index() < self.added_ticks.len());
 | 
					        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) {
 | 
					    pub fn clear(&mut self) {
 | 
				
			||||||
        self.data.clear();
 | 
					        self.data.clear();
 | 
				
			||||||
        self.added_ticks.clear();
 | 
					        self.added_ticks.clear();
 | 
				
			||||||
@ -417,10 +548,10 @@ impl TableBuilder {
 | 
				
			|||||||
/// in a [`World`].
 | 
					/// in a [`World`].
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// Conceptually, a `Table` can be thought of as an `HashMap<ComponentId, Column>`, where
 | 
					/// Conceptually, a `Table` can be thought of as an `HashMap<ComponentId, Column>`, where
 | 
				
			||||||
/// each `Column` is a type-erased `Vec<T: Component>`. Each row corresponds to a single entity
 | 
					/// each [`Column`] is a type-erased `Vec<T: Component>`. 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
 | 
					/// (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
 | 
					/// 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
 | 
					/// [structure-of-arrays]: https://en.wikipedia.org/wiki/AoS_and_SoA#Structure_of_arrays
 | 
				
			||||||
/// [`Component`]: crate::component::Component
 | 
					/// [`Component`]: crate::component::Component
 | 
				
			||||||
@ -431,6 +562,7 @@ pub struct Table {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Table {
 | 
					impl Table {
 | 
				
			||||||
 | 
					    /// Fetches a read-only slice of the entities stored within the [`Table`].
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn entities(&self) -> &[Entity] {
 | 
					    pub fn entities(&self) -> &[Entity] {
 | 
				
			||||||
        &self.entities
 | 
					        &self.entities
 | 
				
			||||||
@ -457,7 +589,8 @@ impl Table {
 | 
				
			|||||||
    /// Moves the `row` column values to `new_table`, for the columns shared between both tables.
 | 
					    /// 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
 | 
					    /// 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
 | 
					    /// 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
 | 
					    /// # Safety
 | 
				
			||||||
    /// Row must be in-bounds
 | 
					    /// 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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get_column(&self, component_id: ComponentId) -> Option<&Column> {
 | 
					    pub fn get_column(&self, component_id: ComponentId) -> Option<&Column> {
 | 
				
			||||||
        self.columns.get(component_id)
 | 
					        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]
 | 
					    #[inline]
 | 
				
			||||||
    pub(crate) fn get_column_mut(&mut self, component_id: ComponentId) -> Option<&mut Column> {
 | 
					    pub(crate) fn get_column_mut(&mut self, component_id: ComponentId) -> Option<&mut Column> {
 | 
				
			||||||
        self.columns.get_mut(component_id)
 | 
					        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]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn has_column(&self, component_id: ComponentId) -> bool {
 | 
					    pub fn has_column(&self, component_id: ComponentId) -> bool {
 | 
				
			||||||
        self.columns.contains(component_id)
 | 
					        self.columns.contains(component_id)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Reserves `additional` elements worth of capacity within the table.
 | 
				
			||||||
    pub(crate) fn reserve(&mut self, additional: usize) {
 | 
					    pub(crate) fn reserve(&mut self, additional: usize) {
 | 
				
			||||||
        if self.entities.capacity() - self.entities.len() < additional {
 | 
					        if self.entities.capacity() - self.entities.len() < additional {
 | 
				
			||||||
            self.entities.reserve(additional);
 | 
					            self.entities.reserve(additional);
 | 
				
			||||||
@ -592,21 +743,28 @@ impl Table {
 | 
				
			|||||||
        TableRow::new(index)
 | 
					        TableRow::new(index)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gets the number of entities currently being stored in the table.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn entity_count(&self) -> usize {
 | 
					    pub fn entity_count(&self) -> usize {
 | 
				
			||||||
        self.entities.len()
 | 
					        self.entities.len()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gets the number of components being stored in the table.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn component_count(&self) -> usize {
 | 
					    pub fn component_count(&self) -> usize {
 | 
				
			||||||
        self.columns.len()
 | 
					        self.columns.len()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gets the maximum number of entities the table can currently store
 | 
				
			||||||
 | 
					    /// without reallocating the underlying memory.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn entity_capacity(&self) -> usize {
 | 
					    pub fn entity_capacity(&self) -> usize {
 | 
				
			||||||
        self.entities.capacity()
 | 
					        self.entities.capacity()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Checks if the [`Table`] is empty or not.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns `true` if the table contains no entities, `false` otherwise.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn is_empty(&self) -> bool {
 | 
					    pub fn is_empty(&self) -> bool {
 | 
				
			||||||
        self.entities.is_empty()
 | 
					        self.entities.is_empty()
 | 
				
			||||||
@ -618,10 +776,12 @@ impl Table {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Iterates over the [`Column`]s of the [`Table`].
 | 
				
			||||||
    pub fn iter(&self) -> impl Iterator<Item = &Column> {
 | 
					    pub fn iter(&self) -> impl Iterator<Item = &Column> {
 | 
				
			||||||
        self.columns.values()
 | 
					        self.columns.values()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Clears all of the stored components in the [`Table`].
 | 
				
			||||||
    pub(crate) fn clear(&mut self) {
 | 
					    pub(crate) fn clear(&mut self) {
 | 
				
			||||||
        self.entities.clear();
 | 
					        self.entities.clear();
 | 
				
			||||||
        for column in self.columns.values_mut() {
 | 
					        for column in self.columns.values_mut() {
 | 
				
			||||||
@ -666,11 +826,19 @@ impl Tables {
 | 
				
			|||||||
        self.tables.is_empty()
 | 
					        self.tables.is_empty()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Fetches a [`Table`] by its [`TableId`].
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns `None` if `id` is invalid.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub fn get(&self, id: TableId) -> Option<&Table> {
 | 
					    pub fn get(&self, id: TableId) -> Option<&Table> {
 | 
				
			||||||
        self.tables.get(id.index())
 | 
					        self.tables.get(id.index())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Fetches mutable references to two different [`Table`]s.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Panics
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Panics if `a` and `b` are equal.
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    pub(crate) fn get_2_mut(&mut self, a: TableId, b: TableId) -> (&mut Table, &mut Table) {
 | 
					    pub(crate) fn get_2_mut(&mut self, a: TableId, b: TableId) -> (&mut Table, &mut Table) {
 | 
				
			||||||
        if a.index() > b.index() {
 | 
					        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
 | 
					    /// # Safety
 | 
				
			||||||
    /// `component_ids` must contain components that exist in `components`
 | 
					    /// `component_ids` must contain components that exist in `components`
 | 
				
			||||||
    pub(crate) unsafe fn get_id_or_insert(
 | 
					    pub(crate) unsafe fn get_id_or_insert(
 | 
				
			||||||
@ -706,10 +877,12 @@ impl Tables {
 | 
				
			|||||||
        *value
 | 
					        *value
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Iterates through all of the tables stored within in [`TableId`] order.
 | 
				
			||||||
    pub fn iter(&self) -> std::slice::Iter<'_, Table> {
 | 
					    pub fn iter(&self) -> std::slice::Iter<'_, Table> {
 | 
				
			||||||
        self.tables.iter()
 | 
					        self.tables.iter()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Clears all data from all [`Table`]s stored within.
 | 
				
			||||||
    pub(crate) fn clear(&mut self) {
 | 
					    pub(crate) fn clear(&mut self) {
 | 
				
			||||||
        for table in &mut self.tables {
 | 
					        for table in &mut self.tables {
 | 
				
			||||||
            table.clear();
 | 
					            table.clear();
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user