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
bba3ec9b4a
commit
f1cc6b609f
@ -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,
|
||||
|
@ -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<unsafe fn(OwningPtr<'_>)>,
|
||||
@ -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::<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
|
||||
/// - 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::<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
|
||||
/// `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<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) {
|
||||
let len = self.len;
|
||||
// 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.
|
||||
//!
|
||||
//! 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<true>,
|
||||
/// Backing storage for `!Send` resources.
|
||||
pub non_send_resources: Resources<false>,
|
||||
}
|
||||
|
@ -42,6 +42,10 @@ impl<const SEND: bool> ResourceData<SEND> {
|
||||
/// 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<const SEND: bool> ResourceData<SEND> {
|
||||
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<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]
|
||||
pub fn get_ticks(&self) -> Option<ComponentTicks> {
|
||||
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<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(
|
||||
&mut self,
|
||||
last_change_tick: u32,
|
||||
|
@ -41,12 +41,16 @@ impl<I, V> SparseArray<I, V> {
|
||||
macro_rules! impl_sparse_array {
|
||||
($ty:ident) => {
|
||||
impl<I: SparseSetIndex, V> $ty<I, V> {
|
||||
/// 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<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]
|
||||
pub fn insert(&mut self, index: I, value: V) {
|
||||
let index = index.sparse_set_index();
|
||||
@ -69,6 +76,9 @@ impl<I: SparseSetIndex, V> SparseArray<I, V> {
|
||||
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<I: SparseSetIndex, V> SparseArray<I, V> {
|
||||
.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<V> {
|
||||
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<I, V> {
|
||||
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<Ptr<'_>> {
|
||||
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<Tick>> {
|
||||
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<Tick>> {
|
||||
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<ComponentTicks> {
|
||||
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<I, V: 'static> {
|
||||
macro_rules! impl_sparse_set {
|
||||
($ty:ident) => {
|
||||
impl<I: SparseSetIndex, V> $ty<I, V> {
|
||||
/// 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<Item = I> + '_ {
|
||||
self.indices.iter().cloned()
|
||||
}
|
||||
|
||||
/// Returns an iterator visiting all values in arbitrary order.
|
||||
pub fn values(&self) -> impl Iterator<Item = &V> {
|
||||
self.dense.iter()
|
||||
}
|
||||
|
||||
/// Returns an iterator visiting all values mutably in arbitrary order.
|
||||
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
|
||||
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)> {
|
||||
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)> {
|
||||
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> {
|
||||
/// Creates a new [`SparseSet`].
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
dense: Vec::new(),
|
||||
@ -388,6 +441,7 @@ impl<I, 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 {
|
||||
Self {
|
||||
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]
|
||||
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<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 {
|
||||
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<I: SparseSetIndex, V> SparseSet<I, V> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<V> {
|
||||
self.sparse.remove(index).map(|dense_index| {
|
||||
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) {
|
||||
self.dense.clear();
|
||||
self.indices.clear();
|
||||
self.sparse.clear();
|
||||
}
|
||||
|
||||
/// Converts the sparse set into its immutable variant.
|
||||
pub(crate) fn into_immutable(self) -> ImmutableSparseSet<I, V> {
|
||||
ImmutableSparseSet {
|
||||
dense: self.dense.into_boxed_slice(),
|
||||
|
@ -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<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)]
|
||||
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<T>(&self) -> &[UnsafeCell<T>] {
|
||||
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<Tick>] {
|
||||
&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<Tick>] {
|
||||
&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<Ptr<'_>> {
|
||||
// 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<PtrMut<'_>> {
|
||||
// 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<Tick>> {
|
||||
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<Tick>> {
|
||||
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<ComponentTicks> {
|
||||
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<Tick> {
|
||||
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<Tick> {
|
||||
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<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
|
||||
/// 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<Item = &Column> {
|
||||
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();
|
||||
|
Loading…
Reference in New Issue
Block a user