Path: blob/main/crates/bevy_ecs/src/storage/table/column.rs
9418 views
use super::*;1use crate::{2change_detection::MaybeLocation,3storage::{blob_array::BlobArray, thin_array_ptr::ThinArrayPtr},4};5use core::{mem::needs_drop, panic::Location};67/// A type-erased contiguous container for data of a homogeneous type.8///9/// Conceptually, a `Column` is very similar to a type-erased `Box<[T]>`.10/// It also stores the change detection ticks for its components, kept in two separate11/// contiguous buffers internally. An element shares its data across these buffers by using the12/// same index (i.e. the entity at row 3 has it's data at index 3 and its change detection ticks at index 3).13///14/// Like many other low-level storage types, `Column` has a limited and highly unsafe15/// interface. It's highly advised to use higher level types and their safe abstractions16/// instead of working directly with `Column`.17///18/// For performance reasons, `Column` does not does not store it's capacity and length.19/// This type is used by [`Table`] and [`ComponentSparseSet`], where the corresponding capacity20/// and length can be found.21///22/// [`ComponentSparseSet`]: crate::storage::ComponentSparseSet23#[derive(Debug)]24pub struct Column {25pub(super) data: BlobArray,26pub(super) added_ticks: ThinArrayPtr<UnsafeCell<Tick>>,27pub(super) changed_ticks: ThinArrayPtr<UnsafeCell<Tick>>,28pub(super) changed_by: MaybeLocation<ThinArrayPtr<UnsafeCell<&'static Location<'static>>>>,29}3031impl Column {32/// Create a new [`Column`] with the given `capacity`.33pub fn with_capacity(component_info: &ComponentInfo, capacity: usize) -> Self {34Self {35// SAFETY: The components stored in this columns will match the information in `component_info`36data: unsafe {37BlobArray::with_capacity(component_info.layout(), component_info.drop(), capacity)38},39added_ticks: ThinArrayPtr::with_capacity(capacity),40changed_ticks: ThinArrayPtr::with_capacity(capacity),41changed_by: MaybeLocation::new_with(|| ThinArrayPtr::with_capacity(capacity)),42}43}4445/// Swap-remove and drop the removed element, but the component at `row` must not be the last element.46///47/// # Safety48/// - `row.as_usize()` < `len`49/// - `last_element_index` = `len - 1`50/// - `last_element_index` != `row.as_usize()`51/// - The caller should update the `len` to `len - 1`, or immediately initialize another element in the `last_element_index`52pub(crate) unsafe fn swap_remove_and_drop_unchecked_nonoverlapping(53&mut self,54last_element_index: usize,55row: TableRow,56) {57self.data58.swap_remove_and_drop_unchecked_nonoverlapping(row.index(), last_element_index);59self.added_ticks60.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);61self.changed_ticks62.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);63self.changed_by.as_mut().map(|changed_by| {64changed_by.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);65});66}6768/// Swap-remove and drop the removed element.69///70/// # Safety71/// - `last_element_index` must be the index of the last element—stored in the highest place in memory.72/// - `row.as_usize()` <= `last_element_index`73/// - The caller should update their saved length to reflect the change (decrement it by 1).74pub(crate) unsafe fn swap_remove_and_drop_unchecked(75&mut self,76last_element_index: usize,77row: TableRow,78) {79self.data80.swap_remove_and_drop_unchecked(row.index(), last_element_index);81self.added_ticks82.swap_remove_unchecked(row.index(), last_element_index);83self.changed_ticks84.swap_remove_unchecked(row.index(), last_element_index);85self.changed_by.as_mut().map(|changed_by| {86changed_by.swap_remove_unchecked(row.index(), last_element_index);87});88}8990/// Swap-remove and forgets the removed element, but the component at `row` must not be the last element.91///92/// # Safety93/// - `row.as_usize()` < `len`94/// - `last_element_index` = `len - 1`95/// - `last_element_index` != `row.as_usize()`96/// - The caller should update the `len` to `len - 1`, or immediately initialize another element in the `last_element_index`97pub(crate) unsafe fn swap_remove_and_forget_unchecked_nonoverlapping(98&mut self,99last_element_index: usize,100row: TableRow,101) -> OwningPtr<'_> {102let data = self103.data104.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);105self.added_ticks106.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);107self.changed_ticks108.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);109self.changed_by.as_mut().map(|changed_by| {110changed_by.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);111});112data113}114115/// Swap-remove and forget the removed element.116///117/// # Safety118/// - `last_element_index` must be the index of the last element—stored in the highest place in memory.119/// - `row.as_usize()` <= `last_element_index`120/// - The caller should update their saved length to reflect the change (decrement it by 1).121pub(crate) unsafe fn swap_remove_and_forget_unchecked(122&mut self,123last_element_index: usize,124row: TableRow,125) -> OwningPtr<'_> {126let data = self127.data128.swap_remove_unchecked(row.index(), last_element_index);129self.added_ticks130.swap_remove_unchecked(row.index(), last_element_index);131self.changed_ticks132.swap_remove_unchecked(row.index(), last_element_index);133self.changed_by134.as_mut()135.map(|changed_by| changed_by.swap_remove_unchecked(row.index(), last_element_index));136data137}138139/// Call [`realloc`](std::alloc::realloc) to expand / shrink the memory allocation for this [`Column`]140///141/// # Panics142/// - Panics if the any of the new capacity overflows `isize::MAX` bytes.143/// - Panics if the any of the reallocations causes an out-of-memory error.144///145/// # Safety146/// - `current_capacity` must be the current capacity of this column (the capacity of `self.data`, `self.added_ticks`, `self.changed_tick`)147/// - The caller should make sure their saved `capacity` value is updated to `new_capacity` after this operation.148pub(crate) unsafe fn realloc(149&mut self,150current_capacity: NonZeroUsize,151new_capacity: NonZeroUsize,152) {153self.data.realloc(current_capacity, new_capacity);154self.added_ticks.realloc(current_capacity, new_capacity);155self.changed_ticks.realloc(current_capacity, new_capacity);156self.changed_by157.as_mut()158.map(|changed_by| changed_by.realloc(current_capacity, new_capacity));159}160161/// Call [`alloc`](std::alloc::alloc) to allocate memory for this [`Column`]162/// The caller should make sure their saved `capacity` value is updated to `new_capacity` after this operation.163///164/// # Panics165/// - Panics if the any of the new capacity overflows `isize::MAX` bytes.166/// - Panics if the any of the allocations causes an out-of-memory error.167pub(crate) fn alloc(&mut self, new_capacity: NonZeroUsize) {168self.data.alloc(new_capacity);169self.added_ticks.alloc(new_capacity);170self.changed_ticks.alloc(new_capacity);171self.changed_by172.as_mut()173.map(|changed_by| changed_by.alloc(new_capacity));174}175176/// Writes component data to the column at the given row.177/// Assumes the slot is uninitialized, drop is not called.178/// To overwrite existing initialized value, use [`Self::replace`] instead.179///180/// # Safety181/// - `row.as_usize()` must be in bounds.182/// - `comp_ptr` holds a component that matches the `component_id`183#[inline]184pub(crate) unsafe fn initialize(185&mut self,186row: TableRow,187data: OwningPtr<'_>,188tick: Tick,189caller: MaybeLocation,190) {191self.data.initialize_unchecked(row.index(), data);192*self.added_ticks.get_unchecked_mut(row.index()).get_mut() = tick;193*self.changed_ticks.get_unchecked_mut(row.index()).get_mut() = tick;194self.changed_by195.as_mut()196.map(|changed_by| changed_by.get_unchecked_mut(row.index()).get_mut())197.assign(caller);198}199200/// Overwrites component data to the column at given row. The previous value is dropped.201///202/// # Safety203/// - There must be a valid initialized value stored at `row`.204/// - `row.as_usize()` must be in bounds.205/// - `data` holds a component that matches the `component_id`206#[inline]207pub(crate) unsafe fn replace(208&mut self,209row: TableRow,210data: OwningPtr<'_>,211change_tick: Tick,212caller: MaybeLocation,213) {214self.data.replace_unchecked(row.index(), data);215*self.changed_ticks.get_unchecked_mut(row.index()).get_mut() = change_tick;216self.changed_by217.as_mut()218.map(|changed_by| changed_by.get_unchecked_mut(row.index()).get_mut())219.assign(caller);220}221222/// Removes the element from `other` at `src_row` and inserts it223/// into the current column to initialize the values at `dst_row`.224/// Does not do any bounds checking.225///226/// # Safety227/// - `other` must have the same data layout as `self`228/// - `src_row` must be in bounds for `other`229/// - `dst_row` must be in bounds for `self`230/// - `other[src_row]` must be initialized to a valid value.231/// - `self[dst_row]` must not be initialized yet.232#[inline]233pub(crate) unsafe fn initialize_from_unchecked(234&mut self,235other: &mut Column,236other_last_element_index: usize,237src_row: TableRow,238dst_row: TableRow,239) {240debug_assert!(self.data.layout() == other.data.layout());241// Init the data242let src_val = other243.data244.swap_remove_unchecked(src_row.index(), other_last_element_index);245self.data.initialize_unchecked(dst_row.index(), src_val);246// Init added_ticks247let added_tick = other248.added_ticks249.swap_remove_unchecked(src_row.index(), other_last_element_index);250self.added_ticks251.initialize_unchecked(dst_row.index(), added_tick);252// Init changed_ticks253let changed_tick = other254.changed_ticks255.swap_remove_unchecked(src_row.index(), other_last_element_index);256self.changed_ticks257.initialize_unchecked(dst_row.index(), changed_tick);258self.changed_by.as_mut().zip(other.changed_by.as_mut()).map(259|(self_changed_by, other_changed_by)| {260let changed_by = other_changed_by261.swap_remove_unchecked(src_row.index(), other_last_element_index);262self_changed_by.initialize_unchecked(dst_row.index(), changed_by);263},264);265}266267/// Call [`Tick::check_tick`] on all of the ticks stored in this column.268///269/// # Safety270/// `len` is the actual length of this column271#[inline]272pub(crate) unsafe fn check_change_ticks(&mut self, len: usize, check: CheckChangeTicks) {273for i in 0..len {274// SAFETY:275// - `i` < `len`276// we have a mutable reference to `self`277unsafe { self.added_ticks.get_unchecked_mut(i) }278.get_mut()279.check_tick(check);280// SAFETY:281// - `i` < `len`282// we have a mutable reference to `self`283unsafe { self.changed_ticks.get_unchecked_mut(i) }284.get_mut()285.check_tick(check);286}287}288289/// Clear all the components from this column.290///291/// # Safety292/// - `len` must match the actual length of the column293/// - The caller must not use the elements this column's data until [`initializing`](Self::initialize) it again (set `len` to 0).294pub(crate) unsafe fn clear(&mut self, len: usize) {295self.added_ticks.clear_elements(len);296self.changed_ticks.clear_elements(len);297self.data.clear(len);298self.changed_by299.as_mut()300.map(|changed_by| changed_by.clear_elements(len));301}302303/// Because this method needs parameters, it can't be the implementation of the `Drop` trait.304/// The owner of this [`Column`] must call this method with the correct information.305///306/// # Safety307/// - `len` is indeed the length of the column308/// - `cap` is indeed the capacity of the column309/// - the data stored in `self` will never be used again310pub(crate) unsafe fn drop(&mut self, cap: usize, len: usize) {311self.added_ticks.drop(cap, len);312self.changed_ticks.drop(cap, len);313self.data.drop(cap, len);314self.changed_by315.as_mut()316.map(|changed_by| changed_by.drop(cap, len));317}318319/// Drops the last component in this column.320///321/// # Safety322/// - `last_element_index` is indeed the index of the last element323/// - the data stored in `last_element_index` will never be used unless properly initialized again.324pub(crate) unsafe fn drop_last_component(&mut self, last_element_index: usize) {325const {326assert!(!needs_drop::<UnsafeCell<Tick>>());327assert!(!needs_drop::<UnsafeCell<&'static Location<'static>>>());328}329self.data.drop_last_element(last_element_index);330}331332/// Get a slice to the data stored in this [`Column`].333///334/// # Safety335/// - `T` must match the type of data that's stored in this [`Column`]336/// - `len` must match the actual length of this column (number of elements stored)337pub unsafe fn get_data_slice<T>(&self, len: usize) -> &[UnsafeCell<T>] {338// SAFETY: Upheld by caller339unsafe { self.data.get_sub_slice(len) }340}341342/// Get a slice to the added [`ticks`](Tick) in this [`Column`].343///344/// # Safety345/// - `len` must match the actual length of this column (number of elements stored)346pub unsafe fn get_added_ticks_slice(&self, len: usize) -> &[UnsafeCell<Tick>] {347// SAFETY: Upheld by caller348unsafe { self.added_ticks.as_slice(len) }349}350351/// Get a slice to the changed [`ticks`](Tick) in this [`Column`].352///353/// # Safety354/// - `len` must match the actual length of this column (number of elements stored)355pub unsafe fn get_changed_ticks_slice(&self, len: usize) -> &[UnsafeCell<Tick>] {356// SAFETY: Upheld by caller357unsafe { self.changed_ticks.as_slice(len) }358}359360/// Get a slice to the calling locations that last changed each value in this [`Column`]361///362/// # Safety363/// - `len` must match the actual length of this column (number of elements stored)364pub unsafe fn get_changed_by_slice(365&self,366len: usize,367) -> MaybeLocation<&[UnsafeCell<&'static Location<'static>>]> {368self.changed_by369.as_ref()370.map(|changed_by| changed_by.as_slice(len))371}372373/// Fetches a read-only reference to the data at `row`. This does not374/// do any bounds checking.375///376/// # Safety377/// - `row` must be within the range `[0, self.len())`.378/// - no other mutable reference to the data of the same row can exist at the same time379#[inline]380pub unsafe fn get_data_unchecked(&self, row: TableRow) -> Ptr<'_> {381self.data.get_unchecked(row.index())382}383384/// Fetches the calling location that last changed the value at `row`.385///386/// This function does not do any bounds checking.387///388/// # Safety389/// `row` must be within the range `[0, self.len())`.390#[inline]391pub unsafe fn get_changed_by_unchecked(392&self,393row: TableRow,394) -> MaybeLocation<&UnsafeCell<&'static Location<'static>>> {395self.changed_by396.as_ref()397.map(|changed_by| changed_by.get_unchecked(row.index()))398}399400/// Fetches the "added" change detection tick for the value at `row`.401/// This function does not do any bounds checking.402///403/// # Safety404/// `row` must be within the range `[0, self.len())`.405#[inline]406pub unsafe fn get_added_tick_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {407// SAFETY: Upheld by caller408unsafe { self.added_ticks.get_unchecked(row.index()) }409}410411/// Fetches the "changed" change detection tick for the value at `row`412/// This function does not do any bounds checking.413///414/// # Safety415/// `row` must be within the range `[0, self.len())`.416#[inline]417pub unsafe fn get_changed_tick_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {418// SAFETY: Upheld by caller419unsafe { self.changed_ticks.get_unchecked(row.index()) }420}421422/// Fetches the change detection ticks for the value at `row`.423/// This function does not do any bounds checking.424///425/// # Safety426/// `row` must be within the range `[0, self.len())`.427#[inline]428pub unsafe fn get_ticks_unchecked(&self, row: TableRow) -> ComponentTicks {429ComponentTicks {430added: self.added_ticks.get_unchecked(row.index()).read(),431changed: self.changed_ticks.get_unchecked(row.index()).read(),432}433}434435/// Returns the drop function for elements of the column,436/// or `None` if they don't need to be dropped.437#[inline]438pub fn get_drop(&self) -> Option<unsafe fn(OwningPtr<'_>)> {439self.data.get_drop()440}441}442443444