Thanks to visit codestin.com
Credit goes to docs.rs

bevy_ecs/storage/
thin_array_ptr.rs

1use crate::query::DebugCheckedUnwrap;
2use alloc::{
3    alloc::{alloc, handle_alloc_error, realloc},
4    boxed::Box,
5};
6use core::{
7    alloc::Layout,
8    mem::{needs_drop, size_of},
9    num::NonZeroUsize,
10    ptr::{self, NonNull},
11};
12
13/// Similar to [`Vec<T>`], but with the capacity and length cut out for performance reasons.
14///
15/// This type can be treated as a `ManuallyDrop<Box<[T]>>` without a built in length. To avoid
16/// memory leaks, [`drop`](Self::drop) must be called when no longer in use.
17///
18/// [`Vec<T>`]: alloc::vec::Vec
19pub struct ThinArrayPtr<T> {
20    data: NonNull<T>,
21    #[cfg(debug_assertions)]
22    capacity: usize,
23}
24
25impl<T> ThinArrayPtr<T> {
26    fn empty() -> Self {
27        #[cfg(debug_assertions)]
28        {
29            Self {
30                data: NonNull::dangling(),
31                capacity: 0,
32            }
33        }
34        #[cfg(not(debug_assertions))]
35        {
36            Self {
37                data: NonNull::dangling(),
38            }
39        }
40    }
41
42    #[inline(always)]
43    fn set_capacity(&mut self, _capacity: usize) {
44        #[cfg(debug_assertions)]
45        {
46            self.capacity = _capacity;
47        }
48    }
49
50    /// Create a new [`ThinArrayPtr`] with a given capacity. If the `capacity` is 0, this will no allocate any memory.
51    #[inline]
52    pub fn with_capacity(capacity: usize) -> Self {
53        let mut arr = Self::empty();
54        if capacity > 0 {
55            // SAFETY:
56            // - The `current_capacity` is 0 because it was just created
57            unsafe { arr.alloc(NonZeroUsize::new_unchecked(capacity)) };
58        }
59        arr
60    }
61
62    /// Allocate memory for the array, this should only be used if not previous allocation has been made (capacity = 0)
63    /// The caller should update their saved `capacity` value to reflect the fact that it was changed
64    ///
65    /// # Panics
66    /// - Panics if the new capacity overflows `usize`
67    pub fn alloc(&mut self, capacity: NonZeroUsize) {
68        self.set_capacity(capacity.get());
69        if size_of::<T>() != 0 {
70            let new_layout = Layout::array::<T>(capacity.get())
71                .expect("layout should be valid (arithmetic overflow)");
72            // SAFETY:
73            // - layout has non-zero size, `capacity` > 0, `size` > 0 (`size_of::<T>() != 0`)
74            self.data = NonNull::new(unsafe { alloc(new_layout) })
75                .unwrap_or_else(|| handle_alloc_error(new_layout))
76                .cast();
77        }
78    }
79
80    /// Reallocate memory for the array, this should only be used if a previous allocation for this array has been made (capacity > 0).
81    ///
82    /// # Panics
83    /// - Panics if the new capacity overflows `usize`
84    ///
85    /// # Safety
86    /// - The current capacity is indeed greater than 0
87    /// - The caller should update their saved `capacity` value to reflect the fact that it was changed
88    pub unsafe fn realloc(&mut self, current_capacity: NonZeroUsize, new_capacity: NonZeroUsize) {
89        #[cfg(debug_assertions)]
90        assert_eq!(self.capacity, current_capacity.get());
91        self.set_capacity(new_capacity.get());
92        if size_of::<T>() != 0 {
93            let new_layout =
94                Layout::array::<T>(new_capacity.get()).expect("overflow while allocating memory");
95            // SAFETY:
96            // - ptr was be allocated via this allocator
97            // - the layout of the array is the same as `Layout::array::<T>(current_capacity)`
98            // - the size of `T` is non 0, and `new_capacity` > 0
99            // - "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)",
100            // since the item size is always a multiple of its align, the rounding cannot happen
101            // here and the overflow is handled in `Layout::array`
102            self.data = NonNull::new(unsafe {
103                realloc(
104                    self.data.cast().as_ptr(),
105                    // We can use `unwrap_unchecked` because this is the Layout of the current allocation, it must be valid
106                    Layout::array::<T>(current_capacity.get()).debug_checked_unwrap(),
107                    new_layout.size(),
108                )
109            })
110            .unwrap_or_else(|| handle_alloc_error(new_layout))
111            .cast();
112        }
113    }
114
115    /// Initializes the value at `index` to `value`. This function does not do any bounds checking.
116    ///
117    /// # Safety
118    /// `index` must be in bounds i.e. within the `capacity`.
119    /// if `index` = `len` the caller should update their saved `len` value to reflect the fact that it was changed
120    #[inline]
121    pub unsafe fn initialize_unchecked(&mut self, index: usize, value: T) {
122        // SAFETY: `index` is in bounds
123        let ptr = unsafe { self.get_unchecked_raw(index) };
124        // SAFETY: `index` is in bounds, therefore the pointer to that location in the array is valid, and aligned.
125        unsafe { ptr::write(ptr, value) };
126    }
127
128    /// Get a raw pointer to the element at `index`. This method doesn't do any bounds checking.
129    ///
130    /// # Safety
131    /// - `index` must be safe to access.
132    #[inline]
133    pub unsafe fn get_unchecked_raw(&mut self, index: usize) -> *mut T {
134        // SAFETY:
135        // - `self.data` and the resulting pointer are in the same allocated object
136        // - the memory address of the last element doesn't overflow `isize`, so if `index` is in bounds, it won't overflow either
137        unsafe { self.data.as_ptr().add(index) }
138    }
139
140    /// Get a reference to the element at `index`. This method doesn't do any bounds checking.
141    ///
142    /// # Safety
143    /// - `index` must be safe to read.
144    #[inline]
145    pub unsafe fn get_unchecked(&self, index: usize) -> &'_ T {
146        // SAFETY:
147        // - `self.data` and the resulting pointer are in the same allocated object
148        // - the memory address of the last element doesn't overflow `isize`, so if `index` is in bounds, it won't overflow either
149        let ptr = unsafe { self.data.as_ptr().add(index) };
150        // SAFETY:
151        // - The pointer is properly aligned
152        // - It is dereferenceable (all in the same allocation)
153        // - `index` < `len` and the element is safe to write to, so its valid
154        // - We have a reference to self, so no other mutable accesses to the element can occur
155        unsafe {
156            ptr.as_ref()
157                // SAFETY: We can use `unwarp_unchecked` because the pointer isn't null)
158                .debug_checked_unwrap()
159        }
160    }
161
162    /// Get a mutable reference to the element at `index`. This method doesn't do any bounds checking.
163    ///
164    /// # Safety
165    /// - `index` must be safe to write to.
166    #[inline]
167    pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &'_ mut T {
168        // SAFETY:
169        // - `self.data` and the resulting pointer are in the same allocated object
170        // - the memory address of the last element doesn't overflow `isize`, so if `index` is in bounds, it won't overflow either
171        let ptr = unsafe { self.data.as_ptr().add(index) };
172        // SAFETY:
173        // - The pointer is properly aligned
174        // - It is dereferenceable (all in the same allocation)
175        // - `index` < `len` and the element is safe to write to, so its valid
176        // - We have a mutable reference to `self`
177        unsafe {
178            ptr.as_mut()
179                // SAFETY: We can use `unwarp_unchecked` because the pointer isn't null)
180                .unwrap_unchecked()
181        }
182    }
183
184    /// Perform a [`swap-remove`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.swap_remove) and return the removed value.
185    ///
186    /// # Safety
187    /// - `index_to_keep` must be safe to access (within the bounds of the length of the array).
188    /// - `index_to_remove` must be safe to access (within the bounds of the length of the array).
189    /// - `index_to_remove` != `index_to_keep`
190    /// -  The caller should address the inconsistent state of the array that has occurred after the swap, either:
191    ///     1) initialize a different value in `index_to_keep`
192    ///     2) update the saved length of the array if `index_to_keep` was the last element.
193    #[inline]
194    pub unsafe fn swap_remove_unchecked_nonoverlapping(
195        &mut self,
196        index_to_remove: usize,
197        index_to_keep: usize,
198    ) -> T {
199        #[cfg(debug_assertions)]
200        {
201            debug_assert!(self.capacity > index_to_keep);
202            debug_assert!(self.capacity > index_to_remove);
203            debug_assert_ne!(index_to_keep, index_to_remove);
204        }
205        let base_ptr = self.data.as_ptr();
206        let value = ptr::read(base_ptr.add(index_to_remove));
207        ptr::copy_nonoverlapping(
208            base_ptr.add(index_to_keep),
209            base_ptr.add(index_to_remove),
210            1,
211        );
212        value
213    }
214
215    /// Perform a [`swap-remove`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.swap_remove) and return the removed value.
216    ///
217    /// # Safety
218    /// - `index_to_keep` must be safe to access (within the bounds of the length of the array).
219    /// - `index_to_remove` must be safe to access (within the bounds of the length of the array).
220    /// - `index_to_remove` != `index_to_keep`
221    /// -  The caller should address the inconsistent state of the array that has occurred after the swap, either:
222    ///     1) initialize a different value in `index_to_keep`
223    ///     2) update the saved length of the array if `index_to_keep` was the last element.
224    #[inline]
225    pub unsafe fn swap_remove_unchecked(
226        &mut self,
227        index_to_remove: usize,
228        index_to_keep: usize,
229    ) -> T {
230        if index_to_remove != index_to_keep {
231            return self.swap_remove_unchecked_nonoverlapping(index_to_remove, index_to_keep);
232        }
233        ptr::read(self.data.as_ptr().add(index_to_remove))
234    }
235
236    /// Perform a [`swap-remove`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.swap_remove) and drop the removed value.
237    ///
238    /// # Safety
239    /// - `index_to_keep` must be safe to access (within the bounds of the length of the array).
240    /// - `index_to_remove` must be safe to access (within the bounds of the length of the array).
241    /// - `index_to_remove` != `index_to_keep`
242    /// -  The caller should address the inconsistent state of the array that has occurred after the swap, either:
243    ///     1) initialize a different value in `index_to_keep`
244    ///     2) update the saved length of the array if `index_to_keep` was the last element.
245    #[inline]
246    pub unsafe fn swap_remove_and_drop_unchecked(
247        &mut self,
248        index_to_remove: usize,
249        index_to_keep: usize,
250    ) {
251        let val = &mut self.swap_remove_unchecked(index_to_remove, index_to_keep);
252        ptr::drop_in_place(ptr::from_mut(val));
253    }
254
255    /// Get a raw pointer to the last element of the array, return `None` if the length is 0
256    ///
257    /// # Safety
258    /// - ensure that `current_len` is indeed the len of the array
259    #[inline]
260    unsafe fn last_element(&mut self, current_len: usize) -> Option<*mut T> {
261        (current_len != 0).then_some(self.data.as_ptr().add(current_len - 1))
262    }
263
264    /// Clears the array, removing (and dropping) Note that this method has no effect on the allocated capacity of the vector.
265    ///
266    /// # Safety
267    /// - `current_len` is indeed the length of the array
268    /// -   The caller should update their saved length value
269    pub unsafe fn clear_elements(&mut self, mut current_len: usize) {
270        if needs_drop::<T>() {
271            while let Some(to_drop) = self.last_element(current_len) {
272                ptr::drop_in_place(to_drop);
273                current_len -= 1;
274            }
275        }
276    }
277
278    /// Drop the entire array and all its elements.
279    ///
280    /// # Safety
281    /// - `current_len` is indeed the length of the array
282    /// - `current_capacity` is indeed the capacity of the array
283    /// - The caller must not use this `ThinArrayPtr` in any way after calling this function
284    pub unsafe fn drop(&mut self, current_capacity: usize, current_len: usize) {
285        #[cfg(debug_assertions)]
286        assert_eq!(self.capacity, current_capacity);
287        if current_capacity != 0 {
288            self.clear_elements(current_len);
289            let layout = Layout::array::<T>(current_capacity).expect("layout should be valid");
290            alloc::alloc::dealloc(self.data.as_ptr().cast(), layout);
291        }
292        self.set_capacity(0);
293    }
294
295    /// Get the [`ThinArrayPtr`] as a slice with a given length.
296    ///
297    /// # Safety
298    /// - `slice_len` must match the actual length of the array
299    #[inline]
300    pub unsafe fn as_slice(&self, slice_len: usize) -> &[T] {
301        // SAFETY:
302        // - the data is valid - allocated with the same allocator
303        // - non-null and well-aligned
304        // - we have a shared reference to self - the data will not be mutated during 'a
305        unsafe { core::slice::from_raw_parts(self.data.as_ptr(), slice_len) }
306    }
307}
308
309impl<T> From<Box<[T]>> for ThinArrayPtr<T> {
310    fn from(value: Box<[T]>) -> Self {
311        let _len = value.len();
312        let slice_ptr = Box::<[T]>::into_raw(value);
313        // SAFETY: We just got the pointer from a reference
314        let first_element_ptr = unsafe { (*slice_ptr).as_mut_ptr() };
315        Self {
316            // SAFETY: The pointer can't be null, it came from a reference
317            data: unsafe { NonNull::new_unchecked(first_element_ptr) },
318            #[cfg(debug_assertions)]
319            capacity: _len,
320        }
321    }
322}