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}