bevy_ecs/storage/table/
mod.rs

1use crate::{
2    change_detection::MaybeLocation,
3    component::{ComponentId, ComponentInfo, ComponentTicks, Components, Tick},
4    entity::Entity,
5    query::DebugCheckedUnwrap,
6    storage::{blob_vec::BlobVec, ImmutableSparseSet, SparseSet},
7};
8use alloc::{boxed::Box, vec, vec::Vec};
9use bevy_platform::collections::HashMap;
10use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref};
11pub use column::*;
12use core::{
13    alloc::Layout,
14    cell::UnsafeCell,
15    num::NonZeroUsize,
16    ops::{Index, IndexMut},
17    panic::Location,
18};
19mod column;
20
21/// An opaque unique ID for a [`Table`] within a [`World`].
22///
23/// Can be used with [`Tables::get`] to fetch the corresponding
24/// 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 components
28/// stored in the table are identical, but do not share the same sparse set
29/// components.
30///
31/// [`World`]: crate::world::World
32/// [`Archetype`]: crate::archetype::Archetype
33/// [`Archetype::table_id`]: crate::archetype::Archetype::table_id
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct TableId(u32);
36
37impl TableId {
38    pub(crate) const INVALID: TableId = TableId(u32::MAX);
39
40    /// Creates a new [`TableId`].
41    ///
42    /// `index` *must* be retrieved from calling [`TableId::as_u32`] on a `TableId` you got
43    /// from a table of a given [`World`] or the created ID may be invalid.
44    ///
45    /// [`World`]: crate::world::World
46    #[inline]
47    pub const fn from_u32(index: u32) -> Self {
48        Self(index)
49    }
50
51    /// Creates a new [`TableId`].
52    ///
53    /// `index` *must* be retrieved from calling [`TableId::as_usize`] on a `TableId` you got
54    /// from a table of a given [`World`] or the created ID may be invalid.
55    ///
56    /// [`World`]: crate::world::World
57    ///
58    /// # Panics
59    ///
60    /// Will panic if the provided value does not fit within a [`u32`].
61    #[inline]
62    pub const fn from_usize(index: usize) -> Self {
63        debug_assert!(index as u32 as usize == index);
64        Self(index as u32)
65    }
66
67    /// Gets the underlying table index from the ID.
68    #[inline]
69    pub const fn as_u32(self) -> u32 {
70        self.0
71    }
72
73    /// Gets the underlying table index from the ID.
74    #[inline]
75    pub const fn as_usize(self) -> usize {
76        // usize is at least u32 in Bevy
77        self.0 as usize
78    }
79
80    /// The [`TableId`] of the [`Table`] without any components.
81    #[inline]
82    pub const fn empty() -> Self {
83        Self(0)
84    }
85}
86
87/// An opaque newtype for rows in [`Table`]s. Specifies a single row in a specific table.
88///
89/// Values of this type are retrievable from [`Archetype::entity_table_row`] and can be
90/// used alongside [`Archetype::table_id`] to fetch the exact table and row where an
91/// [`Entity`]'s components are stored.
92///
93/// Values of this type are only valid so long as entities have not moved around.
94/// Adding and removing components from an entity, or despawning it will invalidate
95/// potentially any table row in the table the entity was previously stored in. Users
96/// should *always* fetch the appropriate row from the entity's [`Archetype`] before
97/// fetching the entity's components.
98///
99/// [`Archetype`]: crate::archetype::Archetype
100/// [`Archetype::entity_table_row`]: crate::archetype::Archetype::entity_table_row
101/// [`Archetype::table_id`]: crate::archetype::Archetype::table_id
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub struct TableRow(u32);
104
105impl TableRow {
106    pub(crate) const INVALID: TableRow = TableRow(u32::MAX);
107
108    /// Creates a `TableRow`.
109    #[inline]
110    pub const fn from_u32(index: u32) -> Self {
111        Self(index)
112    }
113
114    /// Creates a `TableRow` from a [`usize`] index.
115    ///
116    /// # Panics
117    ///
118    /// Will panic in debug mode if the provided value does not fit within a [`u32`].
119    #[inline]
120    pub const fn from_usize(index: usize) -> Self {
121        debug_assert!(index as u32 as usize == index);
122        Self(index as u32)
123    }
124
125    /// Gets the index of the row as a [`usize`].
126    #[inline]
127    pub const fn as_usize(self) -> usize {
128        // usize is at least u32 in Bevy
129        self.0 as usize
130    }
131
132    /// Gets the index of the row as a [`usize`].
133    #[inline]
134    pub const fn as_u32(self) -> u32 {
135        self.0
136    }
137}
138
139/// A builder type for constructing [`Table`]s.
140///
141///  - Use [`with_capacity`] to initialize the builder.
142///  - Repeatedly call [`add_column`] to add columns for components.
143///  - Finalize with [`build`] to get the constructed [`Table`].
144///
145/// [`with_capacity`]: Self::with_capacity
146/// [`add_column`]: Self::add_column
147/// [`build`]: Self::build
148pub(crate) struct TableBuilder {
149    columns: SparseSet<ComponentId, ThinColumn>,
150    capacity: usize,
151}
152
153impl TableBuilder {
154    /// Start building a new [`Table`] with a specified `column_capacity` (How many components per column?) and a `capacity` (How many columns?)
155    pub fn with_capacity(capacity: usize, column_capacity: usize) -> Self {
156        Self {
157            columns: SparseSet::with_capacity(column_capacity),
158            capacity,
159        }
160    }
161
162    /// Add a new column to the [`Table`]. Specify the component which will be stored in the [`column`](ThinColumn) using its [`ComponentId`]
163    #[must_use]
164    pub fn add_column(mut self, component_info: &ComponentInfo) -> Self {
165        self.columns.insert(
166            component_info.id(),
167            ThinColumn::with_capacity(component_info, self.capacity),
168        );
169        self
170    }
171
172    /// Build the [`Table`], after this operation the caller wouldn't be able to add more columns. The [`Table`] will be ready to use.
173    #[must_use]
174    pub fn build(self) -> Table {
175        Table {
176            columns: self.columns.into_immutable(),
177            entities: Vec::with_capacity(self.capacity),
178        }
179    }
180}
181
182/// A column-oriented [structure-of-arrays] based storage for [`Component`]s of entities
183/// in a [`World`].
184///
185/// Conceptually, a `Table` can be thought of as a `HashMap<ComponentId, Column>`, where
186/// each [`ThinColumn`] is a type-erased `Vec<T: Component>`. Each row corresponds to a single entity
187/// (i.e. index 3 in Column A and index 3 in Column B point to different components on the same
188/// entity). Fetching components from a table involves fetching the associated column for a
189/// component type (via its [`ComponentId`]), then fetching the entity's row within that column.
190///
191/// [structure-of-arrays]: https://en.wikipedia.org/wiki/AoS_and_SoA#Structure_of_arrays
192/// [`Component`]: crate::component::Component
193/// [`World`]: crate::world::World
194pub struct Table {
195    columns: ImmutableSparseSet<ComponentId, ThinColumn>,
196    entities: Vec<Entity>,
197}
198
199struct AbortOnPanic;
200
201impl Drop for AbortOnPanic {
202    fn drop(&mut self) {
203        // Panicking while unwinding will force an abort.
204        panic!("Aborting due to allocator error");
205    }
206}
207
208impl Table {
209    /// Fetches a read-only slice of the entities stored within the [`Table`].
210    #[inline]
211    pub fn entities(&self) -> &[Entity] {
212        &self.entities
213    }
214
215    /// Get the capacity of this table, in entities.
216    /// 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.
217    #[inline]
218    pub fn capacity(&self) -> usize {
219        self.entities.capacity()
220    }
221
222    /// Removes the entity at the given row and returns the entity swapped in to replace it (if an
223    /// entity was swapped in)
224    ///
225    /// # Safety
226    /// `row` must be in-bounds (`row.as_usize()` < `self.len()`)
227    pub(crate) unsafe fn swap_remove_unchecked(&mut self, row: TableRow) -> Option<Entity> {
228        debug_assert!(row.as_usize() < self.entity_count());
229        let last_element_index = self.entity_count() - 1;
230        if row.as_usize() != last_element_index {
231            // Instead of checking this condition on every `swap_remove` call, we
232            // check it here and use `swap_remove_nonoverlapping`.
233            for col in self.columns.values_mut() {
234                // SAFETY:
235                // - `row` < `len`
236                // - `last_element_index` = `len` - 1
237                // - `row` != `last_element_index`
238                // - the `len` is kept within `self.entities`, it will update accordingly.
239                unsafe {
240                    col.swap_remove_and_drop_unchecked_nonoverlapping(last_element_index, row);
241                };
242            }
243        } else {
244            // If `row.as_usize()` == `last_element_index` than there's no point in removing the component
245            // at `row`, but we still need to drop it.
246            for col in self.columns.values_mut() {
247                col.drop_last_component(last_element_index);
248            }
249        }
250        let is_last = row.as_usize() == last_element_index;
251        self.entities.swap_remove(row.as_usize());
252        if is_last {
253            None
254        } else {
255            Some(self.entities[row.as_usize()])
256        }
257    }
258
259    /// 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 in
261    /// to replace it (if an entity was swapped in). missing columns will be "forgotten". It is
262    /// the caller's responsibility to drop them.  Failure to do so may result in resources not
263    /// being released (i.e. files handles not being released, memory leaks, etc.)
264    ///
265    /// # Safety
266    /// - `row` must be in-bounds
267    pub(crate) unsafe fn move_to_and_forget_missing_unchecked(
268        &mut self,
269        row: TableRow,
270        new_table: &mut Table,
271    ) -> TableMoveResult {
272        debug_assert!(row.as_usize() < self.entity_count());
273        let last_element_index = self.entity_count() - 1;
274        let is_last = row.as_usize() == last_element_index;
275        let new_row = new_table.allocate(self.entities.swap_remove(row.as_usize()));
276        for (component_id, column) in self.columns.iter_mut() {
277            if let Some(new_column) = new_table.get_column_mut(*component_id) {
278                new_column.initialize_from_unchecked(column, last_element_index, row, new_row);
279            } else {
280                // It's the caller's responsibility to drop these cases.
281                column.swap_remove_and_forget_unchecked(last_element_index, row);
282            }
283        }
284        TableMoveResult {
285            new_row,
286            swapped_entity: if is_last {
287                None
288            } else {
289                Some(self.entities[row.as_usize()])
290            },
291        }
292    }
293
294    /// 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 in
296    /// to replace it (if an entity was swapped in).
297    ///
298    /// # Safety
299    /// row must be in-bounds
300    pub(crate) unsafe fn move_to_and_drop_missing_unchecked(
301        &mut self,
302        row: TableRow,
303        new_table: &mut Table,
304    ) -> TableMoveResult {
305        debug_assert!(row.as_usize() < self.entity_count());
306        let last_element_index = self.entity_count() - 1;
307        let is_last = row.as_usize() == last_element_index;
308        let new_row = new_table.allocate(self.entities.swap_remove(row.as_usize()));
309        for (component_id, column) in self.columns.iter_mut() {
310            if let Some(new_column) = new_table.get_column_mut(*component_id) {
311                new_column.initialize_from_unchecked(column, last_element_index, row, new_row);
312            } else {
313                column.swap_remove_and_drop_unchecked(last_element_index, row);
314            }
315        }
316        TableMoveResult {
317            new_row,
318            swapped_entity: if is_last {
319                None
320            } else {
321                Some(self.entities[row.as_usize()])
322            },
323        }
324    }
325
326    /// Moves the `row` column values to `new_table`, for the columns shared between both tables.
327    /// Returns the index of the new row in `new_table` and the entity in this table swapped in
328    /// to replace it (if an entity was swapped in).
329    ///
330    /// # Safety
331    /// - `row` must be in-bounds
332    /// - `new_table` must contain every component this table has
333    pub(crate) unsafe fn move_to_superset_unchecked(
334        &mut self,
335        row: TableRow,
336        new_table: &mut Table,
337    ) -> TableMoveResult {
338        debug_assert!(row.as_usize() < self.entity_count());
339        let last_element_index = self.entity_count() - 1;
340        let is_last = row.as_usize() == last_element_index;
341        let new_row = new_table.allocate(self.entities.swap_remove(row.as_usize()));
342        for (component_id, column) in self.columns.iter_mut() {
343            new_table
344                .get_column_mut(*component_id)
345                .debug_checked_unwrap()
346                .initialize_from_unchecked(column, last_element_index, row, new_row);
347        }
348        TableMoveResult {
349            new_row,
350            swapped_entity: if is_last {
351                None
352            } else {
353                Some(self.entities[row.as_usize()])
354            },
355        }
356    }
357
358    /// Get the data of the column matching `component_id` as a slice.
359    ///
360    /// # Safety
361    /// `row.as_usize()` < `self.len()`
362    /// - `T` must match the `component_id`
363    pub unsafe fn get_data_slice_for<T>(
364        &self,
365        component_id: ComponentId,
366    ) -> Option<&[UnsafeCell<T>]> {
367        self.get_column(component_id)
368            .map(|col| col.get_data_slice(self.entity_count()))
369    }
370
371    /// Get the added ticks of the column matching `component_id` as a slice.
372    pub fn get_added_ticks_slice_for(
373        &self,
374        component_id: ComponentId,
375    ) -> Option<&[UnsafeCell<Tick>]> {
376        self.get_column(component_id)
377            // SAFETY: `self.len()` is guaranteed to be the len of the ticks array
378            .map(|col| unsafe { col.get_added_ticks_slice(self.entity_count()) })
379    }
380
381    /// Get the changed ticks of the column matching `component_id` as a slice.
382    pub fn get_changed_ticks_slice_for(
383        &self,
384        component_id: ComponentId,
385    ) -> Option<&[UnsafeCell<Tick>]> {
386        self.get_column(component_id)
387            // SAFETY: `self.len()` is guaranteed to be the len of the ticks array
388            .map(|col| unsafe { col.get_changed_ticks_slice(self.entity_count()) })
389    }
390
391    /// Fetches the calling locations that last changed the each component
392    pub fn get_changed_by_slice_for(
393        &self,
394        component_id: ComponentId,
395    ) -> MaybeLocation<Option<&[UnsafeCell<&'static Location<'static>>]>> {
396        MaybeLocation::new_with_flattened(|| {
397            self.get_column(component_id)
398                // SAFETY: `self.len()` is guaranteed to be the len of the locations array
399                .map(|col| unsafe { col.get_changed_by_slice(self.entity_count()) })
400        })
401    }
402
403    /// Get the specific [`change tick`](Tick) of the component matching `component_id` in `row`.
404    pub fn get_changed_tick(
405        &self,
406        component_id: ComponentId,
407        row: TableRow,
408    ) -> Option<&UnsafeCell<Tick>> {
409        (row.as_usize() < self.entity_count()).then_some(
410            // SAFETY: `row.as_usize()` < `len`
411            unsafe {
412                self.get_column(component_id)?
413                    .changed_ticks
414                    .get_unchecked(row.as_usize())
415            },
416        )
417    }
418
419    /// Get the specific [`added tick`](Tick) of the component matching `component_id` in `row`.
420    pub fn get_added_tick(
421        &self,
422        component_id: ComponentId,
423        row: TableRow,
424    ) -> Option<&UnsafeCell<Tick>> {
425        (row.as_usize() < self.entity_count()).then_some(
426            // SAFETY: `row.as_usize()` < `len`
427            unsafe {
428                self.get_column(component_id)?
429                    .added_ticks
430                    .get_unchecked(row.as_usize())
431            },
432        )
433    }
434
435    /// Get the specific calling location that changed the component matching `component_id` in `row`
436    pub fn get_changed_by(
437        &self,
438        component_id: ComponentId,
439        row: TableRow,
440    ) -> MaybeLocation<Option<&UnsafeCell<&'static Location<'static>>>> {
441        MaybeLocation::new_with_flattened(|| {
442            (row.as_usize() < self.entity_count()).then_some(
443                // SAFETY: `row.as_usize()` < `len`
444                unsafe {
445                    self.get_column(component_id)?
446                        .changed_by
447                        .as_ref()
448                        .map(|changed_by| changed_by.get_unchecked(row.as_usize()))
449                },
450            )
451        })
452    }
453
454    /// Get the [`ComponentTicks`] of the component matching `component_id` in `row`.
455    ///
456    /// # Safety
457    /// - `row.as_usize()` < `self.len()`
458    pub unsafe fn get_ticks_unchecked(
459        &self,
460        component_id: ComponentId,
461        row: TableRow,
462    ) -> Option<ComponentTicks> {
463        self.get_column(component_id).map(|col| ComponentTicks {
464            added: col.added_ticks.get_unchecked(row.as_usize()).read(),
465            changed: col.changed_ticks.get_unchecked(row.as_usize()).read(),
466        })
467    }
468
469    /// Fetches a read-only reference to the [`ThinColumn`] for a given [`Component`] within the table.
470    ///
471    /// Returns `None` if the corresponding component does not belong to the table.
472    ///
473    /// [`Component`]: crate::component::Component
474    #[inline]
475    pub fn get_column(&self, component_id: ComponentId) -> Option<&ThinColumn> {
476        self.columns.get(component_id)
477    }
478
479    /// Fetches a mutable reference to the [`ThinColumn`] for a given [`Component`] within the
480    /// table.
481    ///
482    /// Returns `None` if the corresponding component does not belong to the table.
483    ///
484    /// [`Component`]: crate::component::Component
485    #[inline]
486    pub(crate) fn get_column_mut(&mut self, component_id: ComponentId) -> Option<&mut ThinColumn> {
487        self.columns.get_mut(component_id)
488    }
489
490    /// Checks if the table contains a [`ThinColumn`] for a given [`Component`].
491    ///
492    /// Returns `true` if the column is present, `false` otherwise.
493    ///
494    /// [`Component`]: crate::component::Component
495    #[inline]
496    pub fn has_column(&self, component_id: ComponentId) -> bool {
497        self.columns.contains(component_id)
498    }
499
500    /// Reserves `additional` elements worth of capacity within the table.
501    pub(crate) fn reserve(&mut self, additional: usize) {
502        if self.capacity() - self.entity_count() < additional {
503            let column_cap = self.capacity();
504            self.entities.reserve(additional);
505
506            // use entities vector capacity as driving capacity for all related allocations
507            let new_capacity = self.entities.capacity();
508
509            if column_cap == 0 {
510                // SAFETY: the current capacity is 0
511                unsafe { self.alloc_columns(NonZeroUsize::new_unchecked(new_capacity)) };
512            } else {
513                // SAFETY:
514                // - `column_cap` is indeed the columns' capacity
515                unsafe {
516                    self.realloc_columns(
517                        NonZeroUsize::new_unchecked(column_cap),
518                        NonZeroUsize::new_unchecked(new_capacity),
519                    );
520                };
521            }
522        }
523    }
524
525    /// Allocate memory for the columns in the [`Table`]
526    ///
527    /// The current capacity of the columns should be 0, if it's not 0, then the previous data will be overwritten and leaked.
528    fn alloc_columns(&mut self, new_capacity: NonZeroUsize) {
529        // If any of these allocations trigger an unwind, the wrong capacity will be used while dropping this table - UB.
530        // To avoid this, we use `AbortOnPanic`. If the allocation triggered a panic, the `AbortOnPanic`'s Drop impl will be
531        // called, and abort the program.
532        let _guard = AbortOnPanic;
533        for col in self.columns.values_mut() {
534            col.alloc(new_capacity);
535        }
536        core::mem::forget(_guard); // The allocation was successful, so we don't drop the guard.
537    }
538
539    /// Reallocate memory for the columns in the [`Table`]
540    ///
541    /// # Safety
542    /// - `current_column_capacity` is indeed the capacity of the columns
543    unsafe fn realloc_columns(
544        &mut self,
545        current_column_capacity: NonZeroUsize,
546        new_capacity: NonZeroUsize,
547    ) {
548        // If any of these allocations trigger an unwind, the wrong capacity will be used while dropping this table - UB.
549        // To avoid this, we use `AbortOnPanic`. If the allocation triggered a panic, the `AbortOnPanic`'s Drop impl will be
550        // called, and abort the program.
551        let _guard = AbortOnPanic;
552
553        // SAFETY:
554        // - There's no overflow
555        // - `current_capacity` is indeed the capacity - safety requirement
556        // - current capacity > 0
557        for col in self.columns.values_mut() {
558            col.realloc(current_column_capacity, new_capacity);
559        }
560        core::mem::forget(_guard); // The allocation was successful, so we don't drop the guard.
561    }
562
563    /// Allocates space for a new entity
564    ///
565    /// # Safety
566    /// the allocated row must be written to immediately with valid values in each column
567    pub(crate) unsafe fn allocate(&mut self, entity: Entity) -> TableRow {
568        self.reserve(1);
569        let len = self.entity_count();
570        self.entities.push(entity);
571        for col in self.columns.values_mut() {
572            col.added_ticks
573                .initialize_unchecked(len, UnsafeCell::new(Tick::new(0)));
574            col.changed_ticks
575                .initialize_unchecked(len, UnsafeCell::new(Tick::new(0)));
576            col.changed_by
577                .as_mut()
578                .zip(MaybeLocation::caller())
579                .map(|(changed_by, caller)| {
580                    changed_by.initialize_unchecked(len, UnsafeCell::new(caller));
581                });
582        }
583        TableRow::from_usize(len)
584    }
585
586    /// Gets the number of entities currently being stored in the table.
587    #[inline]
588    pub fn entity_count(&self) -> usize {
589        self.entities.len()
590    }
591
592    /// Get the drop function for some component that is stored in this table.
593    #[inline]
594    pub fn get_drop_for(&self, component_id: ComponentId) -> Option<unsafe fn(OwningPtr<'_>)> {
595        self.get_column(component_id)?.data.drop
596    }
597
598    /// Gets the number of components being stored in the table.
599    #[inline]
600    pub fn component_count(&self) -> usize {
601        self.columns.len()
602    }
603
604    /// Gets the maximum number of entities the table can currently store
605    /// without reallocating the underlying memory.
606    #[inline]
607    pub fn entity_capacity(&self) -> usize {
608        self.entities.capacity()
609    }
610
611    /// Checks if the [`Table`] is empty or not.
612    ///
613    /// Returns `true` if the table contains no entities, `false` otherwise.
614    #[inline]
615    pub fn is_empty(&self) -> bool {
616        self.entities.is_empty()
617    }
618
619    /// Call [`Tick::check_tick`] on all of the ticks in the [`Table`]
620    pub(crate) fn check_change_ticks(&mut self, change_tick: Tick) {
621        let len = self.entity_count();
622        for col in self.columns.values_mut() {
623            // SAFETY: `len` is the actual length of the column
624            unsafe { col.check_change_ticks(len, change_tick) };
625        }
626    }
627
628    /// Iterates over the [`ThinColumn`]s of the [`Table`].
629    pub fn iter_columns(&self) -> impl Iterator<Item = &ThinColumn> {
630        self.columns.values()
631    }
632
633    /// Clears all of the stored components in the [`Table`].
634    pub(crate) fn clear(&mut self) {
635        let len = self.entity_count();
636        // We must clear the entities first, because in the drop function causes a panic, it will result in a double free of the columns.
637        self.entities.clear();
638        for column in self.columns.values_mut() {
639            // SAFETY: we defer `self.entities.clear()` until after clearing the columns,
640            // so `self.len()` should match the columns' len
641            unsafe { column.clear(len) };
642        }
643    }
644
645    /// Moves component data out of the [`Table`].
646    ///
647    /// This function leaves the underlying memory unchanged, but the component behind
648    /// returned pointer is semantically owned by the caller and will not be dropped in its original location.
649    /// Caller is responsible to drop component data behind returned pointer.
650    ///
651    /// # Safety
652    /// - This table must hold the component matching `component_id`
653    /// - `row` must be in bounds
654    /// - The row's inconsistent state that happens after taking the component must be resolved—either initialize a new component or remove the row.
655    pub(crate) unsafe fn take_component(
656        &mut self,
657        component_id: ComponentId,
658        row: TableRow,
659    ) -> OwningPtr<'_> {
660        self.get_column_mut(component_id)
661            .debug_checked_unwrap()
662            .data
663            .get_unchecked_mut(row.as_usize())
664            .promote()
665    }
666
667    /// Get the component at a given `row`, if the [`Table`] stores components with the given `component_id`
668    ///
669    /// # Safety
670    /// `row.as_usize()` < `self.len()`
671    pub unsafe fn get_component(
672        &self,
673        component_id: ComponentId,
674        row: TableRow,
675    ) -> Option<Ptr<'_>> {
676        self.get_column(component_id)
677            .map(|col| col.data.get_unchecked(row.as_usize()))
678    }
679}
680
681/// A collection of [`Table`] storages, indexed by [`TableId`]
682///
683/// Can be accessed via [`Storages`](crate::storage::Storages)
684pub struct Tables {
685    tables: Vec<Table>,
686    table_ids: HashMap<Box<[ComponentId]>, TableId>,
687}
688
689impl Default for Tables {
690    fn default() -> Self {
691        let empty_table = TableBuilder::with_capacity(0, 0).build();
692        Tables {
693            tables: vec![empty_table],
694            table_ids: HashMap::default(),
695        }
696    }
697}
698
699pub(crate) struct TableMoveResult {
700    pub swapped_entity: Option<Entity>,
701    pub new_row: TableRow,
702}
703
704impl Tables {
705    /// Returns the number of [`Table`]s this collection contains
706    #[inline]
707    pub fn len(&self) -> usize {
708        self.tables.len()
709    }
710
711    /// Returns true if this collection contains no [`Table`]s
712    #[inline]
713    pub fn is_empty(&self) -> bool {
714        self.tables.is_empty()
715    }
716
717    /// Fetches a [`Table`] by its [`TableId`].
718    ///
719    /// Returns `None` if `id` is invalid.
720    #[inline]
721    pub fn get(&self, id: TableId) -> Option<&Table> {
722        self.tables.get(id.as_usize())
723    }
724
725    /// Fetches mutable references to two different [`Table`]s.
726    ///
727    /// # Panics
728    ///
729    /// Panics if `a` and `b` are equal.
730    #[inline]
731    pub(crate) fn get_2_mut(&mut self, a: TableId, b: TableId) -> (&mut Table, &mut Table) {
732        if a.as_usize() > b.as_usize() {
733            let (b_slice, a_slice) = self.tables.split_at_mut(a.as_usize());
734            (&mut a_slice[0], &mut b_slice[b.as_usize()])
735        } else {
736            let (a_slice, b_slice) = self.tables.split_at_mut(b.as_usize());
737            (&mut a_slice[a.as_usize()], &mut b_slice[0])
738        }
739    }
740
741    /// Attempts to fetch a table based on the provided components,
742    /// creating and returning a new [`Table`] if one did not already exist.
743    ///
744    /// # Safety
745    /// `component_ids` must contain components that exist in `components`
746    pub(crate) unsafe fn get_id_or_insert(
747        &mut self,
748        component_ids: &[ComponentId],
749        components: &Components,
750    ) -> TableId {
751        if component_ids.is_empty() {
752            return TableId::empty();
753        }
754
755        let tables = &mut self.tables;
756        let (_key, value) = self
757            .table_ids
758            .raw_entry_mut()
759            .from_key(component_ids)
760            .or_insert_with(|| {
761                let mut table = TableBuilder::with_capacity(0, component_ids.len());
762                for component_id in component_ids {
763                    table = table.add_column(components.get_info_unchecked(*component_id));
764                }
765                tables.push(table.build());
766                (component_ids.into(), TableId::from_usize(tables.len() - 1))
767            });
768
769        *value
770    }
771
772    /// Iterates through all of the tables stored within in [`TableId`] order.
773    pub fn iter(&self) -> core::slice::Iter<'_, Table> {
774        self.tables.iter()
775    }
776
777    /// Clears all data from all [`Table`]s stored within.
778    pub(crate) fn clear(&mut self) {
779        for table in &mut self.tables {
780            table.clear();
781        }
782    }
783
784    pub(crate) fn check_change_ticks(&mut self, change_tick: Tick) {
785        for table in &mut self.tables {
786            table.check_change_ticks(change_tick);
787        }
788    }
789}
790
791impl Index<TableId> for Tables {
792    type Output = Table;
793
794    #[inline]
795    fn index(&self, index: TableId) -> &Self::Output {
796        &self.tables[index.as_usize()]
797    }
798}
799
800impl IndexMut<TableId> for Tables {
801    #[inline]
802    fn index_mut(&mut self, index: TableId) -> &mut Self::Output {
803        &mut self.tables[index.as_usize()]
804    }
805}
806
807impl Drop for Table {
808    fn drop(&mut self) {
809        let len = self.entity_count();
810        let cap = self.capacity();
811        self.entities.clear();
812        for col in self.columns.values_mut() {
813            // SAFETY: `cap` and `len` are correct
814            unsafe {
815                col.drop(cap, len);
816            }
817        }
818    }
819}
820
821#[cfg(test)]
822mod tests {
823    use crate::{
824        change_detection::MaybeLocation,
825        component::{Component, ComponentIds, Components, ComponentsRegistrator, Tick},
826        entity::Entity,
827        ptr::OwningPtr,
828        storage::{TableBuilder, TableId, TableRow, Tables},
829    };
830    use alloc::vec::Vec;
831
832    #[derive(Component)]
833    struct W<T>(T);
834
835    #[test]
836    fn only_one_empty_table() {
837        let components = Components::default();
838        let mut tables = Tables::default();
839
840        let component_ids = &[];
841        // SAFETY: component_ids is empty, so we know it cannot reference invalid component IDs
842        let table_id = unsafe { tables.get_id_or_insert(component_ids, &components) };
843
844        assert_eq!(table_id, TableId::empty());
845    }
846
847    #[test]
848    fn table() {
849        let mut components = Components::default();
850        let mut componentids = ComponentIds::default();
851        // SAFETY: They are both new.
852        let mut registrator =
853            unsafe { ComponentsRegistrator::new(&mut components, &mut componentids) };
854        let component_id = registrator.register_component::<W<TableRow>>();
855        let columns = &[component_id];
856        let mut table = TableBuilder::with_capacity(0, columns.len())
857            .add_column(components.get_info(component_id).unwrap())
858            .build();
859        let entities = (0..200).map(Entity::from_raw).collect::<Vec<_>>();
860        for entity in &entities {
861            // SAFETY: we allocate and immediately set data afterwards
862            unsafe {
863                let row = table.allocate(*entity);
864                let value: W<TableRow> = W(row);
865                OwningPtr::make(value, |value_ptr| {
866                    table.get_column_mut(component_id).unwrap().initialize(
867                        row,
868                        value_ptr,
869                        Tick::new(0),
870                        MaybeLocation::caller(),
871                    );
872                });
873            };
874        }
875
876        assert_eq!(table.entity_capacity(), 256);
877        assert_eq!(table.entity_count(), 200);
878    }
879}