Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/storage/table/column.rs
9418 views
1
use super::*;
2
use crate::{
3
change_detection::MaybeLocation,
4
storage::{blob_array::BlobArray, thin_array_ptr::ThinArrayPtr},
5
};
6
use core::{mem::needs_drop, panic::Location};
7
8
/// A type-erased contiguous container for data of a homogeneous type.
9
///
10
/// Conceptually, a `Column` is very similar to a type-erased `Box<[T]>`.
11
/// It also stores the change detection ticks for its components, kept in two separate
12
/// contiguous buffers internally. An element shares its data across these buffers by using the
13
/// same index (i.e. the entity at row 3 has it's data at index 3 and its change detection ticks at index 3).
14
///
15
/// Like many other low-level storage types, `Column` has a limited and highly unsafe
16
/// interface. It's highly advised to use higher level types and their safe abstractions
17
/// instead of working directly with `Column`.
18
///
19
/// For performance reasons, `Column` does not does not store it's capacity and length.
20
/// This type is used by [`Table`] and [`ComponentSparseSet`], where the corresponding capacity
21
/// and length can be found.
22
///
23
/// [`ComponentSparseSet`]: crate::storage::ComponentSparseSet
24
#[derive(Debug)]
25
pub struct Column {
26
pub(super) data: BlobArray,
27
pub(super) added_ticks: ThinArrayPtr<UnsafeCell<Tick>>,
28
pub(super) changed_ticks: ThinArrayPtr<UnsafeCell<Tick>>,
29
pub(super) changed_by: MaybeLocation<ThinArrayPtr<UnsafeCell<&'static Location<'static>>>>,
30
}
31
32
impl Column {
33
/// Create a new [`Column`] with the given `capacity`.
34
pub fn with_capacity(component_info: &ComponentInfo, capacity: usize) -> Self {
35
Self {
36
// SAFETY: The components stored in this columns will match the information in `component_info`
37
data: unsafe {
38
BlobArray::with_capacity(component_info.layout(), component_info.drop(), capacity)
39
},
40
added_ticks: ThinArrayPtr::with_capacity(capacity),
41
changed_ticks: ThinArrayPtr::with_capacity(capacity),
42
changed_by: MaybeLocation::new_with(|| ThinArrayPtr::with_capacity(capacity)),
43
}
44
}
45
46
/// Swap-remove and drop the removed element, but the component at `row` must not be the last element.
47
///
48
/// # Safety
49
/// - `row.as_usize()` < `len`
50
/// - `last_element_index` = `len - 1`
51
/// - `last_element_index` != `row.as_usize()`
52
/// - The caller should update the `len` to `len - 1`, or immediately initialize another element in the `last_element_index`
53
pub(crate) unsafe fn swap_remove_and_drop_unchecked_nonoverlapping(
54
&mut self,
55
last_element_index: usize,
56
row: TableRow,
57
) {
58
self.data
59
.swap_remove_and_drop_unchecked_nonoverlapping(row.index(), last_element_index);
60
self.added_ticks
61
.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
62
self.changed_ticks
63
.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
64
self.changed_by.as_mut().map(|changed_by| {
65
changed_by.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
66
});
67
}
68
69
/// Swap-remove and drop the removed element.
70
///
71
/// # Safety
72
/// - `last_element_index` must be the index of the last element—stored in the highest place in memory.
73
/// - `row.as_usize()` <= `last_element_index`
74
/// - The caller should update their saved length to reflect the change (decrement it by 1).
75
pub(crate) unsafe fn swap_remove_and_drop_unchecked(
76
&mut self,
77
last_element_index: usize,
78
row: TableRow,
79
) {
80
self.data
81
.swap_remove_and_drop_unchecked(row.index(), last_element_index);
82
self.added_ticks
83
.swap_remove_unchecked(row.index(), last_element_index);
84
self.changed_ticks
85
.swap_remove_unchecked(row.index(), last_element_index);
86
self.changed_by.as_mut().map(|changed_by| {
87
changed_by.swap_remove_unchecked(row.index(), last_element_index);
88
});
89
}
90
91
/// Swap-remove and forgets the removed element, but the component at `row` must not be the last element.
92
///
93
/// # Safety
94
/// - `row.as_usize()` < `len`
95
/// - `last_element_index` = `len - 1`
96
/// - `last_element_index` != `row.as_usize()`
97
/// - The caller should update the `len` to `len - 1`, or immediately initialize another element in the `last_element_index`
98
pub(crate) unsafe fn swap_remove_and_forget_unchecked_nonoverlapping(
99
&mut self,
100
last_element_index: usize,
101
row: TableRow,
102
) -> OwningPtr<'_> {
103
let data = self
104
.data
105
.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
106
self.added_ticks
107
.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
108
self.changed_ticks
109
.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
110
self.changed_by.as_mut().map(|changed_by| {
111
changed_by.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
112
});
113
data
114
}
115
116
/// Swap-remove and forget the removed element.
117
///
118
/// # Safety
119
/// - `last_element_index` must be the index of the last element—stored in the highest place in memory.
120
/// - `row.as_usize()` <= `last_element_index`
121
/// - The caller should update their saved length to reflect the change (decrement it by 1).
122
pub(crate) unsafe fn swap_remove_and_forget_unchecked(
123
&mut self,
124
last_element_index: usize,
125
row: TableRow,
126
) -> OwningPtr<'_> {
127
let data = self
128
.data
129
.swap_remove_unchecked(row.index(), last_element_index);
130
self.added_ticks
131
.swap_remove_unchecked(row.index(), last_element_index);
132
self.changed_ticks
133
.swap_remove_unchecked(row.index(), last_element_index);
134
self.changed_by
135
.as_mut()
136
.map(|changed_by| changed_by.swap_remove_unchecked(row.index(), last_element_index));
137
data
138
}
139
140
/// Call [`realloc`](std::alloc::realloc) to expand / shrink the memory allocation for this [`Column`]
141
///
142
/// # Panics
143
/// - Panics if the any of the new capacity overflows `isize::MAX` bytes.
144
/// - Panics if the any of the reallocations causes an out-of-memory error.
145
///
146
/// # Safety
147
/// - `current_capacity` must be the current capacity of this column (the capacity of `self.data`, `self.added_ticks`, `self.changed_tick`)
148
/// - The caller should make sure their saved `capacity` value is updated to `new_capacity` after this operation.
149
pub(crate) unsafe fn realloc(
150
&mut self,
151
current_capacity: NonZeroUsize,
152
new_capacity: NonZeroUsize,
153
) {
154
self.data.realloc(current_capacity, new_capacity);
155
self.added_ticks.realloc(current_capacity, new_capacity);
156
self.changed_ticks.realloc(current_capacity, new_capacity);
157
self.changed_by
158
.as_mut()
159
.map(|changed_by| changed_by.realloc(current_capacity, new_capacity));
160
}
161
162
/// Call [`alloc`](std::alloc::alloc) to allocate memory for this [`Column`]
163
/// The caller should make sure their saved `capacity` value is updated to `new_capacity` after this operation.
164
///
165
/// # Panics
166
/// - Panics if the any of the new capacity overflows `isize::MAX` bytes.
167
/// - Panics if the any of the allocations causes an out-of-memory error.
168
pub(crate) fn alloc(&mut self, new_capacity: NonZeroUsize) {
169
self.data.alloc(new_capacity);
170
self.added_ticks.alloc(new_capacity);
171
self.changed_ticks.alloc(new_capacity);
172
self.changed_by
173
.as_mut()
174
.map(|changed_by| changed_by.alloc(new_capacity));
175
}
176
177
/// Writes component data to the column at the given row.
178
/// Assumes the slot is uninitialized, drop is not called.
179
/// To overwrite existing initialized value, use [`Self::replace`] instead.
180
///
181
/// # Safety
182
/// - `row.as_usize()` must be in bounds.
183
/// - `comp_ptr` holds a component that matches the `component_id`
184
#[inline]
185
pub(crate) unsafe fn initialize(
186
&mut self,
187
row: TableRow,
188
data: OwningPtr<'_>,
189
tick: Tick,
190
caller: MaybeLocation,
191
) {
192
self.data.initialize_unchecked(row.index(), data);
193
*self.added_ticks.get_unchecked_mut(row.index()).get_mut() = tick;
194
*self.changed_ticks.get_unchecked_mut(row.index()).get_mut() = tick;
195
self.changed_by
196
.as_mut()
197
.map(|changed_by| changed_by.get_unchecked_mut(row.index()).get_mut())
198
.assign(caller);
199
}
200
201
/// Overwrites component data to the column at given row. The previous value is dropped.
202
///
203
/// # Safety
204
/// - There must be a valid initialized value stored at `row`.
205
/// - `row.as_usize()` must be in bounds.
206
/// - `data` holds a component that matches the `component_id`
207
#[inline]
208
pub(crate) unsafe fn replace(
209
&mut self,
210
row: TableRow,
211
data: OwningPtr<'_>,
212
change_tick: Tick,
213
caller: MaybeLocation,
214
) {
215
self.data.replace_unchecked(row.index(), data);
216
*self.changed_ticks.get_unchecked_mut(row.index()).get_mut() = change_tick;
217
self.changed_by
218
.as_mut()
219
.map(|changed_by| changed_by.get_unchecked_mut(row.index()).get_mut())
220
.assign(caller);
221
}
222
223
/// Removes the element from `other` at `src_row` and inserts it
224
/// into the current column to initialize the values at `dst_row`.
225
/// Does not do any bounds checking.
226
///
227
/// # Safety
228
/// - `other` must have the same data layout as `self`
229
/// - `src_row` must be in bounds for `other`
230
/// - `dst_row` must be in bounds for `self`
231
/// - `other[src_row]` must be initialized to a valid value.
232
/// - `self[dst_row]` must not be initialized yet.
233
#[inline]
234
pub(crate) unsafe fn initialize_from_unchecked(
235
&mut self,
236
other: &mut Column,
237
other_last_element_index: usize,
238
src_row: TableRow,
239
dst_row: TableRow,
240
) {
241
debug_assert!(self.data.layout() == other.data.layout());
242
// Init the data
243
let src_val = other
244
.data
245
.swap_remove_unchecked(src_row.index(), other_last_element_index);
246
self.data.initialize_unchecked(dst_row.index(), src_val);
247
// Init added_ticks
248
let added_tick = other
249
.added_ticks
250
.swap_remove_unchecked(src_row.index(), other_last_element_index);
251
self.added_ticks
252
.initialize_unchecked(dst_row.index(), added_tick);
253
// Init changed_ticks
254
let changed_tick = other
255
.changed_ticks
256
.swap_remove_unchecked(src_row.index(), other_last_element_index);
257
self.changed_ticks
258
.initialize_unchecked(dst_row.index(), changed_tick);
259
self.changed_by.as_mut().zip(other.changed_by.as_mut()).map(
260
|(self_changed_by, other_changed_by)| {
261
let changed_by = other_changed_by
262
.swap_remove_unchecked(src_row.index(), other_last_element_index);
263
self_changed_by.initialize_unchecked(dst_row.index(), changed_by);
264
},
265
);
266
}
267
268
/// Call [`Tick::check_tick`] on all of the ticks stored in this column.
269
///
270
/// # Safety
271
/// `len` is the actual length of this column
272
#[inline]
273
pub(crate) unsafe fn check_change_ticks(&mut self, len: usize, check: CheckChangeTicks) {
274
for i in 0..len {
275
// SAFETY:
276
// - `i` < `len`
277
// we have a mutable reference to `self`
278
unsafe { self.added_ticks.get_unchecked_mut(i) }
279
.get_mut()
280
.check_tick(check);
281
// SAFETY:
282
// - `i` < `len`
283
// we have a mutable reference to `self`
284
unsafe { self.changed_ticks.get_unchecked_mut(i) }
285
.get_mut()
286
.check_tick(check);
287
}
288
}
289
290
/// Clear all the components from this column.
291
///
292
/// # Safety
293
/// - `len` must match the actual length of the column
294
/// - The caller must not use the elements this column's data until [`initializing`](Self::initialize) it again (set `len` to 0).
295
pub(crate) unsafe fn clear(&mut self, len: usize) {
296
self.added_ticks.clear_elements(len);
297
self.changed_ticks.clear_elements(len);
298
self.data.clear(len);
299
self.changed_by
300
.as_mut()
301
.map(|changed_by| changed_by.clear_elements(len));
302
}
303
304
/// Because this method needs parameters, it can't be the implementation of the `Drop` trait.
305
/// The owner of this [`Column`] must call this method with the correct information.
306
///
307
/// # Safety
308
/// - `len` is indeed the length of the column
309
/// - `cap` is indeed the capacity of the column
310
/// - the data stored in `self` will never be used again
311
pub(crate) unsafe fn drop(&mut self, cap: usize, len: usize) {
312
self.added_ticks.drop(cap, len);
313
self.changed_ticks.drop(cap, len);
314
self.data.drop(cap, len);
315
self.changed_by
316
.as_mut()
317
.map(|changed_by| changed_by.drop(cap, len));
318
}
319
320
/// Drops the last component in this column.
321
///
322
/// # Safety
323
/// - `last_element_index` is indeed the index of the last element
324
/// - the data stored in `last_element_index` will never be used unless properly initialized again.
325
pub(crate) unsafe fn drop_last_component(&mut self, last_element_index: usize) {
326
const {
327
assert!(!needs_drop::<UnsafeCell<Tick>>());
328
assert!(!needs_drop::<UnsafeCell<&'static Location<'static>>>());
329
}
330
self.data.drop_last_element(last_element_index);
331
}
332
333
/// Get a slice to the data stored in this [`Column`].
334
///
335
/// # Safety
336
/// - `T` must match the type of data that's stored in this [`Column`]
337
/// - `len` must match the actual length of this column (number of elements stored)
338
pub unsafe fn get_data_slice<T>(&self, len: usize) -> &[UnsafeCell<T>] {
339
// SAFETY: Upheld by caller
340
unsafe { self.data.get_sub_slice(len) }
341
}
342
343
/// Get a slice to the added [`ticks`](Tick) in this [`Column`].
344
///
345
/// # Safety
346
/// - `len` must match the actual length of this column (number of elements stored)
347
pub unsafe fn get_added_ticks_slice(&self, len: usize) -> &[UnsafeCell<Tick>] {
348
// SAFETY: Upheld by caller
349
unsafe { self.added_ticks.as_slice(len) }
350
}
351
352
/// Get a slice to the changed [`ticks`](Tick) in this [`Column`].
353
///
354
/// # Safety
355
/// - `len` must match the actual length of this column (number of elements stored)
356
pub unsafe fn get_changed_ticks_slice(&self, len: usize) -> &[UnsafeCell<Tick>] {
357
// SAFETY: Upheld by caller
358
unsafe { self.changed_ticks.as_slice(len) }
359
}
360
361
/// Get a slice to the calling locations that last changed each value in this [`Column`]
362
///
363
/// # Safety
364
/// - `len` must match the actual length of this column (number of elements stored)
365
pub unsafe fn get_changed_by_slice(
366
&self,
367
len: usize,
368
) -> MaybeLocation<&[UnsafeCell<&'static Location<'static>>]> {
369
self.changed_by
370
.as_ref()
371
.map(|changed_by| changed_by.as_slice(len))
372
}
373
374
/// Fetches a read-only reference to the data at `row`. This does not
375
/// do any bounds checking.
376
///
377
/// # Safety
378
/// - `row` must be within the range `[0, self.len())`.
379
/// - no other mutable reference to the data of the same row can exist at the same time
380
#[inline]
381
pub unsafe fn get_data_unchecked(&self, row: TableRow) -> Ptr<'_> {
382
self.data.get_unchecked(row.index())
383
}
384
385
/// Fetches the calling location that last changed the value at `row`.
386
///
387
/// This function does not do any bounds checking.
388
///
389
/// # Safety
390
/// `row` must be within the range `[0, self.len())`.
391
#[inline]
392
pub unsafe fn get_changed_by_unchecked(
393
&self,
394
row: TableRow,
395
) -> MaybeLocation<&UnsafeCell<&'static Location<'static>>> {
396
self.changed_by
397
.as_ref()
398
.map(|changed_by| changed_by.get_unchecked(row.index()))
399
}
400
401
/// Fetches the "added" change detection tick for the value at `row`.
402
/// This function does not do any bounds checking.
403
///
404
/// # Safety
405
/// `row` must be within the range `[0, self.len())`.
406
#[inline]
407
pub unsafe fn get_added_tick_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
408
// SAFETY: Upheld by caller
409
unsafe { self.added_ticks.get_unchecked(row.index()) }
410
}
411
412
/// Fetches the "changed" change detection tick for the value at `row`
413
/// This function does not do any bounds checking.
414
///
415
/// # Safety
416
/// `row` must be within the range `[0, self.len())`.
417
#[inline]
418
pub unsafe fn get_changed_tick_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
419
// SAFETY: Upheld by caller
420
unsafe { self.changed_ticks.get_unchecked(row.index()) }
421
}
422
423
/// Fetches the change detection ticks for the value at `row`.
424
/// This function does not do any bounds checking.
425
///
426
/// # Safety
427
/// `row` must be within the range `[0, self.len())`.
428
#[inline]
429
pub unsafe fn get_ticks_unchecked(&self, row: TableRow) -> ComponentTicks {
430
ComponentTicks {
431
added: self.added_ticks.get_unchecked(row.index()).read(),
432
changed: self.changed_ticks.get_unchecked(row.index()).read(),
433
}
434
}
435
436
/// Returns the drop function for elements of the column,
437
/// or `None` if they don't need to be dropped.
438
#[inline]
439
pub fn get_drop(&self) -> Option<unsafe fn(OwningPtr<'_>)> {
440
self.data.get_drop()
441
}
442
}
443
444