Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/storage/thin_array_ptr.rs
6604 views
1
use crate::query::DebugCheckedUnwrap;
2
use alloc::{
3
alloc::{alloc, handle_alloc_error, realloc},
4
boxed::Box,
5
};
6
use 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
19
pub struct ThinArrayPtr<T> {
20
data: NonNull<T>,
21
#[cfg(debug_assertions)]
22
capacity: usize,
23
}
24
25
impl<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
309
impl<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
}
323
324