Path: blob/main/crates/bevy_ecs/src/storage/thin_array_ptr.rs
9331 views
use crate::query::DebugCheckedUnwrap;1use alloc::alloc::{alloc, handle_alloc_error, realloc};2use core::{3alloc::Layout,4mem::{needs_drop, size_of},5num::NonZeroUsize,6ptr::{self, NonNull},7};89/// Similar to [`Vec<T>`], but with the capacity and length cut out for performance reasons.10///11/// This type can be treated as a `ManuallyDrop<Box<[T]>>` without a built in length. To avoid12/// memory leaks, [`drop`](Self::drop) must be called when no longer in use.13///14/// [`Vec<T>`]: alloc::vec::Vec15#[derive(Debug)]16pub struct ThinArrayPtr<T> {17data: NonNull<T>,18#[cfg(debug_assertions)]19capacity: usize,20}2122impl<T> ThinArrayPtr<T> {23fn empty() -> Self {24Self {25data: NonNull::dangling(),26#[cfg(debug_assertions)]27capacity: 0,28}29}3031#[inline(always)]32fn set_capacity(&mut self, _capacity: usize) {33#[cfg(debug_assertions)]34{35self.capacity = _capacity;36}37}3839/// Create a new [`ThinArrayPtr`] with a given capacity. If the `capacity` is 0, this will no allocate any memory.40///41/// # Panics42/// - Panics if the new capacity overflows `isize::MAX` bytes.43/// - Panics if the allocation causes an out-of-memory error.44#[inline]45pub fn with_capacity(capacity: usize) -> Self {46let mut arr = Self::empty();47if capacity > 0 {48// SAFETY:49// - The `current_capacity` is 0 because it was just created50unsafe { arr.alloc(NonZeroUsize::new_unchecked(capacity)) };51}52arr53}5455/// Allocate memory for the array, this should only be used if not previous allocation has been made (capacity = 0)56/// The caller should update their saved `capacity` value to reflect the fact that it was changed57///58/// # Panics59/// - Panics if the new capacity overflows `isize::MAX` bytes.60/// - Panics if the allocation causes an out-of-memory error.61///62/// # Panics63/// - Panics if the new capacity overflows `usize`64pub fn alloc(&mut self, capacity: NonZeroUsize) {65self.set_capacity(capacity.get());66if size_of::<T>() != 0 {67let new_layout = Layout::array::<T>(capacity.get())68.expect("layout should be valid (arithmetic overflow)");69// SAFETY:70// - layout has non-zero size, `capacity` > 0, `size` > 0 (`size_of::<T>() != 0`)71self.data = NonNull::new(unsafe { alloc(new_layout) })72.unwrap_or_else(|| handle_alloc_error(new_layout))73.cast();74}75}7677/// Reallocate memory for the array, this should only be used if a previous allocation for this array has been made (capacity > 0).78///79/// # Panics80/// - Panics if the new capacity overflows `isize::MAX` bytes81/// - Panics if the allocation causes an out-of-memory error.82///83/// # Safety84/// - The current capacity is indeed greater than 085/// - The caller should update their saved `capacity` value to reflect the fact that it was changed86pub unsafe fn realloc(&mut self, current_capacity: NonZeroUsize, new_capacity: NonZeroUsize) {87#[cfg(debug_assertions)]88assert_eq!(self.capacity, current_capacity.get());89self.set_capacity(new_capacity.get());90if size_of::<T>() != 0 {91let new_layout =92Layout::array::<T>(new_capacity.get()).expect("overflow while allocating memory");93// SAFETY:94// - ptr was be allocated via this allocator95// - the layout of the array is the same as `Layout::array::<T>(current_capacity)`96// - the size of `T` is non 0, and `new_capacity` > 097// - "new_size, when rounded up to the nearest multiple of layout.align(), must not overflow (i.e., the rounded value must be less than usize::MAX)",98// since the item size is always a multiple of its align, the rounding cannot happen99// here and the overflow is handled in `Layout::array`100self.data = NonNull::new(unsafe {101realloc(102self.data.cast().as_ptr(),103// We can use `unwrap_unchecked` because this is the Layout of the current allocation, it must be valid104Layout::array::<T>(current_capacity.get()).debug_checked_unwrap(),105new_layout.size(),106)107})108.unwrap_or_else(|| handle_alloc_error(new_layout))109.cast();110}111}112113/// Initializes the value at `index` to `value`. This function does not do any bounds checking.114///115/// # Safety116/// `index` must be in bounds i.e. within the `capacity`.117/// if `index` = `len` the caller should update their saved `len` value to reflect the fact that it was changed118#[inline]119pub unsafe fn initialize_unchecked(&mut self, index: usize, value: T) {120// SAFETY:121// - `self.data` and the resulting pointer are in the same allocated object122// - the memory address of the last element doesn't overflow `isize`, so if `index` is in bounds, it won't overflow either123let ptr = unsafe { self.data.as_ptr().add(index) };124// SAFETY: `index` is in bounds, therefore the pointer to that location in the array is valid, and aligned.125unsafe { ptr::write(ptr, value) };126}127128/// Get a reference to the element at `index`. This method doesn't do any bounds checking.129///130/// # Safety131/// - `index` must be safe to read.132#[inline]133pub unsafe fn get_unchecked(&self, index: usize) -> &'_ T {134// SAFETY:135// - `self.data` and the resulting pointer are in the same allocated object136// - the memory address of the last element doesn't overflow `isize`, so if `index` is in bounds, it won't overflow either137let ptr = unsafe { self.data.as_ptr().add(index) };138// SAFETY:139// - The pointer is properly aligned140// - It is dereferenceable (all in the same allocation)141// - `index` < `len` and the element is safe to write to, so its valid142// - We have a reference to self, so no other mutable accesses to the element can occur143unsafe {144ptr.as_ref()145// SAFETY: We can use `unwarp_unchecked` because the pointer isn't null)146.debug_checked_unwrap()147}148}149150/// Get a mutable reference to the element at `index`. This method doesn't do any bounds checking.151///152/// # Safety153/// - `index` must be safe to write to.154#[inline]155pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &'_ mut T {156// SAFETY:157// - `self.data` and the resulting pointer are in the same allocated object158// - the memory address of the last element doesn't overflow `isize`, so if `index` is in bounds, it won't overflow either159let ptr = unsafe { self.data.as_ptr().add(index) };160// SAFETY:161// - The pointer is properly aligned162// - It is dereferenceable (all in the same allocation)163// - `index` < `len` and the element is safe to write to, so its valid164// - We have a mutable reference to `self`165unsafe {166ptr.as_mut()167// SAFETY: We can use `unwarp_unchecked` because the pointer isn't null)168.unwrap_unchecked()169}170}171172/// Perform a [`swap-remove`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.swap_remove) and return the removed value.173///174/// # Safety175/// - `index_to_keep` must be safe to access (within the bounds of the length of the array).176/// - `index_to_remove` must be safe to access (within the bounds of the length of the array).177/// - `index_to_remove` != `index_to_keep`178/// - The caller should address the inconsistent state of the array that has occurred after the swap, either:179/// 1) initialize a different value in `index_to_keep`180/// 2) update the saved length of the array if `index_to_keep` was the last element.181#[inline]182pub unsafe fn swap_remove_unchecked_nonoverlapping(183&mut self,184index_to_remove: usize,185index_to_keep: usize,186) -> T {187#[cfg(debug_assertions)]188{189debug_assert!(self.capacity > index_to_keep);190debug_assert!(self.capacity > index_to_remove);191debug_assert_ne!(index_to_keep, index_to_remove);192}193let base_ptr = self.data.as_ptr();194let value = ptr::read(base_ptr.add(index_to_remove));195ptr::copy_nonoverlapping(196base_ptr.add(index_to_keep),197base_ptr.add(index_to_remove),1981,199);200value201}202203/// Perform a [`swap-remove`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.swap_remove) and return the removed value.204///205/// # Safety206/// - `index_to_keep` must be safe to access (within the bounds of the length of the array).207/// - `index_to_remove` must be safe to access (within the bounds of the length of the array).208/// - `index_to_remove` != `index_to_keep`209/// - The caller should address the inconsistent state of the array that has occurred after the swap, either:210/// 1) initialize a different value in `index_to_keep`211/// 2) update the saved length of the array if `index_to_keep` was the last element.212#[inline]213pub unsafe fn swap_remove_unchecked(214&mut self,215index_to_remove: usize,216index_to_keep: usize,217) -> T {218if index_to_remove != index_to_keep {219return self.swap_remove_unchecked_nonoverlapping(index_to_remove, index_to_keep);220}221ptr::read(self.data.as_ptr().add(index_to_remove))222}223224/// Get a raw pointer to the last element of the array, return `None` if the length is 0225///226/// # Safety227/// - ensure that `current_len` is indeed the len of the array228#[inline]229unsafe fn last_element(&mut self, current_len: usize) -> Option<*mut T> {230(current_len != 0).then_some(self.data.as_ptr().add(current_len - 1))231}232233/// Clears the array, removing (and dropping) Note that this method has no effect on the allocated capacity of the vector.234///235/// # Safety236/// - `current_len` is indeed the length of the array237/// - The caller should update their saved length value238pub unsafe fn clear_elements(&mut self, mut current_len: usize) {239if needs_drop::<T>() {240while let Some(to_drop) = self.last_element(current_len) {241ptr::drop_in_place(to_drop);242current_len -= 1;243}244}245}246247/// Drop the entire array and all its elements.248///249/// # Safety250/// - `current_len` is indeed the length of the array251/// - `current_capacity` is indeed the capacity of the array252/// - The caller must not use this `ThinArrayPtr` in any way after calling this function253pub unsafe fn drop(&mut self, current_capacity: usize, current_len: usize) {254#[cfg(debug_assertions)]255assert_eq!(self.capacity, current_capacity);256if current_capacity != 0 {257self.clear_elements(current_len);258let layout = Layout::array::<T>(current_capacity).expect("layout should be valid");259alloc::alloc::dealloc(self.data.as_ptr().cast(), layout);260}261self.set_capacity(0);262}263264/// Get the [`ThinArrayPtr`] as a slice with a given length.265///266/// # Safety267/// - `slice_len` must match the actual length of the array268#[inline]269pub unsafe fn as_slice(&self, slice_len: usize) -> &[T] {270// SAFETY:271// - the data is valid - allocated with the same allocator272// - non-null and well-aligned273// - we have a shared reference to self - the data will not be mutated during 'a274unsafe { core::slice::from_raw_parts(self.data.as_ptr(), slice_len) }275}276}277278279