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