Path: blob/main/crates/bevy_ecs/src/storage/table/mod.rs
6609 views
use crate::{1change_detection::MaybeLocation,2component::{CheckChangeTicks, ComponentId, ComponentInfo, ComponentTicks, Components, Tick},3entity::Entity,4query::DebugCheckedUnwrap,5storage::{blob_vec::BlobVec, 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::{12alloc::Layout,13cell::UnsafeCell,14num::NonZeroUsize,15ops::{Index, IndexMut},16panic::Location,17};18use nonmax::NonMaxU32;19mod column;2021/// An opaque unique ID for a [`Table`] within a [`World`].22///23/// Can be used with [`Tables::get`] to fetch the corresponding24/// table.25///26/// Each [`Archetype`] always points to a table via [`Archetype::table_id`].27/// Multiple archetypes can point to the same table so long as the components28/// stored in the table are identical, but do not share the same sparse set29/// components.30///31/// [`World`]: crate::world::World32/// [`Archetype`]: crate::archetype::Archetype33/// [`Archetype::table_id`]: crate::archetype::Archetype::table_id34#[derive(Debug, Clone, Copy, PartialEq, Eq)]35pub struct TableId(u32);3637impl TableId {38/// Creates a new [`TableId`].39///40/// `index` *must* be retrieved from calling [`TableId::as_u32`] on a `TableId` you got41/// from a table of a given [`World`] or the created ID may be invalid.42///43/// [`World`]: crate::world::World44#[inline]45pub const fn from_u32(index: u32) -> Self {46Self(index)47}4849/// Creates a new [`TableId`].50///51/// `index` *must* be retrieved from calling [`TableId::as_usize`] on a `TableId` you got52/// from a table of a given [`World`] or the created ID may be invalid.53///54/// [`World`]: crate::world::World55///56/// # Panics57///58/// Will panic if the provided value does not fit within a [`u32`].59#[inline]60pub const fn from_usize(index: usize) -> Self {61debug_assert!(index as u32 as usize == index);62Self(index as u32)63}6465/// Gets the underlying table index from the ID.66#[inline]67pub const fn as_u32(self) -> u32 {68self.069}7071/// Gets the underlying table index from the ID.72#[inline]73pub const fn as_usize(self) -> usize {74// usize is at least u32 in Bevy75self.0 as usize76}7778/// The [`TableId`] of the [`Table`] without any components.79#[inline]80pub const fn empty() -> Self {81Self(0)82}83}8485/// An opaque newtype for rows in [`Table`]s. Specifies a single row in a specific table.86///87/// Values of this type are retrievable from [`Archetype::entity_table_row`] and can be88/// used alongside [`Archetype::table_id`] to fetch the exact table and row where an89/// [`Entity`]'s components are stored.90///91/// Values of this type are only valid so long as entities have not moved around.92/// Adding and removing components from an entity, or despawning it will invalidate93/// potentially any table row in the table the entity was previously stored in. Users94/// should *always* fetch the appropriate row from the entity's [`Archetype`] before95/// fetching the entity's components.96///97/// [`Archetype`]: crate::archetype::Archetype98/// [`Archetype::entity_table_row`]: crate::archetype::Archetype::entity_table_row99/// [`Archetype::table_id`]: crate::archetype::Archetype::table_id100#[derive(Debug, Clone, Copy, PartialEq, Eq)]101#[repr(transparent)]102pub struct TableRow(NonMaxU32);103104impl TableRow {105/// Creates a [`TableRow`].106#[inline]107pub const fn new(index: NonMaxU32) -> Self {108Self(index)109}110111/// Gets the index of the row as a [`usize`].112#[inline]113pub const fn index(self) -> usize {114// usize is at least u32 in Bevy115self.0.get() as usize116}117118/// Gets the index of the row as a [`usize`].119#[inline]120pub const fn index_u32(self) -> u32 {121self.0.get()122}123}124125/// A builder type for constructing [`Table`]s.126///127/// - Use [`with_capacity`] to initialize the builder.128/// - Repeatedly call [`add_column`] to add columns for components.129/// - Finalize with [`build`] to get the constructed [`Table`].130///131/// [`with_capacity`]: Self::with_capacity132/// [`add_column`]: Self::add_column133/// [`build`]: Self::build134//135// # Safety136// The capacity of all columns is determined by that of the `entities` Vec. This means that137// it must be the correct capacity to allocate, reallocate, and deallocate all columns. This138// means the safety invariant must be enforced even in `TableBuilder`.139pub(crate) struct TableBuilder {140columns: SparseSet<ComponentId, ThinColumn>,141entities: Vec<Entity>,142}143144impl TableBuilder {145/// Start building a new [`Table`] with a specified `column_capacity` (How many components per column?) and a `capacity` (How many columns?)146pub fn with_capacity(capacity: usize, column_capacity: usize) -> Self {147Self {148columns: SparseSet::with_capacity(column_capacity),149entities: Vec::with_capacity(capacity),150}151}152153/// Add a new column to the [`Table`]. Specify the component which will be stored in the [`column`](ThinColumn) using its [`ComponentId`]154#[must_use]155pub fn add_column(mut self, component_info: &ComponentInfo) -> Self {156self.columns.insert(157component_info.id(),158ThinColumn::with_capacity(component_info, self.entities.capacity()),159);160self161}162163/// Build the [`Table`], after this operation the caller wouldn't be able to add more columns. The [`Table`] will be ready to use.164#[must_use]165pub fn build(self) -> Table {166Table {167columns: self.columns.into_immutable(),168entities: self.entities,169}170}171}172173/// A column-oriented [structure-of-arrays] based storage for [`Component`]s of entities174/// in a [`World`].175///176/// Conceptually, a `Table` can be thought of as a `HashMap<ComponentId, Column>`, where177/// each [`ThinColumn`] is a type-erased `Vec<T: Component>`. Each row corresponds to a single entity178/// (i.e. index 3 in Column A and index 3 in Column B point to different components on the same179/// entity). Fetching components from a table involves fetching the associated column for a180/// component type (via its [`ComponentId`]), then fetching the entity's row within that column.181///182/// [structure-of-arrays]: https://en.wikipedia.org/wiki/AoS_and_SoA#Structure_of_arrays183/// [`Component`]: crate::component::Component184/// [`World`]: crate::world::World185//186// # Safety187// The capacity of all columns is determined by that of the `entities` Vec. This means that188// it must be the correct capacity to allocate, reallocate, and deallocate all columns. This189// means the safety invariant must be enforced even in `TableBuilder`.190pub struct Table {191columns: ImmutableSparseSet<ComponentId, ThinColumn>,192entities: Vec<Entity>,193}194195struct AbortOnPanic;196197impl Drop for AbortOnPanic {198fn drop(&mut self) {199// Panicking while unwinding will force an abort.200panic!("Aborting due to allocator error");201}202}203204impl Table {205/// Fetches a read-only slice of the entities stored within the [`Table`].206#[inline]207pub fn entities(&self) -> &[Entity] {208&self.entities209}210211/// Get the capacity of this table, in entities.212/// 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.213#[inline]214pub fn capacity(&self) -> usize {215self.entities.capacity()216}217218/// Removes the entity at the given row and returns the entity swapped in to replace it (if an219/// entity was swapped in)220///221/// # Safety222/// `row` must be in-bounds (`row.as_usize()` < `self.len()`)223pub(crate) unsafe fn swap_remove_unchecked(&mut self, row: TableRow) -> Option<Entity> {224debug_assert!(row.index_u32() < self.entity_count());225let last_element_index = self.entity_count() - 1;226if row.index_u32() != last_element_index {227// Instead of checking this condition on every `swap_remove` call, we228// check it here and use `swap_remove_nonoverlapping`.229for col in self.columns.values_mut() {230// SAFETY:231// - `row` < `len`232// - `last_element_index` = `len` - 1233// - `row` != `last_element_index`234// - the `len` is kept within `self.entities`, it will update accordingly.235unsafe {236col.swap_remove_and_drop_unchecked_nonoverlapping(237last_element_index as usize,238row,239);240};241}242} else {243// If `row.as_usize()` == `last_element_index` than there's no point in removing the component244// at `row`, but we still need to drop it.245for col in self.columns.values_mut() {246col.drop_last_component(last_element_index as usize);247}248}249let is_last = row.index_u32() == last_element_index;250self.entities.swap_remove(row.index());251if is_last {252None253} else {254// SAFETY: This was sawp removed and was not last, so it must be in bounds.255unsafe { Some(*self.entities.get_unchecked(row.index())) }256}257}258259/// Moves the `row` column values to `new_table`, for the columns shared between both tables.260/// Returns the index of the new row in `new_table` and the entity in this table swapped in261/// to replace it (if an entity was swapped in). missing columns will be "forgotten". It is262/// the caller's responsibility to drop them. Failure to do so may result in resources not263/// being released (i.e. files handles not being released, memory leaks, etc.)264///265/// # Safety266/// - `row` must be in-bounds267pub(crate) unsafe fn move_to_and_forget_missing_unchecked(268&mut self,269row: TableRow,270new_table: &mut Table,271) -> TableMoveResult {272debug_assert!(row.index_u32() < self.entity_count());273let last_element_index = self.entity_count() - 1;274let is_last = row.index_u32() == last_element_index;275let new_row = new_table.allocate(self.entities.swap_remove(row.index()));276for (component_id, column) in self.columns.iter_mut() {277if let Some(new_column) = new_table.get_column_mut(*component_id) {278new_column.initialize_from_unchecked(279column,280last_element_index as usize,281row,282new_row,283);284} else {285// It's the caller's responsibility to drop these cases.286column.swap_remove_and_forget_unchecked(last_element_index as usize, row);287}288}289TableMoveResult {290new_row,291swapped_entity: if is_last {292None293} else {294// SAFETY: This was sawp removed and was not last, so it must be in bounds.295unsafe { Some(*self.entities.get_unchecked(row.index())) }296},297}298}299300/// Moves the `row` column values to `new_table`, for the columns shared between both tables.301/// Returns the index of the new row in `new_table` and the entity in this table swapped in302/// to replace it (if an entity was swapped in).303///304/// # Safety305/// row must be in-bounds306pub(crate) unsafe fn move_to_and_drop_missing_unchecked(307&mut self,308row: TableRow,309new_table: &mut Table,310) -> TableMoveResult {311debug_assert!(row.index_u32() < self.entity_count());312let last_element_index = self.entity_count() - 1;313let is_last = row.index_u32() == last_element_index;314let new_row = new_table.allocate(self.entities.swap_remove(row.index()));315for (component_id, column) in self.columns.iter_mut() {316if let Some(new_column) = new_table.get_column_mut(*component_id) {317new_column.initialize_from_unchecked(318column,319last_element_index as usize,320row,321new_row,322);323} else {324column.swap_remove_and_drop_unchecked(last_element_index as usize, row);325}326}327TableMoveResult {328new_row,329swapped_entity: if is_last {330None331} else {332// SAFETY: This was sawp removed and was not last, so it must be in bounds.333unsafe { Some(*self.entities.get_unchecked(row.index())) }334},335}336}337338/// Moves the `row` column values to `new_table`, for the columns shared between both tables.339/// Returns the index of the new row in `new_table` and the entity in this table swapped in340/// to replace it (if an entity was swapped in).341///342/// # Safety343/// - `row` must be in-bounds344/// - `new_table` must contain every component this table has345pub(crate) unsafe fn move_to_superset_unchecked(346&mut self,347row: TableRow,348new_table: &mut Table,349) -> TableMoveResult {350debug_assert!(row.index_u32() < self.entity_count());351let last_element_index = self.entity_count() - 1;352let is_last = row.index_u32() == last_element_index;353let new_row = new_table.allocate(self.entities.swap_remove(row.index()));354for (component_id, column) in self.columns.iter_mut() {355new_table356.get_column_mut(*component_id)357.debug_checked_unwrap()358.initialize_from_unchecked(column, last_element_index as usize, row, new_row);359}360TableMoveResult {361new_row,362swapped_entity: if is_last {363None364} else {365// SAFETY: This was sawp removed and was not last, so it must be in bounds.366unsafe { Some(*self.entities.get_unchecked(row.index())) }367},368}369}370371/// Get the data of the column matching `component_id` as a slice.372///373/// # Safety374/// `row.as_usize()` < `self.len()`375/// - `T` must match the `component_id`376pub unsafe fn get_data_slice_for<T>(377&self,378component_id: ComponentId,379) -> Option<&[UnsafeCell<T>]> {380self.get_column(component_id)381.map(|col| col.get_data_slice(self.entity_count() as usize))382}383384/// Get the added ticks of the column matching `component_id` as a slice.385pub fn get_added_ticks_slice_for(386&self,387component_id: ComponentId,388) -> Option<&[UnsafeCell<Tick>]> {389self.get_column(component_id)390// SAFETY: `self.len()` is guaranteed to be the len of the ticks array391.map(|col| unsafe { col.get_added_ticks_slice(self.entity_count() as usize) })392}393394/// Get the changed ticks of the column matching `component_id` as a slice.395pub fn get_changed_ticks_slice_for(396&self,397component_id: ComponentId,398) -> Option<&[UnsafeCell<Tick>]> {399self.get_column(component_id)400// SAFETY: `self.len()` is guaranteed to be the len of the ticks array401.map(|col| unsafe { col.get_changed_ticks_slice(self.entity_count() as usize) })402}403404/// Fetches the calling locations that last changed the each component405pub fn get_changed_by_slice_for(406&self,407component_id: ComponentId,408) -> MaybeLocation<Option<&[UnsafeCell<&'static Location<'static>>]>> {409MaybeLocation::new_with_flattened(|| {410self.get_column(component_id)411// SAFETY: `self.len()` is guaranteed to be the len of the locations array412.map(|col| unsafe { col.get_changed_by_slice(self.entity_count() as usize) })413})414}415416/// Get the specific [`change tick`](Tick) of the component matching `component_id` in `row`.417pub fn get_changed_tick(418&self,419component_id: ComponentId,420row: TableRow,421) -> Option<&UnsafeCell<Tick>> {422(row.index_u32() < self.entity_count()).then_some(423// SAFETY: `row.as_usize()` < `len`424unsafe {425self.get_column(component_id)?426.changed_ticks427.get_unchecked(row.index())428},429)430}431432/// Get the specific [`added tick`](Tick) of the component matching `component_id` in `row`.433pub fn get_added_tick(434&self,435component_id: ComponentId,436row: TableRow,437) -> Option<&UnsafeCell<Tick>> {438(row.index_u32() < self.entity_count()).then_some(439// SAFETY: `row.as_usize()` < `len`440unsafe {441self.get_column(component_id)?442.added_ticks443.get_unchecked(row.index())444},445)446}447448/// Get the specific calling location that changed the component matching `component_id` in `row`449pub fn get_changed_by(450&self,451component_id: ComponentId,452row: TableRow,453) -> MaybeLocation<Option<&UnsafeCell<&'static Location<'static>>>> {454MaybeLocation::new_with_flattened(|| {455(row.index_u32() < self.entity_count()).then_some(456// SAFETY: `row.as_usize()` < `len`457unsafe {458self.get_column(component_id)?459.changed_by460.as_ref()461.map(|changed_by| changed_by.get_unchecked(row.index()))462},463)464})465}466467/// Get the [`ComponentTicks`] of the component matching `component_id` in `row`.468///469/// # Safety470/// - `row.as_usize()` < `self.len()`471pub unsafe fn get_ticks_unchecked(472&self,473component_id: ComponentId,474row: TableRow,475) -> Option<ComponentTicks> {476self.get_column(component_id).map(|col| ComponentTicks {477added: col.added_ticks.get_unchecked(row.index()).read(),478changed: col.changed_ticks.get_unchecked(row.index()).read(),479})480}481482/// Fetches a read-only reference to the [`ThinColumn`] for a given [`Component`] within the table.483///484/// Returns `None` if the corresponding component does not belong to the table.485///486/// [`Component`]: crate::component::Component487#[inline]488pub fn get_column(&self, component_id: ComponentId) -> Option<&ThinColumn> {489self.columns.get(component_id)490}491492/// Fetches a mutable reference to the [`ThinColumn`] for a given [`Component`] within the493/// table.494///495/// Returns `None` if the corresponding component does not belong to the table.496///497/// [`Component`]: crate::component::Component498#[inline]499pub(crate) fn get_column_mut(&mut self, component_id: ComponentId) -> Option<&mut ThinColumn> {500self.columns.get_mut(component_id)501}502503/// Checks if the table contains a [`ThinColumn`] for a given [`Component`].504///505/// Returns `true` if the column is present, `false` otherwise.506///507/// [`Component`]: crate::component::Component508#[inline]509pub fn has_column(&self, component_id: ComponentId) -> bool {510self.columns.contains(component_id)511}512513/// Reserves `additional` elements worth of capacity within the table.514pub(crate) fn reserve(&mut self, additional: usize) {515if (self.capacity() - self.entity_count() as usize) < additional {516let column_cap = self.capacity();517self.entities.reserve(additional);518519// use entities vector capacity as driving capacity for all related allocations520let new_capacity = self.entities.capacity();521522if column_cap == 0 {523// SAFETY: the current capacity is 0524unsafe { self.alloc_columns(NonZeroUsize::new_unchecked(new_capacity)) };525} else {526// SAFETY:527// - `column_cap` is indeed the columns' capacity528unsafe {529self.realloc_columns(530NonZeroUsize::new_unchecked(column_cap),531NonZeroUsize::new_unchecked(new_capacity),532);533};534}535}536}537538/// Allocate memory for the columns in the [`Table`]539///540/// The current capacity of the columns should be 0, if it's not 0, then the previous data will be overwritten and leaked.541///542/// # Safety543/// The capacity of all columns is determined by that of the `entities` Vec. This means that544/// it must be the correct capacity to allocate, reallocate, and deallocate all columns. This545/// means the safety invariant must be enforced even in `TableBuilder`.546fn alloc_columns(&mut self, new_capacity: NonZeroUsize) {547// If any of these allocations trigger an unwind, the wrong capacity will be used while dropping this table - UB.548// To avoid this, we use `AbortOnPanic`. If the allocation triggered a panic, the `AbortOnPanic`'s Drop impl will be549// called, and abort the program.550let _guard = AbortOnPanic;551for col in self.columns.values_mut() {552col.alloc(new_capacity);553}554core::mem::forget(_guard); // The allocation was successful, so we don't drop the guard.555}556557/// Reallocate memory for the columns in the [`Table`]558///559/// # Safety560/// - `current_column_capacity` is indeed the capacity of the columns561///562/// The capacity of all columns is determined by that of the `entities` Vec. This means that563/// it must be the correct capacity to allocate, reallocate, and deallocate all columnts. This564/// means the safety invariant must be enforced even in `TableBuilder`.565unsafe fn realloc_columns(566&mut self,567current_column_capacity: NonZeroUsize,568new_capacity: NonZeroUsize,569) {570// If any of these allocations trigger an unwind, the wrong capacity will be used while dropping this table - UB.571// To avoid this, we use `AbortOnPanic`. If the allocation triggered a panic, the `AbortOnPanic`'s Drop impl will be572// called, and abort the program.573let _guard = AbortOnPanic;574575// SAFETY:576// - There's no overflow577// - `current_capacity` is indeed the capacity - safety requirement578// - current capacity > 0579for col in self.columns.values_mut() {580col.realloc(current_column_capacity, new_capacity);581}582core::mem::forget(_guard); // The allocation was successful, so we don't drop the guard.583}584585/// Allocates space for a new entity586///587/// # Safety588///589/// The allocated row must be written to immediately with valid values in each column590pub(crate) unsafe fn allocate(&mut self, entity: Entity) -> TableRow {591self.reserve(1);592let len = self.entity_count();593// SAFETY: No entity row may be in more than one table row at once, so there are no duplicates,594// and there can not be an entity row of u32::MAX. Therefore, this can not be max either.595let row = unsafe { TableRow::new(NonMaxU32::new_unchecked(len)) };596let len = len as usize;597self.entities.push(entity);598for col in self.columns.values_mut() {599col.added_ticks600.initialize_unchecked(len, UnsafeCell::new(Tick::new(0)));601col.changed_ticks602.initialize_unchecked(len, UnsafeCell::new(Tick::new(0)));603col.changed_by604.as_mut()605.zip(MaybeLocation::caller())606.map(|(changed_by, caller)| {607changed_by.initialize_unchecked(len, UnsafeCell::new(caller));608});609}610611row612}613614/// Gets the number of entities currently being stored in the table.615#[inline]616pub fn entity_count(&self) -> u32 {617// No entity may have more than one table row, so there are no duplicates,618// and there may only ever be u32::MAX entities, so the length never exceeds u32's capacity.619self.entities.len() as u32620}621622/// Get the drop function for some component that is stored in this table.623#[inline]624pub fn get_drop_for(&self, component_id: ComponentId) -> Option<unsafe fn(OwningPtr<'_>)> {625self.get_column(component_id)?.data.drop626}627628/// Gets the number of components being stored in the table.629#[inline]630pub fn component_count(&self) -> usize {631self.columns.len()632}633634/// Gets the maximum number of entities the table can currently store635/// without reallocating the underlying memory.636#[inline]637pub fn entity_capacity(&self) -> usize {638self.entities.capacity()639}640641/// Checks if the [`Table`] is empty or not.642///643/// Returns `true` if the table contains no entities, `false` otherwise.644#[inline]645pub fn is_empty(&self) -> bool {646self.entities.is_empty()647}648649/// Call [`Tick::check_tick`] on all of the ticks in the [`Table`]650pub(crate) fn check_change_ticks(&mut self, check: CheckChangeTicks) {651let len = self.entity_count() as usize;652for col in self.columns.values_mut() {653// SAFETY: `len` is the actual length of the column654unsafe { col.check_change_ticks(len, check) };655}656}657658/// Iterates over the [`ThinColumn`]s of the [`Table`].659pub fn iter_columns(&self) -> impl Iterator<Item = &ThinColumn> {660self.columns.values()661}662663/// Clears all of the stored components in the [`Table`].664pub(crate) fn clear(&mut self) {665let len = self.entity_count() as usize;666// We must clear the entities first, because in the drop function causes a panic, it will result in a double free of the columns.667self.entities.clear();668for column in self.columns.values_mut() {669// SAFETY: we defer `self.entities.clear()` until after clearing the columns,670// so `self.len()` should match the columns' len671unsafe { column.clear(len) };672}673}674675/// Moves component data out of the [`Table`].676///677/// This function leaves the underlying memory unchanged, but the component behind678/// returned pointer is semantically owned by the caller and will not be dropped in its original location.679/// Caller is responsible to drop component data behind returned pointer.680///681/// # Safety682/// - This table must hold the component matching `component_id`683/// - `row` must be in bounds684/// - The row's inconsistent state that happens after taking the component must be resolved—either initialize a new component or remove the row.685pub(crate) unsafe fn take_component(686&mut self,687component_id: ComponentId,688row: TableRow,689) -> OwningPtr<'_> {690self.get_column_mut(component_id)691.debug_checked_unwrap()692.data693.get_unchecked_mut(row.index())694.promote()695}696697/// Get the component at a given `row`, if the [`Table`] stores components with the given `component_id`698///699/// # Safety700/// `row.as_usize()` < `self.len()`701pub unsafe fn get_component(702&self,703component_id: ComponentId,704row: TableRow,705) -> Option<Ptr<'_>> {706self.get_column(component_id)707.map(|col| col.data.get_unchecked(row.index()))708}709}710711/// A collection of [`Table`] storages, indexed by [`TableId`]712///713/// Can be accessed via [`Storages`](crate::storage::Storages)714pub struct Tables {715tables: Vec<Table>,716table_ids: HashMap<Box<[ComponentId]>, TableId>,717}718719impl Default for Tables {720fn default() -> Self {721let empty_table = TableBuilder::with_capacity(0, 0).build();722Tables {723tables: vec![empty_table],724table_ids: HashMap::default(),725}726}727}728729pub(crate) struct TableMoveResult {730pub swapped_entity: Option<Entity>,731pub new_row: TableRow,732}733734impl Tables {735/// Returns the number of [`Table`]s this collection contains736#[inline]737pub fn len(&self) -> usize {738self.tables.len()739}740741/// Returns true if this collection contains no [`Table`]s742#[inline]743pub fn is_empty(&self) -> bool {744self.tables.is_empty()745}746747/// Fetches a [`Table`] by its [`TableId`].748///749/// Returns `None` if `id` is invalid.750#[inline]751pub fn get(&self, id: TableId) -> Option<&Table> {752self.tables.get(id.as_usize())753}754755/// Fetches mutable references to two different [`Table`]s.756///757/// # Panics758///759/// Panics if `a` and `b` are equal.760#[inline]761pub(crate) fn get_2_mut(&mut self, a: TableId, b: TableId) -> (&mut Table, &mut Table) {762if a.as_usize() > b.as_usize() {763let (b_slice, a_slice) = self.tables.split_at_mut(a.as_usize());764(&mut a_slice[0], &mut b_slice[b.as_usize()])765} else {766let (a_slice, b_slice) = self.tables.split_at_mut(b.as_usize());767(&mut a_slice[a.as_usize()], &mut b_slice[0])768}769}770771/// Attempts to fetch a table based on the provided components,772/// creating and returning a new [`Table`] if one did not already exist.773///774/// # Safety775/// `component_ids` must contain components that exist in `components`776pub(crate) unsafe fn get_id_or_insert(777&mut self,778component_ids: &[ComponentId],779components: &Components,780) -> TableId {781if component_ids.is_empty() {782return TableId::empty();783}784785let tables = &mut self.tables;786let (_key, value) = self787.table_ids788.raw_entry_mut()789.from_key(component_ids)790.or_insert_with(|| {791let mut table = TableBuilder::with_capacity(0, component_ids.len());792for component_id in component_ids {793table = table.add_column(components.get_info_unchecked(*component_id));794}795tables.push(table.build());796(component_ids.into(), TableId::from_usize(tables.len() - 1))797});798799*value800}801802/// Iterates through all of the tables stored within in [`TableId`] order.803pub fn iter(&self) -> core::slice::Iter<'_, Table> {804self.tables.iter()805}806807/// Clears all data from all [`Table`]s stored within.808pub(crate) fn clear(&mut self) {809for table in &mut self.tables {810table.clear();811}812}813814pub(crate) fn check_change_ticks(&mut self, check: CheckChangeTicks) {815for table in &mut self.tables {816table.check_change_ticks(check);817}818}819}820821impl Index<TableId> for Tables {822type Output = Table;823824#[inline]825fn index(&self, index: TableId) -> &Self::Output {826&self.tables[index.as_usize()]827}828}829830impl IndexMut<TableId> for Tables {831#[inline]832fn index_mut(&mut self, index: TableId) -> &mut Self::Output {833&mut self.tables[index.as_usize()]834}835}836837impl Drop for Table {838fn drop(&mut self) {839let len = self.entity_count() as usize;840let cap = self.capacity();841self.entities.clear();842for col in self.columns.values_mut() {843// SAFETY: `cap` and `len` are correct844unsafe {845col.drop(cap, len);846}847}848}849}850851#[cfg(test)]852mod tests {853use crate::{854change_detection::MaybeLocation,855component::{Component, ComponentIds, Components, ComponentsRegistrator, Tick},856entity::{Entity, EntityRow},857ptr::OwningPtr,858storage::{TableBuilder, TableId, TableRow, Tables},859};860use alloc::vec::Vec;861use nonmax::NonMaxU32;862863#[derive(Component)]864struct W<T>(T);865866#[test]867fn only_one_empty_table() {868let components = Components::default();869let mut tables = Tables::default();870871let component_ids = &[];872// SAFETY: component_ids is empty, so we know it cannot reference invalid component IDs873let table_id = unsafe { tables.get_id_or_insert(component_ids, &components) };874875assert_eq!(table_id, TableId::empty());876}877878#[test]879fn table() {880let mut components = Components::default();881let mut componentids = ComponentIds::default();882// SAFETY: They are both new.883let mut registrator =884unsafe { ComponentsRegistrator::new(&mut components, &mut componentids) };885let component_id = registrator.register_component::<W<TableRow>>();886let columns = &[component_id];887let mut table = TableBuilder::with_capacity(0, columns.len())888.add_column(components.get_info(component_id).unwrap())889.build();890let entities = (0..200)891.map(|index| Entity::from_raw(EntityRow::new(NonMaxU32::new(index).unwrap())))892.collect::<Vec<_>>();893for entity in &entities {894// SAFETY: we allocate and immediately set data afterwards895unsafe {896let row = table.allocate(*entity);897let value: W<TableRow> = W(row);898OwningPtr::make(value, |value_ptr| {899table.get_column_mut(component_id).unwrap().initialize(900row,901value_ptr,902Tick::new(0),903MaybeLocation::caller(),904);905});906};907}908909assert_eq!(table.entity_capacity(), 256);910assert_eq!(table.entity_count(), 200);911}912}913914915