bevy_ecs/storage/table/
column.rs

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