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