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