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:
James Liu 2023-03-08 23:22:48 -08:00 committed by François
parent bba3ec9b4a
commit f1cc6b609f
6 changed files with 335 additions and 20 deletions

View File

@ -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,

View File

@ -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

View File

@ -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>,
}

View File

@ -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,

View File

@ -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(),

View File

@ -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();