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
6609 views
1
use super::*;
2
use crate::{
3
change_detection::MaybeLocation,
4
component::TickCells,
5
storage::{blob_array::BlobArray, thin_array_ptr::ThinArrayPtr},
6
};
7
use alloc::vec::Vec;
8
use bevy_ptr::PtrMut;
9
use core::panic::Location;
10
11
/// Very similar to a normal [`Column`], but with the capacities and lengths cut out for performance reasons.
12
///
13
/// This type is used by [`Table`], because all of the capacities and lengths of the [`Table`]'s columns must match.
14
///
15
/// Like many other low-level storage types, [`ThinColumn`] 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 [`ThinColumn`].
18
pub struct ThinColumn {
19
pub(super) data: BlobArray,
20
pub(super) added_ticks: ThinArrayPtr<UnsafeCell<Tick>>,
21
pub(super) changed_ticks: ThinArrayPtr<UnsafeCell<Tick>>,
22
pub(super) changed_by: MaybeLocation<ThinArrayPtr<UnsafeCell<&'static Location<'static>>>>,
23
}
24
25
impl ThinColumn {
26
/// Create a new [`ThinColumn`] with the given `capacity`.
27
pub fn with_capacity(component_info: &ComponentInfo, capacity: usize) -> Self {
28
Self {
29
// SAFETY: The components stored in this columns will match the information in `component_info`
30
data: unsafe {
31
BlobArray::with_capacity(component_info.layout(), component_info.drop(), capacity)
32
},
33
added_ticks: ThinArrayPtr::with_capacity(capacity),
34
changed_ticks: ThinArrayPtr::with_capacity(capacity),
35
changed_by: MaybeLocation::new_with(|| ThinArrayPtr::with_capacity(capacity)),
36
}
37
}
38
39
/// Swap-remove and drop the removed element, but the component at `row` must not be the last element.
40
///
41
/// # Safety
42
/// - `row.as_usize()` < `len`
43
/// - `last_element_index` = `len - 1`
44
/// - `last_element_index` != `row.as_usize()`
45
/// - The caller should update the `len` to `len - 1`, or immediately initialize another element in the `last_element_index`
46
pub(crate) unsafe fn swap_remove_and_drop_unchecked_nonoverlapping(
47
&mut self,
48
last_element_index: usize,
49
row: TableRow,
50
) {
51
self.data
52
.swap_remove_and_drop_unchecked_nonoverlapping(row.index(), last_element_index);
53
self.added_ticks
54
.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
55
self.changed_ticks
56
.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
57
self.changed_by.as_mut().map(|changed_by| {
58
changed_by.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
59
});
60
}
61
62
/// Swap-remove and drop the removed element.
63
///
64
/// # Safety
65
/// - `last_element_index` must be the index of the last element—stored in the highest place in memory.
66
/// - `row.as_usize()` <= `last_element_index`
67
/// - The caller should update the their saved length to reflect the change (decrement it by 1).
68
pub(crate) unsafe fn swap_remove_and_drop_unchecked(
69
&mut self,
70
last_element_index: usize,
71
row: TableRow,
72
) {
73
self.data
74
.swap_remove_and_drop_unchecked(row.index(), last_element_index);
75
self.added_ticks
76
.swap_remove_and_drop_unchecked(row.index(), last_element_index);
77
self.changed_ticks
78
.swap_remove_and_drop_unchecked(row.index(), last_element_index);
79
self.changed_by.as_mut().map(|changed_by| {
80
changed_by.swap_remove_and_drop_unchecked(row.index(), last_element_index);
81
});
82
}
83
84
/// Swap-remove and forget the removed element.
85
///
86
/// # Safety
87
/// - `last_element_index` must be the index of the last element—stored in the highest place in memory.
88
/// - `row.as_usize()` <= `last_element_index`
89
/// - The caller should update the their saved length to reflect the change (decrement it by 1).
90
pub(crate) unsafe fn swap_remove_and_forget_unchecked(
91
&mut self,
92
last_element_index: usize,
93
row: TableRow,
94
) {
95
let _ = self
96
.data
97
.swap_remove_unchecked(row.index(), last_element_index);
98
self.added_ticks
99
.swap_remove_unchecked(row.index(), last_element_index);
100
self.changed_ticks
101
.swap_remove_unchecked(row.index(), last_element_index);
102
self.changed_by
103
.as_mut()
104
.map(|changed_by| changed_by.swap_remove_unchecked(row.index(), last_element_index));
105
}
106
107
/// Call [`realloc`](std::alloc::realloc) to expand / shrink the memory allocation for this [`ThinColumn`]
108
///
109
/// # Safety
110
/// - `current_capacity` must be the current capacity of this column (the capacity of `self.data`, `self.added_ticks`, `self.changed_tick`)
111
/// - The caller should make sure their saved `capacity` value is updated to `new_capacity` after this operation.
112
pub(crate) unsafe fn realloc(
113
&mut self,
114
current_capacity: NonZeroUsize,
115
new_capacity: NonZeroUsize,
116
) {
117
self.data.realloc(current_capacity, new_capacity);
118
self.added_ticks.realloc(current_capacity, new_capacity);
119
self.changed_ticks.realloc(current_capacity, new_capacity);
120
self.changed_by
121
.as_mut()
122
.map(|changed_by| changed_by.realloc(current_capacity, new_capacity));
123
}
124
125
/// Call [`alloc`](std::alloc::alloc) to allocate memory for this [`ThinColumn`]
126
/// The caller should make sure their saved `capacity` value is updated to `new_capacity` after this operation.
127
pub(crate) fn alloc(&mut self, new_capacity: NonZeroUsize) {
128
self.data.alloc(new_capacity);
129
self.added_ticks.alloc(new_capacity);
130
self.changed_ticks.alloc(new_capacity);
131
self.changed_by
132
.as_mut()
133
.map(|changed_by| changed_by.alloc(new_capacity));
134
}
135
136
/// Writes component data to the column at the given row.
137
/// Assumes the slot is uninitialized, drop is not called.
138
/// To overwrite existing initialized value, use [`Self::replace`] instead.
139
///
140
/// # Safety
141
/// - `row.as_usize()` must be in bounds.
142
/// - `comp_ptr` holds a component that matches the `component_id`
143
#[inline]
144
pub(crate) unsafe fn initialize(
145
&mut self,
146
row: TableRow,
147
data: OwningPtr<'_>,
148
tick: Tick,
149
caller: MaybeLocation,
150
) {
151
self.data.initialize_unchecked(row.index(), data);
152
*self.added_ticks.get_unchecked_mut(row.index()).get_mut() = tick;
153
*self.changed_ticks.get_unchecked_mut(row.index()).get_mut() = tick;
154
self.changed_by
155
.as_mut()
156
.map(|changed_by| changed_by.get_unchecked_mut(row.index()).get_mut())
157
.assign(caller);
158
}
159
160
/// Writes component data to the column at given row. Assumes the slot is initialized, drops the previous value.
161
///
162
/// # Safety
163
/// - `row.as_usize()` must be in bounds.
164
/// - `data` holds a component that matches the `component_id`
165
#[inline]
166
pub(crate) unsafe fn replace(
167
&mut self,
168
row: TableRow,
169
data: OwningPtr<'_>,
170
change_tick: Tick,
171
caller: MaybeLocation,
172
) {
173
self.data.replace_unchecked(row.index(), data);
174
*self.changed_ticks.get_unchecked_mut(row.index()).get_mut() = change_tick;
175
self.changed_by
176
.as_mut()
177
.map(|changed_by| changed_by.get_unchecked_mut(row.index()).get_mut())
178
.assign(caller);
179
}
180
181
/// Removes the element from `other` at `src_row` and inserts it
182
/// into the current column to initialize the values at `dst_row`.
183
/// Does not do any bounds checking.
184
///
185
/// # Safety
186
/// - `other` must have the same data layout as `self`
187
/// - `src_row` must be in bounds for `other`
188
/// - `dst_row` must be in bounds for `self`
189
/// - `other[src_row]` must be initialized to a valid value.
190
/// - `self[dst_row]` must not be initialized yet.
191
#[inline]
192
pub(crate) unsafe fn initialize_from_unchecked(
193
&mut self,
194
other: &mut ThinColumn,
195
other_last_element_index: usize,
196
src_row: TableRow,
197
dst_row: TableRow,
198
) {
199
debug_assert!(self.data.layout() == other.data.layout());
200
// Init the data
201
let src_val = other
202
.data
203
.swap_remove_unchecked(src_row.index(), other_last_element_index);
204
self.data.initialize_unchecked(dst_row.index(), src_val);
205
// Init added_ticks
206
let added_tick = other
207
.added_ticks
208
.swap_remove_unchecked(src_row.index(), other_last_element_index);
209
self.added_ticks
210
.initialize_unchecked(dst_row.index(), added_tick);
211
// Init changed_ticks
212
let changed_tick = other
213
.changed_ticks
214
.swap_remove_unchecked(src_row.index(), other_last_element_index);
215
self.changed_ticks
216
.initialize_unchecked(dst_row.index(), changed_tick);
217
self.changed_by.as_mut().zip(other.changed_by.as_mut()).map(
218
|(self_changed_by, other_changed_by)| {
219
let changed_by = other_changed_by
220
.swap_remove_unchecked(src_row.index(), other_last_element_index);
221
self_changed_by.initialize_unchecked(dst_row.index(), changed_by);
222
},
223
);
224
}
225
226
/// Call [`Tick::check_tick`] on all of the ticks stored in this column.
227
///
228
/// # Safety
229
/// `len` is the actual length of this column
230
#[inline]
231
pub(crate) unsafe fn check_change_ticks(&mut self, len: usize, check: CheckChangeTicks) {
232
for i in 0..len {
233
// SAFETY:
234
// - `i` < `len`
235
// we have a mutable reference to `self`
236
unsafe { self.added_ticks.get_unchecked_mut(i) }
237
.get_mut()
238
.check_tick(check);
239
// SAFETY:
240
// - `i` < `len`
241
// we have a mutable reference to `self`
242
unsafe { self.changed_ticks.get_unchecked_mut(i) }
243
.get_mut()
244
.check_tick(check);
245
}
246
}
247
248
/// Clear all the components from this column.
249
///
250
/// # Safety
251
/// - `len` must match the actual length of the column
252
/// - The caller must not use the elements this column's data until [`initializing`](Self::initialize) it again (set `len` to 0).
253
pub(crate) unsafe fn clear(&mut self, len: usize) {
254
self.added_ticks.clear_elements(len);
255
self.changed_ticks.clear_elements(len);
256
self.data.clear(len);
257
self.changed_by
258
.as_mut()
259
.map(|changed_by| changed_by.clear_elements(len));
260
}
261
262
/// Because this method needs parameters, it can't be the implementation of the `Drop` trait.
263
/// The owner of this [`ThinColumn`] must call this method with the correct information.
264
///
265
/// # Safety
266
/// - `len` is indeed the length of the column
267
/// - `cap` is indeed the capacity of the column
268
/// - the data stored in `self` will never be used again
269
pub(crate) unsafe fn drop(&mut self, cap: usize, len: usize) {
270
self.added_ticks.drop(cap, len);
271
self.changed_ticks.drop(cap, len);
272
self.data.drop(cap, len);
273
self.changed_by
274
.as_mut()
275
.map(|changed_by| changed_by.drop(cap, len));
276
}
277
278
/// Drops the last component in this column.
279
///
280
/// # Safety
281
/// - `last_element_index` is indeed the index of the last element
282
/// - the data stored in `last_element_index` will never be used unless properly initialized again.
283
pub(crate) unsafe fn drop_last_component(&mut self, last_element_index: usize) {
284
core::ptr::drop_in_place(self.added_ticks.get_unchecked_raw(last_element_index));
285
core::ptr::drop_in_place(self.changed_ticks.get_unchecked_raw(last_element_index));
286
self.changed_by.as_mut().map(|changed_by| {
287
core::ptr::drop_in_place(changed_by.get_unchecked_raw(last_element_index));
288
});
289
self.data.drop_last_element(last_element_index);
290
}
291
292
/// Get a slice to the data stored in this [`ThinColumn`].
293
///
294
/// # Safety
295
/// - `T` must match the type of data that's stored in this [`ThinColumn`]
296
/// - `len` must match the actual length of this column (number of elements stored)
297
pub unsafe fn get_data_slice<T>(&self, len: usize) -> &[UnsafeCell<T>] {
298
self.data.get_sub_slice(len)
299
}
300
301
/// Get a slice to the added [`ticks`](Tick) in this [`ThinColumn`].
302
///
303
/// # Safety
304
/// - `len` must match the actual length of this column (number of elements stored)
305
pub unsafe fn get_added_ticks_slice(&self, len: usize) -> &[UnsafeCell<Tick>] {
306
self.added_ticks.as_slice(len)
307
}
308
309
/// Get a slice to the changed [`ticks`](Tick) in this [`ThinColumn`].
310
///
311
/// # Safety
312
/// - `len` must match the actual length of this column (number of elements stored)
313
pub unsafe fn get_changed_ticks_slice(&self, len: usize) -> &[UnsafeCell<Tick>] {
314
self.changed_ticks.as_slice(len)
315
}
316
317
/// Get a slice to the calling locations that last changed each value in this [`ThinColumn`]
318
///
319
/// # Safety
320
/// - `len` must match the actual length of this column (number of elements stored)
321
pub unsafe fn get_changed_by_slice(
322
&self,
323
len: usize,
324
) -> MaybeLocation<&[UnsafeCell<&'static Location<'static>>]> {
325
self.changed_by
326
.as_ref()
327
.map(|changed_by| changed_by.as_slice(len))
328
}
329
}
330
331
/// A type-erased contiguous container for data of a homogeneous type.
332
///
333
/// Conceptually, a [`Column`] is very similar to a type-erased `Vec<T>`.
334
/// It also stores the change detection ticks for its components, kept in two separate
335
/// contiguous buffers internally. An element shares its data across these buffers by using the
336
/// same index (i.e. the entity at row 3 has it's data at index 3 and its change detection ticks at index 3).
337
///
338
/// Like many other low-level storage types, [`Column`] has a limited and highly unsafe
339
/// interface. It's highly advised to use higher level types and their safe abstractions
340
/// instead of working directly with [`Column`].
341
#[derive(Debug)]
342
pub struct Column {
343
pub(super) data: BlobVec,
344
pub(super) added_ticks: Vec<UnsafeCell<Tick>>,
345
pub(super) changed_ticks: Vec<UnsafeCell<Tick>>,
346
changed_by: MaybeLocation<Vec<UnsafeCell<&'static Location<'static>>>>,
347
}
348
349
impl Column {
350
/// Constructs a new [`Column`], configured with a component's layout and an initial `capacity`.
351
#[inline]
352
pub(crate) fn with_capacity(component_info: &ComponentInfo, capacity: usize) -> Self {
353
Column {
354
// SAFETY: component_info.drop() is valid for the types that will be inserted.
355
data: unsafe { BlobVec::new(component_info.layout(), component_info.drop(), capacity) },
356
added_ticks: Vec::with_capacity(capacity),
357
changed_ticks: Vec::with_capacity(capacity),
358
changed_by: MaybeLocation::new_with(|| Vec::with_capacity(capacity)),
359
}
360
}
361
362
/// Fetches the [`Layout`] for the underlying type.
363
#[inline]
364
pub fn item_layout(&self) -> Layout {
365
self.data.layout()
366
}
367
368
/// Writes component data to the column at given row.
369
/// Assumes the slot is initialized, calls drop.
370
///
371
/// # Safety
372
/// Assumes data has already been allocated for the given row.
373
#[inline]
374
pub(crate) unsafe fn replace(
375
&mut self,
376
row: TableRow,
377
data: OwningPtr<'_>,
378
change_tick: Tick,
379
caller: MaybeLocation,
380
) {
381
debug_assert!(row.index() < self.len());
382
self.data.replace_unchecked(row.index(), data);
383
*self.changed_ticks.get_unchecked_mut(row.index()).get_mut() = change_tick;
384
self.changed_by
385
.as_mut()
386
.map(|changed_by| changed_by.get_unchecked_mut(row.index()).get_mut())
387
.assign(caller);
388
}
389
390
/// Gets the current number of elements stored in the column.
391
#[inline]
392
pub fn len(&self) -> usize {
393
self.data.len()
394
}
395
396
/// Checks if the column is empty. Returns `true` if there are no elements, `false` otherwise.
397
#[inline]
398
pub fn is_empty(&self) -> bool {
399
self.data.is_empty()
400
}
401
402
/// Removes an element from the [`Column`].
403
///
404
/// - The value will be dropped if it implements [`Drop`].
405
/// - This does not preserve ordering, but is O(1).
406
/// - This does not do any bounds checking.
407
/// - The element is replaced with the last element in the [`Column`].
408
///
409
/// # Safety
410
/// `row` must be within the range `[0, self.len())`.
411
#[inline]
412
pub(crate) unsafe fn swap_remove_unchecked(&mut self, row: TableRow) {
413
self.data.swap_remove_and_drop_unchecked(row.index());
414
self.added_ticks.swap_remove(row.index());
415
self.changed_ticks.swap_remove(row.index());
416
self.changed_by
417
.as_mut()
418
.map(|changed_by| changed_by.swap_remove(row.index()));
419
}
420
421
/// Removes an element from the [`Column`] and returns it and its change detection ticks.
422
/// This does not preserve ordering, but is O(1) and does not do any bounds checking.
423
///
424
/// The element is replaced with the last element in the [`Column`].
425
///
426
/// It's the caller's responsibility to ensure that the removed value is dropped or used.
427
/// Failure to do so may result in resources not being released (i.e. files handles not being
428
/// released, memory leaks, etc.)
429
///
430
/// # Safety
431
/// `row` must be within the range `[0, self.len())`.
432
#[inline]
433
#[must_use = "The returned pointer should be used to dropped the removed component"]
434
pub(crate) unsafe fn swap_remove_and_forget_unchecked(
435
&mut self,
436
row: TableRow,
437
) -> (OwningPtr<'_>, ComponentTicks, MaybeLocation) {
438
let data = self.data.swap_remove_and_forget_unchecked(row.index());
439
let added = self.added_ticks.swap_remove(row.index()).into_inner();
440
let changed = self.changed_ticks.swap_remove(row.index()).into_inner();
441
let caller = self
442
.changed_by
443
.as_mut()
444
.map(|changed_by| changed_by.swap_remove(row.index()).into_inner());
445
(data, ComponentTicks { added, changed }, caller)
446
}
447
448
/// Pushes a new value onto the end of the [`Column`].
449
///
450
/// # Safety
451
/// `ptr` must point to valid data of this column's component type
452
pub(crate) unsafe fn push(
453
&mut self,
454
ptr: OwningPtr<'_>,
455
ticks: ComponentTicks,
456
caller: MaybeLocation,
457
) {
458
self.data.push(ptr);
459
self.added_ticks.push(UnsafeCell::new(ticks.added));
460
self.changed_ticks.push(UnsafeCell::new(ticks.changed));
461
self.changed_by
462
.as_mut()
463
.zip(caller)
464
.map(|(changed_by, caller)| changed_by.push(UnsafeCell::new(caller)));
465
}
466
467
/// Fetches the data pointer to the first element of the [`Column`].
468
///
469
/// The pointer is type erased, so using this function to fetch anything
470
/// other than the first element will require computing the offset using
471
/// [`Column::item_layout`].
472
#[inline]
473
pub fn get_data_ptr(&self) -> Ptr<'_> {
474
self.data.get_ptr()
475
}
476
477
/// Fetches the slice to the [`Column`]'s data cast to a given type.
478
///
479
/// Note: The values stored within are [`UnsafeCell`].
480
/// Users of this API must ensure that accesses to each individual element
481
/// adhere to the safety invariants of [`UnsafeCell`].
482
///
483
/// # Safety
484
/// The type `T` must be the type of the items in this column.
485
pub unsafe fn get_data_slice<T>(&self) -> &[UnsafeCell<T>] {
486
self.data.get_slice()
487
}
488
489
/// Fetches the slice to the [`Column`]'s "added" change detection ticks.
490
///
491
/// Note: The values stored within are [`UnsafeCell`].
492
/// Users of this API must ensure that accesses to each individual element
493
/// adhere to the safety invariants of [`UnsafeCell`].
494
#[inline]
495
pub fn get_added_ticks_slice(&self) -> &[UnsafeCell<Tick>] {
496
&self.added_ticks
497
}
498
499
/// Fetches the slice to the [`Column`]'s "changed" change detection ticks.
500
///
501
/// Note: The values stored within are [`UnsafeCell`].
502
/// Users of this API must ensure that accesses to each individual element
503
/// adhere to the safety invariants of [`UnsafeCell`].
504
#[inline]
505
pub fn get_changed_ticks_slice(&self) -> &[UnsafeCell<Tick>] {
506
&self.changed_ticks
507
}
508
509
/// Fetches a reference to the data and change detection ticks at `row`.
510
///
511
/// Returns `None` if `row` is out of bounds.
512
#[inline]
513
pub fn get(&self, row: TableRow) -> Option<(Ptr<'_>, TickCells<'_>)> {
514
(row.index() < self.data.len())
515
// SAFETY: The row is length checked before fetching the pointer. This is being
516
// accessed through a read-only reference to the column.
517
.then(|| unsafe {
518
(
519
self.data.get_unchecked(row.index()),
520
TickCells {
521
added: self.added_ticks.get_unchecked(row.index()),
522
changed: self.changed_ticks.get_unchecked(row.index()),
523
},
524
)
525
})
526
}
527
528
/// Fetches a read-only reference to the data at `row`.
529
///
530
/// Returns `None` if `row` is out of bounds.
531
#[inline]
532
pub fn get_data(&self, row: TableRow) -> Option<Ptr<'_>> {
533
(row.index() < self.data.len()).then(|| {
534
// SAFETY: The row is length checked before fetching the pointer. This is being
535
// accessed through a read-only reference to the column.
536
unsafe { self.data.get_unchecked(row.index()) }
537
})
538
}
539
540
/// Fetches a read-only reference to the data at `row`. Unlike [`Column::get`] this does not
541
/// do any bounds checking.
542
///
543
/// # Safety
544
/// - `row` must be within the range `[0, self.len())`.
545
/// - no other mutable reference to the data of the same row can exist at the same time
546
#[inline]
547
pub unsafe fn get_data_unchecked(&self, row: TableRow) -> Ptr<'_> {
548
debug_assert!(row.index() < self.data.len());
549
self.data.get_unchecked(row.index())
550
}
551
552
/// Fetches a mutable reference to the data at `row`.
553
///
554
/// Returns `None` if `row` is out of bounds.
555
#[inline]
556
pub fn get_data_mut(&mut self, row: TableRow) -> Option<PtrMut<'_>> {
557
(row.index() < self.data.len()).then(|| {
558
// SAFETY: The row is length checked before fetching the pointer. This is being
559
// accessed through an exclusive reference to the column.
560
unsafe { self.data.get_unchecked_mut(row.index()) }
561
})
562
}
563
564
/// Fetches the "added" change detection tick for the value at `row`.
565
///
566
/// Returns `None` if `row` is out of bounds.
567
///
568
/// Note: The values stored within are [`UnsafeCell`].
569
/// Users of this API must ensure that accesses to each individual element
570
/// adhere to the safety invariants of [`UnsafeCell`].
571
#[inline]
572
pub fn get_added_tick(&self, row: TableRow) -> Option<&UnsafeCell<Tick>> {
573
self.added_ticks.get(row.index())
574
}
575
576
/// Fetches the "changed" change detection tick for the value at `row`.
577
///
578
/// Returns `None` if `row` is out of bounds.
579
///
580
/// Note: The values stored within are [`UnsafeCell`].
581
/// Users of this API must ensure that accesses to each individual element
582
/// adhere to the safety invariants of [`UnsafeCell`].
583
#[inline]
584
pub fn get_changed_tick(&self, row: TableRow) -> Option<&UnsafeCell<Tick>> {
585
self.changed_ticks.get(row.index())
586
}
587
588
/// Fetches the change detection ticks for the value at `row`.
589
///
590
/// Returns `None` if `row` is out of bounds.
591
#[inline]
592
pub fn get_ticks(&self, row: TableRow) -> Option<ComponentTicks> {
593
if row.index() < self.data.len() {
594
// SAFETY: The size of the column has already been checked.
595
Some(unsafe { self.get_ticks_unchecked(row) })
596
} else {
597
None
598
}
599
}
600
601
/// Fetches the "added" change detection tick for the value at `row`. Unlike [`Column::get_added_tick`]
602
/// this function does not do any bounds checking.
603
///
604
/// # Safety
605
/// `row` must be within the range `[0, self.len())`.
606
#[inline]
607
pub unsafe fn get_added_tick_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
608
debug_assert!(row.index() < self.added_ticks.len());
609
self.added_ticks.get_unchecked(row.index())
610
}
611
612
/// Fetches the "changed" change detection tick for the value at `row`. Unlike [`Column::get_changed_tick`]
613
/// this function does not do any bounds checking.
614
///
615
/// # Safety
616
/// `row` must be within the range `[0, self.len())`.
617
#[inline]
618
pub unsafe fn get_changed_tick_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
619
debug_assert!(row.index() < self.changed_ticks.len());
620
self.changed_ticks.get_unchecked(row.index())
621
}
622
623
/// Fetches the change detection ticks for the value at `row`. Unlike [`Column::get_ticks`]
624
/// this function does not do any bounds checking.
625
///
626
/// # Safety
627
/// `row` must be within the range `[0, self.len())`.
628
#[inline]
629
pub unsafe fn get_ticks_unchecked(&self, row: TableRow) -> ComponentTicks {
630
debug_assert!(row.index() < self.added_ticks.len());
631
debug_assert!(row.index() < self.changed_ticks.len());
632
ComponentTicks {
633
added: self.added_ticks.get_unchecked(row.index()).read(),
634
changed: self.changed_ticks.get_unchecked(row.index()).read(),
635
}
636
}
637
638
/// Clears the column, removing all values.
639
///
640
/// Note that this function has no effect on the allocated capacity of the [`Column`]>
641
pub fn clear(&mut self) {
642
self.data.clear();
643
self.added_ticks.clear();
644
self.changed_ticks.clear();
645
self.changed_by.as_mut().map(Vec::clear);
646
}
647
648
#[inline]
649
pub(crate) fn check_change_ticks(&mut self, check: CheckChangeTicks) {
650
for component_ticks in &mut self.added_ticks {
651
component_ticks.get_mut().check_tick(check);
652
}
653
for component_ticks in &mut self.changed_ticks {
654
component_ticks.get_mut().check_tick(check);
655
}
656
}
657
658
/// Fetches the calling location that last changed the value at `row`.
659
///
660
/// Returns `None` if `row` is out of bounds.
661
///
662
/// Note: The values stored within are [`UnsafeCell`].
663
/// Users of this API must ensure that accesses to each individual element
664
/// adhere to the safety invariants of [`UnsafeCell`].
665
#[inline]
666
pub fn get_changed_by(
667
&self,
668
row: TableRow,
669
) -> MaybeLocation<Option<&UnsafeCell<&'static Location<'static>>>> {
670
self.changed_by
671
.as_ref()
672
.map(|changed_by| changed_by.get(row.index()))
673
}
674
675
/// Fetches the calling location that last changed the value at `row`.
676
///
677
/// Unlike [`Column::get_changed_by`] this function does not do any bounds checking.
678
///
679
/// # Safety
680
/// `row` must be within the range `[0, self.len())`.
681
#[inline]
682
pub unsafe fn get_changed_by_unchecked(
683
&self,
684
row: TableRow,
685
) -> MaybeLocation<&UnsafeCell<&'static Location<'static>>> {
686
self.changed_by.as_ref().map(|changed_by| {
687
debug_assert!(row.index() < changed_by.len());
688
changed_by.get_unchecked(row.index())
689
})
690
}
691
692
/// Returns the drop function for elements of the column,
693
/// or `None` if they don't need to be dropped.
694
#[inline]
695
pub fn get_drop(&self) -> Option<unsafe fn(OwningPtr<'_>)> {
696
self.data.get_drop()
697
}
698
}
699
700