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}