Path: blob/main/crates/bevy_ecs/src/storage/table/mod.rs
9395 views
use crate::{1change_detection::{CheckChangeTicks, ComponentTicks, MaybeLocation, Tick},2component::{ComponentId, ComponentInfo, Components},3entity::Entity,4query::DebugCheckedUnwrap,5storage::{AbortOnPanic, ImmutableSparseSet, SparseSet},6};7use alloc::{boxed::Box, vec, vec::Vec};8use bevy_platform::collections::HashMap;9use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref};10pub use column::*;11use core::{12cell::UnsafeCell,13num::NonZeroUsize,14ops::{Index, IndexMut},15panic::Location,16};17use nonmax::NonMaxU32;18mod column;1920/// An opaque unique ID for a [`Table`] within a [`World`].21///22/// Can be used with [`Tables::get`] to fetch the corresponding23/// table.24///25/// Each [`Archetype`] always points to a table via [`Archetype::table_id`].26/// Multiple archetypes can point to the same table so long as the components27/// stored in the table are identical, but do not share the same sparse set28/// components.29///30/// [`World`]: crate::world::World31/// [`Archetype`]: crate::archetype::Archetype32/// [`Archetype::table_id`]: crate::archetype::Archetype::table_id33#[derive(Debug, Clone, Copy, PartialEq, Eq)]34pub struct TableId(u32);3536impl TableId {37/// Creates a new [`TableId`].38///39/// `index` *must* be retrieved from calling [`TableId::as_u32`] on a `TableId` you got40/// from a table of a given [`World`] or the created ID may be invalid.41///42/// [`World`]: crate::world::World43#[inline]44pub const fn from_u32(index: u32) -> Self {45Self(index)46}4748/// Creates a new [`TableId`].49///50/// `index` *must* be retrieved from calling [`TableId::as_usize`] on a `TableId` you got51/// from a table of a given [`World`] or the created ID may be invalid.52///53/// [`World`]: crate::world::World54///55/// # Panics56///57/// Will panic if the provided value does not fit within a [`u32`].58#[inline]59pub const fn from_usize(index: usize) -> Self {60debug_assert!(index as u32 as usize == index);61Self(index as u32)62}6364/// Gets the underlying table index from the ID.65#[inline]66pub const fn as_u32(self) -> u32 {67self.068}6970/// Gets the underlying table index from the ID.71#[inline]72pub const fn as_usize(self) -> usize {73// usize is at least u32 in Bevy74self.0 as usize75}7677/// The [`TableId`] of the [`Table`] without any components.78#[inline]79pub const fn empty() -> Self {80Self(0)81}82}8384/// An opaque newtype for rows in [`Table`]s. Specifies a single row in a specific table.85///86/// Values of this type are retrievable from [`Archetype::entity_table_row`] and can be87/// used alongside [`Archetype::table_id`] to fetch the exact table and row where an88/// [`Entity`]'s components are stored.89///90/// Values of this type are only valid so long as entities have not moved around.91/// Adding and removing components from an entity, or despawning it will invalidate92/// potentially any table row in the table the entity was previously stored in. Users93/// should *always* fetch the appropriate row from the entity's [`Archetype`] before94/// fetching the entity's components.95///96/// [`Archetype`]: crate::archetype::Archetype97/// [`Archetype::entity_table_row`]: crate::archetype::Archetype::entity_table_row98/// [`Archetype::table_id`]: crate::archetype::Archetype::table_id99#[derive(Debug, Clone, Copy, PartialEq, Eq)]100#[repr(transparent)]101pub struct TableRow(NonMaxU32);102103impl TableRow {104/// Creates a [`TableRow`].105#[inline]106pub const fn new(index: NonMaxU32) -> Self {107Self(index)108}109110/// Gets the index of the row as a [`usize`].111#[inline]112pub const fn index(self) -> usize {113// usize is at least u32 in Bevy114self.0.get() as usize115}116117/// Gets the index of the row as a [`usize`].118#[inline]119pub const fn index_u32(self) -> u32 {120self.0.get()121}122}123124/// A builder type for constructing [`Table`]s.125///126/// - Use [`with_capacity`] to initialize the builder.127/// - Repeatedly call [`add_column`] to add columns for components.128/// - Finalize with [`build`] to get the constructed [`Table`].129///130/// [`with_capacity`]: Self::with_capacity131/// [`add_column`]: Self::add_column132/// [`build`]: Self::build133//134// # Safety135// The capacity of all columns is determined by that of the `entities` Vec. This means that136// it must be the correct capacity to allocate, reallocate, and deallocate all columns. This137// means the safety invariant must be enforced even in `TableBuilder`.138pub(crate) struct TableBuilder {139columns: SparseSet<ComponentId, Column>,140entities: Vec<Entity>,141}142143impl TableBuilder {144/// Start building a new [`Table`] with a specified `column_capacity` (How many components per column?) and a `capacity` (How many columns?)145pub fn with_capacity(capacity: usize, column_capacity: usize) -> Self {146Self {147columns: SparseSet::with_capacity(column_capacity),148entities: Vec::with_capacity(capacity),149}150}151152/// Add a new column to the [`Table`]. Specify the component which will be stored in the [`column`](Column) using its [`ComponentId`]153#[must_use]154pub fn add_column(mut self, component_info: &ComponentInfo) -> Self {155self.columns.insert(156component_info.id(),157Column::with_capacity(component_info, self.entities.capacity()),158);159self160}161162/// Build the [`Table`], after this operation the caller wouldn't be able to add more columns. The [`Table`] will be ready to use.163#[must_use]164pub fn build(self) -> Table {165Table {166columns: self.columns.into_immutable(),167entities: self.entities,168}169}170}171172/// A column-oriented [structure-of-arrays] based storage for [`Component`]s of entities173/// in a [`World`].174///175/// Conceptually, a `Table` can be thought of as a `HashMap<ComponentId, Column>`, where176/// each [`Column`] is a type-erased `Vec<T: Component>`. Each row corresponds to a single entity177/// (i.e. index 3 in Column A and index 3 in Column B point to different components on the same178/// entity). Fetching components from a table involves fetching the associated column for a179/// component type (via its [`ComponentId`]), then fetching the entity's row within that column.180///181/// [structure-of-arrays]: https://en.wikipedia.org/wiki/AoS_and_SoA#Structure_of_arrays182/// [`Component`]: crate::component::Component183/// [`World`]: crate::world::World184//185// # Safety186// The capacity of all columns is determined by that of the `entities` Vec. This means that187// it must be the correct capacity to allocate, reallocate, and deallocate all columns. This188// means the safety invariant must be enforced even in `TableBuilder`.189pub struct Table {190columns: ImmutableSparseSet<ComponentId, Column>,191entities: Vec<Entity>,192}193194impl Table {195/// Fetches a read-only slice of the entities stored within the [`Table`].196#[inline]197pub fn entities(&self) -> &[Entity] {198&self.entities199}200201/// Get the capacity of this table, in entities.202/// Note that if an allocation is in process, this might not match the actual capacity of the columns, but it should once the allocation ends.203#[inline]204pub fn capacity(&self) -> usize {205self.entities.capacity()206}207208/// Removes the entity at the given row and returns the entity swapped in to replace it (if an209/// entity was swapped in)210///211/// # Safety212/// `row` must be in-bounds (`row.as_usize()` < `self.len()`)213pub(crate) unsafe fn swap_remove_unchecked(&mut self, row: TableRow) -> Option<Entity> {214debug_assert!(row.index_u32() < self.entity_count());215let last_element_index = self.entity_count() - 1;216if row.index_u32() != last_element_index {217// Instead of checking this condition on every `swap_remove` call, we218// check it here and use `swap_remove_nonoverlapping`.219for col in self.columns.values_mut() {220// SAFETY:221// - `row` < `len`222// - `last_element_index` = `len` - 1223// - `row` != `last_element_index`224// - the `len` is kept within `self.entities`, it will update accordingly.225unsafe {226col.swap_remove_and_drop_unchecked_nonoverlapping(227last_element_index as usize,228row,229);230};231}232} else {233// If `row.as_usize()` == `last_element_index` than there's no point in removing the component234// at `row`, but we still need to drop it.235for col in self.columns.values_mut() {236col.drop_last_component(last_element_index as usize);237}238}239let is_last = row.index_u32() == last_element_index;240self.entities.swap_remove(row.index());241if is_last {242None243} else {244// SAFETY: This was swap removed and was not last, so it must be in bounds.245unsafe { Some(*self.entities.get_unchecked(row.index())) }246}247}248249/// Moves the `row` column values to `new_table`, for the columns shared between both tables.250/// Returns the index of the new row in `new_table` and the entity in this table swapped in251/// to replace it (if an entity was swapped in). missing columns will be "forgotten". It is252/// the caller's responsibility to drop them. Failure to do so may result in resources not253/// being released (i.e. files handles not being released, memory leaks, etc.)254///255/// # Panics256/// - Panics if the move forces a reallocation, and any of the new capacity overflows `isize::MAX` bytes.257/// - Panics if the move forces a reallocation, and any of the new the reallocations causes an out-of-memory error.258///259/// # Safety260/// - `row` must be in-bounds261pub(crate) unsafe fn move_to_and_forget_missing_unchecked(262&mut self,263row: TableRow,264new_table: &mut Table,265) -> TableMoveResult {266debug_assert!(row.index_u32() < self.entity_count());267let last_element_index = self.entity_count() - 1;268let is_last = row.index_u32() == last_element_index;269let new_row = new_table.allocate(self.entities.swap_remove(row.index()));270for (component_id, column) in self.columns.iter_mut() {271if let Some(new_column) = new_table.get_column_mut(*component_id) {272new_column.initialize_from_unchecked(273column,274last_element_index as usize,275row,276new_row,277);278} else {279// It's the caller's responsibility to drop these cases.280column.swap_remove_and_forget_unchecked(last_element_index as usize, row);281}282}283TableMoveResult {284new_row,285swapped_entity: if is_last {286None287} else {288// SAFETY: This was swap removed and was not last, so it must be in bounds.289unsafe { Some(*self.entities.get_unchecked(row.index())) }290},291}292}293294/// Moves the `row` column values to `new_table`, for the columns shared between both tables.295/// Returns the index of the new row in `new_table` and the entity in this table swapped in296/// to replace it (if an entity was swapped in).297///298/// # Panics299/// - Panics if the move forces a reallocation, and any of the new capacity overflows `isize::MAX` bytes.300/// - Panics if the move forces a reallocation, and any of the new the reallocations causes an out-of-memory error.301///302/// # Safety303/// row must be in-bounds304pub(crate) unsafe fn move_to_and_drop_missing_unchecked(305&mut self,306row: TableRow,307new_table: &mut Table,308) -> TableMoveResult {309debug_assert!(row.index_u32() < self.entity_count());310let last_element_index = self.entity_count() - 1;311let is_last = row.index_u32() == last_element_index;312let new_row = new_table.allocate(self.entities.swap_remove(row.index()));313for (component_id, column) in self.columns.iter_mut() {314if let Some(new_column) = new_table.get_column_mut(*component_id) {315new_column.initialize_from_unchecked(316column,317last_element_index as usize,318row,319new_row,320);321} else {322column.swap_remove_and_drop_unchecked(last_element_index as usize, row);323}324}325TableMoveResult {326new_row,327swapped_entity: if is_last {328None329} else {330// SAFETY: This was swap removed and was not last, so it must be in bounds.331unsafe { Some(*self.entities.get_unchecked(row.index())) }332},333}334}335336/// Moves the `row` column values to `new_table`, for the columns shared between both tables.337/// Returns the index of the new row in `new_table` and the entity in this table swapped in338/// to replace it (if an entity was swapped in).339///340/// # Panics341/// - Panics if the move forces a reallocation, and any of the new capacity overflows `isize::MAX` bytes.342/// - Panics if the move forces a reallocation, and any of the new the reallocations causes an out-of-memory error.343///344/// # Safety345/// - `row` must be in-bounds346/// - `new_table` must contain every component this table has347pub(crate) unsafe fn move_to_superset_unchecked(348&mut self,349row: TableRow,350new_table: &mut Table,351) -> TableMoveResult {352debug_assert!(row.index_u32() < self.entity_count());353let last_element_index = self.entity_count() - 1;354let is_last = row.index_u32() == last_element_index;355let new_row = new_table.allocate(self.entities.swap_remove(row.index()));356for (component_id, column) in self.columns.iter_mut() {357new_table358.get_column_mut(*component_id)359.debug_checked_unwrap()360.initialize_from_unchecked(column, last_element_index as usize, row, new_row);361}362TableMoveResult {363new_row,364swapped_entity: if is_last {365None366} else {367// SAFETY: This was swap removed and was not last, so it must be in bounds.368unsafe { Some(*self.entities.get_unchecked(row.index())) }369},370}371}372373/// Get the data of the column matching `component_id` as a slice.374///375/// # Safety376/// `row.as_usize()` < `self.len()`377/// - `T` must match the `component_id`378pub unsafe fn get_data_slice_for<T>(379&self,380component_id: ComponentId,381) -> Option<&[UnsafeCell<T>]> {382self.get_column(component_id)383.map(|col| col.get_data_slice(self.entity_count() as usize))384}385386/// Get the added ticks of the column matching `component_id` as a slice.387pub fn get_added_ticks_slice_for(388&self,389component_id: ComponentId,390) -> Option<&[UnsafeCell<Tick>]> {391self.get_column(component_id)392// SAFETY: `self.len()` is guaranteed to be the len of the ticks array393.map(|col| unsafe { col.get_added_ticks_slice(self.entity_count() as usize) })394}395396/// Get the changed ticks of the column matching `component_id` as a slice.397pub fn get_changed_ticks_slice_for(398&self,399component_id: ComponentId,400) -> Option<&[UnsafeCell<Tick>]> {401self.get_column(component_id)402// SAFETY: `self.len()` is guaranteed to be the len of the ticks array403.map(|col| unsafe { col.get_changed_ticks_slice(self.entity_count() as usize) })404}405406/// Fetches the calling locations that last changed the each component407pub fn get_changed_by_slice_for(408&self,409component_id: ComponentId,410) -> MaybeLocation<Option<&[UnsafeCell<&'static Location<'static>>]>> {411MaybeLocation::new_with_flattened(|| {412self.get_column(component_id)413// SAFETY: `self.len()` is guaranteed to be the len of the locations array414.map(|col| unsafe { col.get_changed_by_slice(self.entity_count() as usize) })415})416}417418/// Get the specific [`change tick`](Tick) of the component matching `component_id` in `row`.419pub fn get_changed_tick(420&self,421component_id: ComponentId,422row: TableRow,423) -> Option<&UnsafeCell<Tick>> {424(row.index_u32() < self.entity_count()).then_some(425// SAFETY: `row.as_usize()` < `len`426unsafe {427self.get_column(component_id)?428.changed_ticks429.get_unchecked(row.index())430},431)432}433434/// Get the specific [`added tick`](Tick) of the component matching `component_id` in `row`.435pub fn get_added_tick(436&self,437component_id: ComponentId,438row: TableRow,439) -> Option<&UnsafeCell<Tick>> {440(row.index_u32() < self.entity_count()).then_some(441// SAFETY: `row.as_usize()` < `len`442unsafe {443self.get_column(component_id)?444.added_ticks445.get_unchecked(row.index())446},447)448}449450/// Get the specific calling location that changed the component matching `component_id` in `row`451pub fn get_changed_by(452&self,453component_id: ComponentId,454row: TableRow,455) -> MaybeLocation<Option<&UnsafeCell<&'static Location<'static>>>> {456MaybeLocation::new_with_flattened(|| {457(row.index_u32() < self.entity_count()).then_some(458// SAFETY: `row.as_usize()` < `len`459unsafe {460self.get_column(component_id)?461.changed_by462.as_ref()463.map(|changed_by| changed_by.get_unchecked(row.index()))464},465)466})467}468469/// Get the [`ComponentTicks`] of the component matching `component_id` in `row`.470///471/// # Safety472/// - `row.as_usize()` < `self.len()`473pub unsafe fn get_ticks_unchecked(474&self,475component_id: ComponentId,476row: TableRow,477) -> Option<ComponentTicks> {478self.get_column(component_id).map(|col| ComponentTicks {479added: col.added_ticks.get_unchecked(row.index()).read(),480changed: col.changed_ticks.get_unchecked(row.index()).read(),481})482}483484/// Fetches a read-only reference to the [`Column`] for a given [`Component`] within the table.485///486/// Returns `None` if the corresponding component does not belong to the table.487///488/// [`Component`]: crate::component::Component489#[inline]490pub fn get_column(&self, component_id: ComponentId) -> Option<&Column> {491self.columns.get(component_id)492}493494/// Fetches a mutable reference to the [`Column`] for a given [`Component`] within the495/// table.496///497/// Returns `None` if the corresponding component does not belong to the table.498///499/// [`Component`]: crate::component::Component500#[inline]501pub(crate) fn get_column_mut(&mut self, component_id: ComponentId) -> Option<&mut Column> {502self.columns.get_mut(component_id)503}504505/// Checks if the table contains a [`Column`] for a given [`Component`].506///507/// Returns `true` if the column is present, `false` otherwise.508///509/// [`Component`]: crate::component::Component510#[inline]511pub fn has_column(&self, component_id: ComponentId) -> bool {512self.columns.contains(component_id)513}514515/// Reserves `additional` elements worth of capacity within the table.516pub(crate) fn reserve(&mut self, additional: usize) {517if (self.capacity() - self.entity_count() as usize) < additional {518let column_cap = self.capacity();519self.entities.reserve(additional);520521// use entities vector capacity as driving capacity for all related allocations522let new_capacity = self.entities.capacity();523524if column_cap == 0 {525// SAFETY: the current capacity is 0526unsafe { self.alloc_columns(NonZeroUsize::new_unchecked(new_capacity)) };527} else {528// SAFETY:529// - `column_cap` is indeed the columns' capacity530unsafe {531self.realloc_columns(532NonZeroUsize::new_unchecked(column_cap),533NonZeroUsize::new_unchecked(new_capacity),534);535};536}537}538}539540/// Allocate memory for the columns in the [`Table`]541///542/// # Panics543/// - Panics if any of the new capacity overflows `isize::MAX` bytes.544/// - Panics if any of the new allocations causes an out-of-memory error.545///546/// The current capacity of the columns should be 0, if it's not 0, then the previous data will be overwritten and leaked.547///548/// # Safety549/// The capacity of all columns is determined by that of the `entities` Vec. This means that550/// it must be the correct capacity to allocate, reallocate, and deallocate all columns. This551/// means the safety invariant must be enforced even in `TableBuilder`.552fn alloc_columns(&mut self, new_capacity: NonZeroUsize) {553// If any of these allocations trigger an unwind, the wrong capacity will be used while dropping this table - UB.554// To avoid this, we use `AbortOnPanic`. If the allocation triggered a panic, the `AbortOnPanic`'s Drop impl will be555// called, and abort the program.556let _guard = AbortOnPanic;557for col in self.columns.values_mut() {558col.alloc(new_capacity);559}560core::mem::forget(_guard); // The allocation was successful, so we don't drop the guard.561}562563/// Reallocate memory for the columns in the [`Table`]564///565/// # Panics566/// - Panics if any of the new capacities overflows `isize::MAX` bytes.567/// - Panics if any of the new reallocations causes an out-of-memory error.568///569/// # Safety570/// - `current_column_capacity` is indeed the capacity of the columns571///572/// The capacity of all columns is determined by that of the `entities` Vec. This means that573/// it must be the correct capacity to allocate, reallocate, and deallocate all columns. This574/// means the safety invariant must be enforced even in `TableBuilder`.575unsafe fn realloc_columns(576&mut self,577current_column_capacity: NonZeroUsize,578new_capacity: NonZeroUsize,579) {580// If any of these allocations trigger an unwind, the wrong capacity will be used while dropping this table - UB.581// To avoid this, we use `AbortOnPanic`. If the allocation triggered a panic, the `AbortOnPanic`'s Drop impl will be582// called, and abort the program.583let _guard = AbortOnPanic;584585// SAFETY:586// - There's no overflow587// - `current_capacity` is indeed the capacity - safety requirement588// - current capacity > 0589for col in self.columns.values_mut() {590col.realloc(current_column_capacity, new_capacity);591}592core::mem::forget(_guard); // The allocation was successful, so we don't drop the guard.593}594595/// Allocates space for a new entity596///597/// # Panics598/// - Panics if the allocation forces a reallocation and the new capacities overflows `isize::MAX` bytes.599/// - Panics if the allocation forces a reallocation and causes an out-of-memory error.600///601/// # Safety602///603/// The allocated row must be written to immediately with valid values in each column604pub(crate) unsafe fn allocate(&mut self, entity: Entity) -> TableRow {605self.reserve(1);606let len = self.entity_count();607// SAFETY: No entity index may be in more than one table row at once, so there are no duplicates,608// and there can not be an entity index of u32::MAX. Therefore, this can not be max either.609let row = unsafe { TableRow::new(NonMaxU32::new_unchecked(len)) };610let len = len as usize;611self.entities.push(entity);612for col in self.columns.values_mut() {613col.added_ticks614.initialize_unchecked(len, UnsafeCell::new(Tick::new(0)));615col.changed_ticks616.initialize_unchecked(len, UnsafeCell::new(Tick::new(0)));617col.changed_by618.as_mut()619.zip(MaybeLocation::caller())620.map(|(changed_by, caller)| {621changed_by.initialize_unchecked(len, UnsafeCell::new(caller));622});623}624625row626}627628/// Gets the number of entities currently being stored in the table.629#[inline]630pub fn entity_count(&self) -> u32 {631// No entity may have more than one table row, so there are no duplicates,632// and there may only ever be u32::MAX entities, so the length never exceeds u32's capacity.633self.entities.len() as u32634}635636/// Get the drop function for some component that is stored in this table.637#[inline]638pub fn get_drop_for(&self, component_id: ComponentId) -> Option<unsafe fn(OwningPtr<'_>)> {639self.get_column(component_id)?.data.drop640}641642/// Gets the number of components being stored in the table.643#[inline]644pub fn component_count(&self) -> usize {645self.columns.len()646}647648/// Gets the maximum number of entities the table can currently store649/// without reallocating the underlying memory.650#[inline]651pub fn entity_capacity(&self) -> usize {652self.entities.capacity()653}654655/// Checks if the [`Table`] is empty or not.656///657/// Returns `true` if the table contains no entities, `false` otherwise.658#[inline]659pub fn is_empty(&self) -> bool {660self.entities.is_empty()661}662663/// Call [`Tick::check_tick`] on all of the ticks in the [`Table`]664pub(crate) fn check_change_ticks(&mut self, check: CheckChangeTicks) {665let len = self.entity_count() as usize;666for col in self.columns.values_mut() {667// SAFETY: `len` is the actual length of the column668unsafe { col.check_change_ticks(len, check) };669}670}671672/// Iterates over the [`Column`]s of the [`Table`].673pub fn iter_columns(&self) -> impl Iterator<Item = &Column> {674self.columns.values()675}676677/// Clears all of the stored components in the [`Table`].678///679/// # Panics680/// - Panics if any of the components in any of the columns panics while being dropped.681pub(crate) fn clear(&mut self) {682let len = self.entity_count() as usize;683// We must clear the entities first, because in the drop function causes a panic, it will result in a double free of the columns.684self.entities.clear();685for column in self.columns.values_mut() {686// SAFETY: we defer `self.entities.clear()` until after clearing the columns,687// so `self.len()` should match the columns' len688unsafe { column.clear(len) };689}690}691692/// Moves component data out of the [`Table`].693///694/// This function leaves the underlying memory unchanged, but the component behind695/// returned pointer is semantically owned by the caller and will not be dropped in its original location.696/// Caller is responsible to drop component data behind returned pointer.697///698/// # Safety699/// - This table must hold the component matching `component_id`700/// - `row` must be in bounds701/// - The row's inconsistent state that happens after taking the component must be resolved—either initialize a new component or remove the row.702pub(crate) unsafe fn take_component(703&mut self,704component_id: ComponentId,705row: TableRow,706) -> OwningPtr<'_> {707self.get_column_mut(component_id)708.debug_checked_unwrap()709.data710.get_unchecked_mut(row.index())711.promote()712}713714/// Get the component at a given `row`, if the [`Table`] stores components with the given `component_id`715///716/// # Safety717/// `row.as_usize()` < `self.len()`718pub unsafe fn get_component(719&self,720component_id: ComponentId,721row: TableRow,722) -> Option<Ptr<'_>> {723self.get_column(component_id)724.map(|col| col.data.get_unchecked(row.index()))725}726}727728/// A collection of [`Table`] storages, indexed by [`TableId`]729///730/// Can be accessed via [`Storages`](crate::storage::Storages)731pub struct Tables {732tables: Vec<Table>,733table_ids: HashMap<Box<[ComponentId]>, TableId>,734}735736impl Default for Tables {737fn default() -> Self {738let empty_table = TableBuilder::with_capacity(0, 0).build();739Tables {740tables: vec![empty_table],741table_ids: HashMap::default(),742}743}744}745746pub(crate) struct TableMoveResult {747pub swapped_entity: Option<Entity>,748pub new_row: TableRow,749}750751impl Tables {752/// Returns the number of [`Table`]s this collection contains753#[inline]754pub fn len(&self) -> usize {755self.tables.len()756}757758/// Returns true if this collection contains no [`Table`]s759#[inline]760pub fn is_empty(&self) -> bool {761self.tables.is_empty()762}763764/// Fetches a [`Table`] by its [`TableId`].765///766/// Returns `None` if `id` is invalid.767#[inline]768pub fn get(&self, id: TableId) -> Option<&Table> {769self.tables.get(id.as_usize())770}771772/// Fetches mutable references to two different [`Table`]s.773///774/// # Panics775///776/// Panics if `a` and `b` are equal.777#[inline]778pub(crate) fn get_2_mut(&mut self, a: TableId, b: TableId) -> (&mut Table, &mut Table) {779if a.as_usize() > b.as_usize() {780let (b_slice, a_slice) = self.tables.split_at_mut(a.as_usize());781(&mut a_slice[0], &mut b_slice[b.as_usize()])782} else {783let (a_slice, b_slice) = self.tables.split_at_mut(b.as_usize());784(&mut a_slice[a.as_usize()], &mut b_slice[0])785}786}787788/// Attempts to fetch a table based on the provided components,789/// creating and returning a new [`Table`] if one did not already exist.790///791/// # Safety792/// `component_ids` must contain components that exist in `components`793pub(crate) unsafe fn get_id_or_insert(794&mut self,795component_ids: &[ComponentId],796components: &Components,797) -> TableId {798if component_ids.is_empty() {799return TableId::empty();800}801802let tables = &mut self.tables;803let (_key, value) = self804.table_ids805.raw_entry_mut()806.from_key(component_ids)807.or_insert_with(|| {808let mut table = TableBuilder::with_capacity(0, component_ids.len());809for component_id in component_ids {810table = table.add_column(components.get_info_unchecked(*component_id));811}812tables.push(table.build());813(component_ids.into(), TableId::from_usize(tables.len() - 1))814});815816*value817}818819/// Iterates through all of the tables stored within in [`TableId`] order.820pub fn iter(&self) -> core::slice::Iter<'_, Table> {821self.tables.iter()822}823824/// Clears all data from all [`Table`]s stored within.825pub(crate) fn clear(&mut self) {826for table in &mut self.tables {827table.clear();828}829}830831pub(crate) fn check_change_ticks(&mut self, check: CheckChangeTicks) {832for table in &mut self.tables {833table.check_change_ticks(check);834}835}836}837838impl Index<TableId> for Tables {839type Output = Table;840841#[inline]842fn index(&self, index: TableId) -> &Self::Output {843&self.tables[index.as_usize()]844}845}846847impl IndexMut<TableId> for Tables {848#[inline]849fn index_mut(&mut self, index: TableId) -> &mut Self::Output {850&mut self.tables[index.as_usize()]851}852}853854impl Drop for Table {855fn drop(&mut self) {856let len = self.entity_count() as usize;857let cap = self.capacity();858self.entities.clear();859for col in self.columns.values_mut() {860// SAFETY: `cap` and `len` are correct. `col` is never accessed again after this call.861unsafe {862col.drop(cap, len);863}864}865}866}867868#[cfg(test)]869mod tests {870use crate::{871change_detection::{MaybeLocation, Tick},872component::{Component, ComponentIds, Components, ComponentsRegistrator},873entity::{Entity, EntityIndex},874ptr::OwningPtr,875storage::{TableBuilder, TableId, TableRow, Tables},876};877use alloc::vec::Vec;878879#[derive(Component)]880struct W<T>(T);881882#[test]883fn only_one_empty_table() {884let components = Components::default();885let mut tables = Tables::default();886887let component_ids = &[];888// SAFETY: component_ids is empty, so we know it cannot reference invalid component IDs889let table_id = unsafe { tables.get_id_or_insert(component_ids, &components) };890891assert_eq!(table_id, TableId::empty());892}893894#[test]895fn table() {896let mut components = Components::default();897let mut componentids = ComponentIds::default();898// SAFETY: They are both new.899let mut registrator =900unsafe { ComponentsRegistrator::new(&mut components, &mut componentids) };901let component_id = registrator.register_component::<W<TableRow>>();902let columns = &[component_id];903let mut table = TableBuilder::with_capacity(0, columns.len())904.add_column(components.get_info(component_id).unwrap())905.build();906let entities = (0..200)907.map(|index| Entity::from_index(EntityIndex::from_raw_u32(index).unwrap()))908.collect::<Vec<_>>();909for entity in &entities {910// SAFETY: we allocate and immediately set data afterwards911unsafe {912let row = table.allocate(*entity);913let value: W<TableRow> = W(row);914OwningPtr::make(value, |value_ptr| {915table.get_column_mut(component_id).unwrap().initialize(916row,917value_ptr,918Tick::new(0),919MaybeLocation::caller(),920);921});922};923}924925assert_eq!(table.entity_capacity(), 256);926assert_eq!(table.entity_count(), 200);927}928}929930931