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}