bevy_ecs/storage/table/
column.rs

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