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