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}