bevy_ecs/storage/table/
column.rs

1use super::*;
2use crate::{
3    change_detection::MaybeLocation,
4    storage::{blob_array::BlobArray, thin_array_ptr::ThinArrayPtr},
5};
6use core::{mem::needs_drop, panic::Location};
7
8/// A type-erased contiguous container for data of a homogeneous type.
9///
10/// Conceptually, a `Column` is very similar to a type-erased `Box<[T]>`.
11/// It also stores the change detection ticks for its components, kept in two separate
12/// contiguous buffers internally. An element shares its data across these buffers by using the
13/// same index (i.e. the entity at row 3 has it's data at index 3 and its change detection ticks at index 3).
14///
15/// Like many other low-level storage types, `Column` 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 `Column`.
18///
19/// For performance reasons, `Column` does not does not store it's capacity and length.
20/// This type is used by [`Table`] and [`ComponentSparseSet`], where the corresponding capacity
21/// and length can be found.
22///
23/// [`ComponentSparseSet`]: crate::storage::ComponentSparseSet
24#[derive(Debug)]
25pub struct Column {
26    pub(super) data: BlobArray,
27    pub(super) added_ticks: ThinArrayPtr<UnsafeCell<Tick>>,
28    pub(super) changed_ticks: ThinArrayPtr<UnsafeCell<Tick>>,
29    pub(super) changed_by: MaybeLocation<ThinArrayPtr<UnsafeCell<&'static Location<'static>>>>,
30}
31
32impl Column {
33    /// Create a new [`Column`] with the given `capacity`.
34    pub fn with_capacity(component_info: &ComponentInfo, capacity: usize) -> Self {
35        Self {
36            // SAFETY: The components stored in this columns will match the information in `component_info`
37            data: unsafe {
38                BlobArray::with_capacity(component_info.layout(), component_info.drop(), capacity)
39            },
40            added_ticks: ThinArrayPtr::with_capacity(capacity),
41            changed_ticks: ThinArrayPtr::with_capacity(capacity),
42            changed_by: MaybeLocation::new_with(|| ThinArrayPtr::with_capacity(capacity)),
43        }
44    }
45
46    /// Swap-remove and drop the removed element, but the component at `row` must not be the last element.
47    ///
48    /// # Safety
49    /// - `row.as_usize()` < `len`
50    /// - `last_element_index` = `len - 1`
51    /// - `last_element_index` != `row.as_usize()`
52    /// -   The caller should update the `len` to `len - 1`, or immediately initialize another element in the `last_element_index`
53    pub(crate) unsafe fn swap_remove_and_drop_unchecked_nonoverlapping(
54        &mut self,
55        last_element_index: usize,
56        row: TableRow,
57    ) {
58        self.data
59            .swap_remove_and_drop_unchecked_nonoverlapping(row.index(), last_element_index);
60        self.added_ticks
61            .swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
62        self.changed_ticks
63            .swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
64        self.changed_by.as_mut().map(|changed_by| {
65            changed_by.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
66        });
67    }
68
69    /// Swap-remove and drop the removed element.
70    ///
71    /// # Safety
72    /// - `last_element_index` must be the index of the last element—stored in the highest place in memory.
73    /// - `row.as_usize()` <= `last_element_index`
74    /// -   The caller should update their saved length to reflect the change (decrement it by 1).
75    pub(crate) unsafe fn swap_remove_and_drop_unchecked(
76        &mut self,
77        last_element_index: usize,
78        row: TableRow,
79    ) {
80        self.data
81            .swap_remove_and_drop_unchecked(row.index(), last_element_index);
82        self.added_ticks
83            .swap_remove_unchecked(row.index(), last_element_index);
84        self.changed_ticks
85            .swap_remove_unchecked(row.index(), last_element_index);
86        self.changed_by.as_mut().map(|changed_by| {
87            changed_by.swap_remove_unchecked(row.index(), last_element_index);
88        });
89    }
90
91    /// Swap-remove and forgets the removed element, but the component at `row` must not be the last element.
92    ///
93    /// # Safety
94    /// - `row.as_usize()` < `len`
95    /// - `last_element_index` = `len - 1`
96    /// - `last_element_index` != `row.as_usize()`
97    /// -   The caller should update the `len` to `len - 1`, or immediately initialize another element in the `last_element_index`
98    pub(crate) unsafe fn swap_remove_and_forget_unchecked_nonoverlapping(
99        &mut self,
100        last_element_index: usize,
101        row: TableRow,
102    ) -> OwningPtr<'_> {
103        let data = self
104            .data
105            .swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
106        self.added_ticks
107            .swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
108        self.changed_ticks
109            .swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
110        self.changed_by.as_mut().map(|changed_by| {
111            changed_by.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
112        });
113        data
114    }
115
116    /// Swap-remove and forget the removed element.
117    ///
118    /// # Safety
119    /// - `last_element_index` must be the index of the last element—stored in the highest place in memory.
120    /// - `row.as_usize()` <= `last_element_index`
121    /// -   The caller should update their saved length to reflect the change (decrement it by 1).
122    pub(crate) unsafe fn swap_remove_and_forget_unchecked(
123        &mut self,
124        last_element_index: usize,
125        row: TableRow,
126    ) -> OwningPtr<'_> {
127        let data = self
128            .data
129            .swap_remove_unchecked(row.index(), last_element_index);
130        self.added_ticks
131            .swap_remove_unchecked(row.index(), last_element_index);
132        self.changed_ticks
133            .swap_remove_unchecked(row.index(), last_element_index);
134        self.changed_by
135            .as_mut()
136            .map(|changed_by| changed_by.swap_remove_unchecked(row.index(), last_element_index));
137        data
138    }
139
140    /// Call [`realloc`](std::alloc::realloc) to expand / shrink the memory allocation for this [`Column`]
141    ///
142    /// # Panics
143    /// - Panics if the any of the new capacity overflows `isize::MAX` bytes.
144    /// - Panics if the any of the reallocations causes an out-of-memory error.
145    ///
146    /// # Safety
147    /// - `current_capacity` must be the current capacity of this column (the capacity of `self.data`, `self.added_ticks`, `self.changed_tick`)
148    /// -   The caller should make sure their saved `capacity` value is updated to `new_capacity` after this operation.
149    pub(crate) unsafe fn realloc(
150        &mut self,
151        current_capacity: NonZeroUsize,
152        new_capacity: NonZeroUsize,
153    ) {
154        self.data.realloc(current_capacity, new_capacity);
155        self.added_ticks.realloc(current_capacity, new_capacity);
156        self.changed_ticks.realloc(current_capacity, new_capacity);
157        self.changed_by
158            .as_mut()
159            .map(|changed_by| changed_by.realloc(current_capacity, new_capacity));
160    }
161
162    /// Call [`alloc`](std::alloc::alloc) to allocate memory for this [`Column`]
163    /// The caller should make sure their saved `capacity` value is updated to `new_capacity` after this operation.
164    ///
165    /// # Panics
166    /// - Panics if the any of the new capacity overflows `isize::MAX` bytes.
167    /// - Panics if the any of the allocations causes an out-of-memory error.
168    pub(crate) fn alloc(&mut self, new_capacity: NonZeroUsize) {
169        self.data.alloc(new_capacity);
170        self.added_ticks.alloc(new_capacity);
171        self.changed_ticks.alloc(new_capacity);
172        self.changed_by
173            .as_mut()
174            .map(|changed_by| changed_by.alloc(new_capacity));
175    }
176
177    /// Writes component data to the column at the given row.
178    /// Assumes the slot is uninitialized, drop is not called.
179    /// To overwrite existing initialized value, use [`Self::replace`] instead.
180    ///
181    /// # Safety
182    /// - `row.as_usize()` must be in bounds.
183    /// - `comp_ptr` holds a component that matches the `component_id`
184    #[inline]
185    pub(crate) unsafe fn initialize(
186        &mut self,
187        row: TableRow,
188        data: OwningPtr<'_>,
189        tick: Tick,
190        caller: MaybeLocation,
191    ) {
192        self.data.initialize_unchecked(row.index(), data);
193        *self.added_ticks.get_unchecked_mut(row.index()).get_mut() = tick;
194        *self.changed_ticks.get_unchecked_mut(row.index()).get_mut() = tick;
195        self.changed_by
196            .as_mut()
197            .map(|changed_by| changed_by.get_unchecked_mut(row.index()).get_mut())
198            .assign(caller);
199    }
200
201    /// Overwrites component data to the column at given row. The previous value is dropped.
202    ///
203    /// # Safety
204    /// - There must be a valid initialized value stored at `row`.
205    /// - `row.as_usize()` must be in bounds.
206    /// - `data` holds a component that matches the `component_id`
207    #[inline]
208    pub(crate) unsafe fn replace(
209        &mut self,
210        row: TableRow,
211        data: OwningPtr<'_>,
212        change_tick: Tick,
213        caller: MaybeLocation,
214    ) {
215        self.data.replace_unchecked(row.index(), data);
216        *self.changed_ticks.get_unchecked_mut(row.index()).get_mut() = change_tick;
217        self.changed_by
218            .as_mut()
219            .map(|changed_by| changed_by.get_unchecked_mut(row.index()).get_mut())
220            .assign(caller);
221    }
222
223    /// Removes the element from `other` at `src_row` and inserts it
224    /// into the current column to initialize the values at `dst_row`.
225    /// Does not do any bounds checking.
226    ///
227    /// # Safety
228    ///  - `other` must have the same data layout as `self`
229    ///  - `src_row` must be in bounds for `other`
230    ///  - `dst_row` must be in bounds for `self`
231    ///  - `other[src_row]` must be initialized to a valid value.
232    ///  - `self[dst_row]` must not be initialized yet.
233    #[inline]
234    pub(crate) unsafe fn initialize_from_unchecked(
235        &mut self,
236        other: &mut Column,
237        other_last_element_index: usize,
238        src_row: TableRow,
239        dst_row: TableRow,
240    ) {
241        debug_assert!(self.data.layout() == other.data.layout());
242        // Init the data
243        let src_val = other
244            .data
245            .swap_remove_unchecked(src_row.index(), other_last_element_index);
246        self.data.initialize_unchecked(dst_row.index(), src_val);
247        // Init added_ticks
248        let added_tick = other
249            .added_ticks
250            .swap_remove_unchecked(src_row.index(), other_last_element_index);
251        self.added_ticks
252            .initialize_unchecked(dst_row.index(), added_tick);
253        // Init changed_ticks
254        let changed_tick = other
255            .changed_ticks
256            .swap_remove_unchecked(src_row.index(), other_last_element_index);
257        self.changed_ticks
258            .initialize_unchecked(dst_row.index(), changed_tick);
259        self.changed_by.as_mut().zip(other.changed_by.as_mut()).map(
260            |(self_changed_by, other_changed_by)| {
261                let changed_by = other_changed_by
262                    .swap_remove_unchecked(src_row.index(), other_last_element_index);
263                self_changed_by.initialize_unchecked(dst_row.index(), changed_by);
264            },
265        );
266    }
267
268    /// Call [`Tick::check_tick`] on all of the ticks stored in this column.
269    ///
270    /// # Safety
271    /// `len` is the actual length of this column
272    #[inline]
273    pub(crate) unsafe fn check_change_ticks(&mut self, len: usize, check: CheckChangeTicks) {
274        for i in 0..len {
275            // SAFETY:
276            // - `i` < `len`
277            // we have a mutable reference to `self`
278            unsafe { self.added_ticks.get_unchecked_mut(i) }
279                .get_mut()
280                .check_tick(check);
281            // SAFETY:
282            // - `i` < `len`
283            // we have a mutable reference to `self`
284            unsafe { self.changed_ticks.get_unchecked_mut(i) }
285                .get_mut()
286                .check_tick(check);
287        }
288    }
289
290    /// Clear all the components from this column.
291    ///
292    /// # Safety
293    /// - `len` must match the actual length of the column
294    /// -   The caller must not use the elements this column's data until [`initializing`](Self::initialize) it again (set `len` to 0).
295    pub(crate) unsafe fn clear(&mut self, len: usize) {
296        self.added_ticks.clear_elements(len);
297        self.changed_ticks.clear_elements(len);
298        self.data.clear(len);
299        self.changed_by
300            .as_mut()
301            .map(|changed_by| changed_by.clear_elements(len));
302    }
303
304    /// Because this method needs parameters, it can't be the implementation of the `Drop` trait.
305    /// The owner of this [`Column`] must call this method with the correct information.
306    ///
307    /// # Safety
308    /// - `len` is indeed the length of the column
309    /// - `cap` is indeed the capacity of the column
310    /// - the data stored in `self` will never be used again
311    pub(crate) unsafe fn drop(&mut self, cap: usize, len: usize) {
312        self.added_ticks.drop(cap, len);
313        self.changed_ticks.drop(cap, len);
314        self.data.drop(cap, len);
315        self.changed_by
316            .as_mut()
317            .map(|changed_by| changed_by.drop(cap, len));
318    }
319
320    /// Drops the last component in this column.
321    ///
322    /// # Safety
323    /// - `last_element_index` is indeed the index of the last element
324    /// - the data stored in `last_element_index` will never be used unless properly initialized again.
325    pub(crate) unsafe fn drop_last_component(&mut self, last_element_index: usize) {
326        const {
327            assert!(!needs_drop::<UnsafeCell<Tick>>());
328            assert!(!needs_drop::<UnsafeCell<&'static Location<'static>>>());
329        }
330        self.data.drop_last_element(last_element_index);
331    }
332
333    /// Get a slice to the data stored in this [`Column`].
334    ///
335    /// # Safety
336    /// - `T` must match the type of data that's stored in this [`Column`]
337    /// - `len` must match the actual length of this column (number of elements stored)
338    pub unsafe fn get_data_slice<T>(&self, len: usize) -> &[UnsafeCell<T>] {
339        // SAFETY: Upheld by caller
340        unsafe { self.data.get_sub_slice(len) }
341    }
342
343    /// Get a slice to the added [`ticks`](Tick) in this [`Column`].
344    ///
345    /// # Safety
346    /// - `len` must match the actual length of this column (number of elements stored)
347    pub unsafe fn get_added_ticks_slice(&self, len: usize) -> &[UnsafeCell<Tick>] {
348        // SAFETY: Upheld by caller
349        unsafe { self.added_ticks.as_slice(len) }
350    }
351
352    /// Get a slice to the changed [`ticks`](Tick) in this [`Column`].
353    ///
354    /// # Safety
355    /// - `len` must match the actual length of this column (number of elements stored)
356    pub unsafe fn get_changed_ticks_slice(&self, len: usize) -> &[UnsafeCell<Tick>] {
357        // SAFETY: Upheld by caller
358        unsafe { self.changed_ticks.as_slice(len) }
359    }
360
361    /// Get a slice to the calling locations that last changed each value in this [`Column`]
362    ///
363    /// # Safety
364    /// - `len` must match the actual length of this column (number of elements stored)
365    pub unsafe fn get_changed_by_slice(
366        &self,
367        len: usize,
368    ) -> MaybeLocation<&[UnsafeCell<&'static Location<'static>>]> {
369        self.changed_by
370            .as_ref()
371            .map(|changed_by| changed_by.as_slice(len))
372    }
373
374    /// Fetches a read-only reference to the data at `row`. This does not
375    /// do any bounds checking.
376    ///
377    /// # Safety
378    /// - `row` must be within the range `[0, self.len())`.
379    /// - no other mutable reference to the data of the same row can exist at the same time
380    #[inline]
381    pub unsafe fn get_data_unchecked(&self, row: TableRow) -> Ptr<'_> {
382        self.data.get_unchecked(row.index())
383    }
384
385    /// Fetches the calling location that last changed the value at `row`.
386    ///
387    /// This function does not do any bounds checking.
388    ///
389    /// # Safety
390    /// `row` must be within the range `[0, self.len())`.
391    #[inline]
392    pub unsafe fn get_changed_by_unchecked(
393        &self,
394        row: TableRow,
395    ) -> MaybeLocation<&UnsafeCell<&'static Location<'static>>> {
396        self.changed_by
397            .as_ref()
398            .map(|changed_by| changed_by.get_unchecked(row.index()))
399    }
400
401    /// Fetches the "added" change detection tick for the value at `row`.
402    /// This function does not do any bounds checking.
403    ///
404    /// # Safety
405    /// `row` must be within the range `[0, self.len())`.
406    #[inline]
407    pub unsafe fn get_added_tick_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
408        // SAFETY: Upheld by caller
409        unsafe { self.added_ticks.get_unchecked(row.index()) }
410    }
411
412    /// Fetches the "changed" change detection tick for the value at `row`
413    /// This function does not do any bounds checking.
414    ///
415    /// # Safety
416    /// `row` must be within the range `[0, self.len())`.
417    #[inline]
418    pub unsafe fn get_changed_tick_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
419        // SAFETY: Upheld by caller
420        unsafe { self.changed_ticks.get_unchecked(row.index()) }
421    }
422
423    /// Fetches the change detection ticks for the value at `row`.
424    /// This function does not do any bounds checking.
425    ///
426    /// # Safety
427    /// `row` must be within the range `[0, self.len())`.
428    #[inline]
429    pub unsafe fn get_ticks_unchecked(&self, row: TableRow) -> ComponentTicks {
430        ComponentTicks {
431            added: self.added_ticks.get_unchecked(row.index()).read(),
432            changed: self.changed_ticks.get_unchecked(row.index()).read(),
433        }
434    }
435
436    /// Returns the drop function for elements of the column,
437    /// or `None` if they don't need to be dropped.
438    #[inline]
439    pub fn get_drop(&self) -> Option<unsafe fn(OwningPtr<'_>)> {
440        self.data.get_drop()
441    }
442}