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}