Skip to main content

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