Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/storage/blob_vec.rs
6604 views
1
use alloc::alloc::handle_alloc_error;
2
use bevy_ptr::{OwningPtr, Ptr, PtrMut};
3
use bevy_utils::OnDrop;
4
use core::{alloc::Layout, cell::UnsafeCell, num::NonZero, ptr::NonNull};
5
6
/// A flat, type-erased data storage type
7
///
8
/// Used to densely store homogeneous ECS data. A blob is usually just an arbitrary block of contiguous memory without any identity, and
9
/// could be used to represent any arbitrary data (i.e. string, arrays, etc). This type is an extendable and re-allocatable blob, which makes
10
/// it a blobby Vec, a `BlobVec`.
11
pub(super) struct BlobVec {
12
item_layout: Layout,
13
capacity: usize,
14
/// Number of elements, not bytes
15
len: usize,
16
// the `data` ptr's layout is always `array_layout(item_layout, capacity)`
17
data: NonNull<u8>,
18
// None if the underlying type doesn't need to be dropped
19
drop: Option<unsafe fn(OwningPtr<'_>)>,
20
}
21
22
// We want to ignore the `drop` field in our `Debug` impl
23
impl core::fmt::Debug for BlobVec {
24
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
25
f.debug_struct("BlobVec")
26
.field("item_layout", &self.item_layout)
27
.field("capacity", &self.capacity)
28
.field("len", &self.len)
29
.field("data", &self.data)
30
.finish()
31
}
32
}
33
34
impl BlobVec {
35
/// Creates a new [`BlobVec`] with the specified `capacity`.
36
///
37
/// `drop` is an optional function pointer that is meant to be invoked when any element in the [`BlobVec`]
38
/// should be dropped. For all Rust-based types, this should match 1:1 with the implementation of [`Drop`]
39
/// if present, and should be `None` if `T: !Drop`. For non-Rust based types, this should match any cleanup
40
/// processes typically associated with the stored element.
41
///
42
/// # Safety
43
///
44
/// `drop` should be safe to call with an [`OwningPtr`] pointing to any item that's been pushed into this [`BlobVec`].
45
///
46
/// If `drop` is `None`, the items will be leaked. This should generally be set as None based on [`needs_drop`].
47
///
48
/// [`needs_drop`]: std::mem::needs_drop
49
pub unsafe fn new(
50
item_layout: Layout,
51
drop: Option<unsafe fn(OwningPtr<'_>)>,
52
capacity: usize,
53
) -> BlobVec {
54
let align = NonZero::<usize>::new(item_layout.align()).expect("alignment must be > 0");
55
let data = bevy_ptr::dangling_with_align(align);
56
if item_layout.size() == 0 {
57
BlobVec {
58
data,
59
// ZST `BlobVec` max size is `usize::MAX`, and `reserve_exact` for ZST assumes
60
// the capacity is always `usize::MAX` and panics if it overflows.
61
capacity: usize::MAX,
62
len: 0,
63
item_layout,
64
drop,
65
}
66
} else {
67
let mut blob_vec = BlobVec {
68
data,
69
capacity: 0,
70
len: 0,
71
item_layout,
72
drop,
73
};
74
blob_vec.reserve_exact(capacity);
75
blob_vec
76
}
77
}
78
79
/// Returns the number of elements in the vector.
80
#[inline]
81
pub fn len(&self) -> usize {
82
self.len
83
}
84
85
/// Returns `true` if the vector contains no elements.
86
#[inline]
87
pub fn is_empty(&self) -> bool {
88
self.len == 0
89
}
90
91
/// Returns the [`Layout`] of the element type stored in the vector.
92
#[inline]
93
pub fn layout(&self) -> Layout {
94
self.item_layout
95
}
96
97
/// Reserves the minimum capacity for at least `additional` more elements to be inserted in the given `BlobVec`.
98
/// After calling `reserve_exact`, capacity will be greater than or equal to `self.len() + additional`. Does nothing if
99
/// the capacity is already sufficient.
100
///
101
/// Note that the allocator may give the collection more space than it requests. Therefore, capacity can not be relied upon
102
/// to be precisely minimal.
103
///
104
/// # Panics
105
///
106
/// Panics if new capacity overflows `usize`.
107
pub fn reserve_exact(&mut self, additional: usize) {
108
let available_space = self.capacity - self.len;
109
if available_space < additional {
110
// SAFETY: `available_space < additional`, so `additional - available_space > 0`
111
let increment =
112
unsafe { NonZero::<usize>::new_unchecked(additional - available_space) };
113
self.grow_exact(increment);
114
}
115
}
116
117
/// Reserves the minimum capacity for at least `additional` more elements to be inserted in the given `BlobVec`.
118
#[inline]
119
pub fn reserve(&mut self, additional: usize) {
120
/// Similar to `reserve_exact`. This method ensures that the capacity will grow at least `self.capacity()` if there is no
121
/// enough space to hold `additional` more elements.
122
#[cold]
123
fn do_reserve(slf: &mut BlobVec, additional: usize) {
124
let increment = slf.capacity.max(additional - (slf.capacity - slf.len));
125
let increment = NonZero::<usize>::new(increment).unwrap();
126
slf.grow_exact(increment);
127
}
128
129
if self.capacity - self.len < additional {
130
do_reserve(self, additional);
131
}
132
}
133
134
/// Grows the capacity by `increment` elements.
135
///
136
/// # Panics
137
///
138
/// Panics if the new capacity overflows `usize`.
139
/// For ZST it panics unconditionally because ZST `BlobVec` capacity
140
/// is initialized to `usize::MAX` and always stays that way.
141
fn grow_exact(&mut self, increment: NonZero<usize>) {
142
let new_capacity = self
143
.capacity
144
.checked_add(increment.get())
145
.expect("capacity overflow");
146
let new_layout =
147
array_layout(&self.item_layout, new_capacity).expect("array layout should be valid");
148
let new_data = if self.capacity == 0 {
149
// SAFETY:
150
// - layout has non-zero size as per safety requirement
151
unsafe { alloc::alloc::alloc(new_layout) }
152
} else {
153
// SAFETY:
154
// - ptr was be allocated via this allocator
155
// - the layout of the ptr was `array_layout(self.item_layout, self.capacity)`
156
// - `item_layout.size() > 0` and `new_capacity > 0`, so the layout size is non-zero
157
// - "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)",
158
// since the item size is always a multiple of its alignment, the rounding cannot happen
159
// here and the overflow is handled in `array_layout`
160
unsafe {
161
alloc::alloc::realloc(
162
self.get_ptr_mut().as_ptr(),
163
array_layout(&self.item_layout, self.capacity)
164
.expect("array layout should be valid"),
165
new_layout.size(),
166
)
167
}
168
};
169
170
self.data = NonNull::new(new_data).unwrap_or_else(|| handle_alloc_error(new_layout));
171
self.capacity = new_capacity;
172
}
173
174
/// Initializes the value at `index` to `value`. This function does not do any bounds checking.
175
///
176
/// # Safety
177
/// - index must be in bounds
178
/// - the memory in the [`BlobVec`] starting at index `index`, of a size matching this [`BlobVec`]'s
179
/// `item_layout`, must have been previously allocated.
180
#[inline]
181
pub unsafe fn initialize_unchecked(&mut self, index: usize, value: OwningPtr<'_>) {
182
debug_assert!(index < self.len());
183
let ptr = self.get_unchecked_mut(index);
184
core::ptr::copy_nonoverlapping::<u8>(value.as_ptr(), ptr.as_ptr(), self.item_layout.size());
185
}
186
187
/// Replaces the value at `index` with `value`. This function does not do any bounds checking.
188
///
189
/// # Safety
190
/// - index must be in-bounds
191
/// - the memory in the [`BlobVec`] starting at index `index`, of a size matching this
192
/// [`BlobVec`]'s `item_layout`, must have been previously initialized with an item matching
193
/// this [`BlobVec`]'s `item_layout`
194
/// - the memory at `*value` must also be previously initialized with an item matching this
195
/// [`BlobVec`]'s `item_layout`
196
pub unsafe fn replace_unchecked(&mut self, index: usize, value: OwningPtr<'_>) {
197
debug_assert!(index < self.len());
198
199
// Pointer to the value in the vector that will get replaced.
200
// SAFETY: The caller ensures that `index` fits in this vector.
201
let destination = NonNull::from(unsafe { self.get_unchecked_mut(index) });
202
let source = value.as_ptr();
203
204
if let Some(drop) = self.drop {
205
// Temporarily set the length to zero, so that if `drop` panics the caller
206
// will not be left with a `BlobVec` containing a dropped element within
207
// its initialized range.
208
let old_len = self.len;
209
self.len = 0;
210
211
// Transfer ownership of the old value out of the vector, so it can be dropped.
212
// SAFETY:
213
// - `destination` was obtained from a `PtrMut` in this vector, which ensures it is non-null,
214
// well-aligned for the underlying type, and has proper provenance.
215
// - The storage location will get overwritten with `value` later, which ensures
216
// that the element will not get observed or double dropped later.
217
// - If a panic occurs, `self.len` will remain `0`, which ensures a double-drop
218
// does not occur. Instead, all elements will be forgotten.
219
let old_value = unsafe { OwningPtr::new(destination) };
220
221
// This closure will run in case `drop()` panics,
222
// which ensures that `value` does not get forgotten.
223
let on_unwind = OnDrop::new(|| drop(value));
224
225
drop(old_value);
226
227
// If the above code does not panic, make sure that `value` doesn't get dropped.
228
core::mem::forget(on_unwind);
229
230
// Make the vector's contents observable again, since panics are no longer possible.
231
self.len = old_len;
232
}
233
234
// Copy the new value into the vector, overwriting the previous value.
235
// SAFETY:
236
// - `source` and `destination` were obtained from `OwningPtr`s, which ensures they are
237
// valid for both reads and writes.
238
// - The value behind `source` will only be dropped if the above branch panics,
239
// so it must still be initialized and it is safe to transfer ownership into the vector.
240
// - `source` and `destination` were obtained from different memory locations,
241
// both of which we have exclusive access to, so they are guaranteed not to overlap.
242
unsafe {
243
core::ptr::copy_nonoverlapping::<u8>(
244
source,
245
destination.as_ptr(),
246
self.item_layout.size(),
247
);
248
}
249
}
250
251
/// Appends an element to the back of the vector.
252
///
253
/// # Safety
254
/// The `value` must match the [`layout`](`BlobVec::layout`) of the elements in the [`BlobVec`].
255
#[inline]
256
pub unsafe fn push(&mut self, value: OwningPtr<'_>) {
257
self.reserve(1);
258
let index = self.len;
259
self.len += 1;
260
self.initialize_unchecked(index, value);
261
}
262
263
/// Performs a "swap remove" at the given `index`, which removes the item at `index` and moves
264
/// the last item in the [`BlobVec`] to `index` (if `index` is not the last item). It is the
265
/// caller's responsibility to drop the returned pointer, if that is desirable.
266
///
267
/// # Safety
268
/// It is the caller's responsibility to ensure that `index` is less than `self.len()`.
269
#[must_use = "The returned pointer should be used to dropped the removed element"]
270
pub unsafe fn swap_remove_and_forget_unchecked(&mut self, index: usize) -> OwningPtr<'_> {
271
debug_assert!(index < self.len());
272
// Since `index` must be strictly less than `self.len` and `index` is at least zero,
273
// `self.len` must be at least one. Thus, this cannot underflow.
274
let new_len = self.len - 1;
275
let size = self.item_layout.size();
276
if index != new_len {
277
core::ptr::swap_nonoverlapping::<u8>(
278
self.get_unchecked_mut(index).as_ptr(),
279
self.get_unchecked_mut(new_len).as_ptr(),
280
size,
281
);
282
}
283
self.len = new_len;
284
// Cannot use get_unchecked here as this is technically out of bounds after changing len.
285
// SAFETY:
286
// - `new_len` is less than the old len, so it must fit in this vector's allocation.
287
// - `size` is a multiple of the erased type's alignment,
288
// so adding a multiple of `size` will preserve alignment.
289
// - The removed element lives as long as this vector's mutable reference.
290
let p = unsafe { self.get_ptr_mut().byte_add(new_len * size) };
291
// SAFETY: The removed element is unreachable by this vector so it's safe to promote the
292
// `PtrMut` to an `OwningPtr`.
293
unsafe { p.promote() }
294
}
295
296
/// Removes the value at `index` and drops it.
297
/// Does not do any bounds checking on `index`.
298
/// The removed element is replaced by the last element of the `BlobVec`.
299
///
300
/// # Safety
301
/// It is the caller's responsibility to ensure that `index` is `< self.len()`.
302
#[inline]
303
pub unsafe fn swap_remove_and_drop_unchecked(&mut self, index: usize) {
304
debug_assert!(index < self.len());
305
let drop = self.drop;
306
let value = self.swap_remove_and_forget_unchecked(index);
307
if let Some(drop) = drop {
308
drop(value);
309
}
310
}
311
312
/// Returns a reference to the element at `index`, without doing bounds checking.
313
///
314
/// # Safety
315
/// It is the caller's responsibility to ensure that `index < self.len()`.
316
#[inline]
317
pub unsafe fn get_unchecked(&self, index: usize) -> Ptr<'_> {
318
debug_assert!(index < self.len());
319
let size = self.item_layout.size();
320
// SAFETY:
321
// - The caller ensures that `index` fits in this vector,
322
// so this operation will not overflow the original allocation.
323
// - `size` is a multiple of the erased type's alignment,
324
// so adding a multiple of `size` will preserve alignment.
325
// - The element at `index` outlives this vector's reference.
326
unsafe { self.get_ptr().byte_add(index * size) }
327
}
328
329
/// Returns a mutable reference to the element at `index`, without doing bounds checking.
330
///
331
/// # Safety
332
/// It is the caller's responsibility to ensure that `index < self.len()`.
333
#[inline]
334
pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> PtrMut<'_> {
335
debug_assert!(index < self.len());
336
let size = self.item_layout.size();
337
// SAFETY:
338
// - The caller ensures that `index` fits in this vector,
339
// so this operation will not overflow the original allocation.
340
// - `size` is a multiple of the erased type's alignment,
341
// so adding a multiple of `size` will preserve alignment.
342
// - The element at `index` outlives this vector's mutable reference.
343
unsafe { self.get_ptr_mut().byte_add(index * size) }
344
}
345
346
/// Gets a [`Ptr`] to the start of the vec
347
#[inline]
348
pub fn get_ptr(&self) -> Ptr<'_> {
349
// SAFETY: the inner data will remain valid for as long as 'self.
350
unsafe { Ptr::new(self.data) }
351
}
352
353
/// Gets a [`PtrMut`] to the start of the vec
354
#[inline]
355
pub fn get_ptr_mut(&mut self) -> PtrMut<'_> {
356
// SAFETY: the inner data will remain valid for as long as 'self.
357
unsafe { PtrMut::new(self.data) }
358
}
359
360
/// Get a reference to the entire [`BlobVec`] as if it were an array with elements of type `T`
361
///
362
/// # Safety
363
/// The type `T` must be the type of the items in this [`BlobVec`].
364
pub unsafe fn get_slice<T>(&self) -> &[UnsafeCell<T>] {
365
// SAFETY: the inner data will remain valid for as long as 'self.
366
unsafe { core::slice::from_raw_parts(self.data.as_ptr() as *const UnsafeCell<T>, self.len) }
367
}
368
369
/// Returns the drop function for values stored in the vector,
370
/// or `None` if they don't need to be dropped.
371
#[inline]
372
pub fn get_drop(&self) -> Option<unsafe fn(OwningPtr<'_>)> {
373
self.drop
374
}
375
376
/// Clears the vector, removing (and dropping) all values.
377
///
378
/// Note that this method has no effect on the allocated capacity of the vector.
379
pub fn clear(&mut self) {
380
let len = self.len;
381
// We set len to 0 _before_ dropping elements for unwind safety. This ensures we don't
382
// accidentally drop elements twice in the event of a drop impl panicking.
383
self.len = 0;
384
if let Some(drop) = self.drop {
385
let size = self.item_layout.size();
386
for i in 0..len {
387
// SAFETY:
388
// * 0 <= `i` < `len`, so `i * size` must be in bounds for the allocation.
389
// * `size` is a multiple of the erased type's alignment,
390
// so adding a multiple of `size` will preserve alignment.
391
// * The item lives until it's dropped.
392
// * The item is left unreachable so it can be safely promoted to an `OwningPtr`.
393
// NOTE: `self.get_unchecked_mut(i)` cannot be used here, since the `debug_assert`
394
// would panic due to `self.len` being set to 0.
395
let item = unsafe { self.get_ptr_mut().byte_add(i * size).promote() };
396
// SAFETY: `item` was obtained from this `BlobVec`, so its underlying type must match `drop`.
397
unsafe { drop(item) };
398
}
399
}
400
}
401
}
402
403
impl Drop for BlobVec {
404
fn drop(&mut self) {
405
self.clear();
406
let array_layout =
407
array_layout(&self.item_layout, self.capacity).expect("array layout should be valid");
408
if array_layout.size() > 0 {
409
// SAFETY: data ptr layout is correct, swap_scratch ptr layout is correct
410
unsafe {
411
alloc::alloc::dealloc(self.get_ptr_mut().as_ptr(), array_layout);
412
}
413
}
414
}
415
}
416
417
/// From <https://doc.rust-lang.org/beta/src/core/alloc/layout.rs.html>
418
pub(super) fn array_layout(layout: &Layout, n: usize) -> Option<Layout> {
419
let (array_layout, offset) = repeat_layout(layout, n)?;
420
debug_assert_eq!(layout.size(), offset);
421
Some(array_layout)
422
}
423
424
// TODO: replace with `Layout::repeat` if/when it stabilizes
425
/// From <https://doc.rust-lang.org/beta/src/core/alloc/layout.rs.html>
426
fn repeat_layout(layout: &Layout, n: usize) -> Option<(Layout, usize)> {
427
// This cannot overflow. Quoting from the invariant of Layout:
428
// > `size`, when rounded up to the nearest multiple of `align`,
429
// > must not overflow (i.e., the rounded value must be less than
430
// > `usize::MAX`)
431
let padded_size = layout.size() + padding_needed_for(layout, layout.align());
432
let alloc_size = padded_size.checked_mul(n)?;
433
434
// SAFETY: self.align is already known to be valid and alloc_size has been
435
// padded already.
436
unsafe {
437
Some((
438
Layout::from_size_align_unchecked(alloc_size, layout.align()),
439
padded_size,
440
))
441
}
442
}
443
444
/// From <https://doc.rust-lang.org/beta/src/core/alloc/layout.rs.html>
445
/// # Safety
446
/// The caller must ensure that:
447
/// - The resulting [`Layout`] is valid, by ensuring that `(layout.size() + padding_needed_for(layout, layout.align())) * n` doesn't overflow.
448
pub(super) unsafe fn array_layout_unchecked(layout: &Layout, n: usize) -> Layout {
449
let (array_layout, offset) = repeat_layout_unchecked(layout, n);
450
debug_assert_eq!(layout.size(), offset);
451
array_layout
452
}
453
454
// TODO: replace with `Layout::repeat` if/when it stabilizes
455
/// From <https://doc.rust-lang.org/beta/src/core/alloc/layout.rs.html>
456
/// # Safety
457
/// The caller must ensure that:
458
/// - The resulting [`Layout`] is valid, by ensuring that `(layout.size() + padding_needed_for(layout, layout.align())) * n` doesn't overflow.
459
unsafe fn repeat_layout_unchecked(layout: &Layout, n: usize) -> (Layout, usize) {
460
// This cannot overflow. Quoting from the invariant of Layout:
461
// > `size`, when rounded up to the nearest multiple of `align`,
462
// > must not overflow (i.e., the rounded value must be less than
463
// > `usize::MAX`)
464
let padded_size = layout.size() + padding_needed_for(layout, layout.align());
465
// This may overflow in release builds, that's why this function is unsafe.
466
let alloc_size = padded_size * n;
467
468
// SAFETY: self.align is already known to be valid and alloc_size has been
469
// padded already.
470
unsafe {
471
(
472
Layout::from_size_align_unchecked(alloc_size, layout.align()),
473
padded_size,
474
)
475
}
476
}
477
478
/// From <https://doc.rust-lang.org/beta/src/core/alloc/layout.rs.html>
479
const fn padding_needed_for(layout: &Layout, align: usize) -> usize {
480
let len = layout.size();
481
482
// Rounded up value is:
483
// len_rounded_up = (len + align - 1) & !(align - 1);
484
// and then we return the padding difference: `len_rounded_up - len`.
485
//
486
// We use modular arithmetic throughout:
487
//
488
// 1. align is guaranteed to be > 0, so align - 1 is always
489
// valid.
490
//
491
// 2. `len + align - 1` can overflow by at most `align - 1`,
492
// so the &-mask with `!(align - 1)` will ensure that in the
493
// case of overflow, `len_rounded_up` will itself be 0.
494
// Thus the returned padding, when added to `len`, yields 0,
495
// which trivially satisfies the alignment `align`.
496
//
497
// (Of course, attempts to allocate blocks of memory whose
498
// size and padding overflow in the above manner should cause
499
// the allocator to yield an error anyway.)
500
501
let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
502
len_rounded_up.wrapping_sub(len)
503
}
504
505
#[cfg(test)]
506
mod tests {
507
use super::BlobVec;
508
use crate::{component::Component, ptr::OwningPtr, world::World};
509
use alloc::{
510
rc::Rc,
511
string::{String, ToString},
512
};
513
use core::{alloc::Layout, cell::RefCell};
514
515
/// # Safety
516
///
517
/// The pointer `x` must point to a valid value of type `T` and it must be safe to drop this value.
518
unsafe fn drop_ptr<T>(x: OwningPtr<'_>) {
519
// SAFETY: It is guaranteed by the caller that `x` points to a
520
// valid value of type `T` and it is safe to drop this value.
521
unsafe {
522
x.drop_as::<T>();
523
}
524
}
525
526
/// # Safety
527
///
528
/// `blob_vec` must have a layout that matches `Layout::new::<T>()`
529
unsafe fn push<T>(blob_vec: &mut BlobVec, value: T) {
530
OwningPtr::make(value, |ptr| {
531
blob_vec.push(ptr);
532
});
533
}
534
535
/// # Safety
536
///
537
/// `blob_vec` must have a layout that matches `Layout::new::<T>()`
538
unsafe fn swap_remove<T>(blob_vec: &mut BlobVec, index: usize) -> T {
539
assert!(index < blob_vec.len());
540
let value = blob_vec.swap_remove_and_forget_unchecked(index);
541
value.read::<T>()
542
}
543
544
/// # Safety
545
///
546
/// `blob_vec` must have a layout that matches `Layout::new::<T>()`, it most store a valid `T`
547
/// value at the given `index`
548
unsafe fn get_mut<T>(blob_vec: &mut BlobVec, index: usize) -> &mut T {
549
assert!(index < blob_vec.len());
550
blob_vec.get_unchecked_mut(index).deref_mut::<T>()
551
}
552
553
#[test]
554
fn resize_test() {
555
let item_layout = Layout::new::<usize>();
556
// SAFETY: `drop` fn is `None`, usize doesn't need dropping
557
let mut blob_vec = unsafe { BlobVec::new(item_layout, None, 64) };
558
// SAFETY: `i` is a usize, i.e. the type corresponding to `item_layout`
559
unsafe {
560
for i in 0..1_000 {
561
push(&mut blob_vec, i as usize);
562
}
563
}
564
565
assert_eq!(blob_vec.len(), 1_000);
566
assert_eq!(blob_vec.capacity, 1_024);
567
}
568
569
#[derive(Debug, Eq, PartialEq, Clone)]
570
struct Foo {
571
a: u8,
572
b: String,
573
drop_counter: Rc<RefCell<usize>>,
574
}
575
576
impl Drop for Foo {
577
fn drop(&mut self) {
578
*self.drop_counter.borrow_mut() += 1;
579
}
580
}
581
582
#[test]
583
fn blob_vec() {
584
let drop_counter = Rc::new(RefCell::new(0));
585
{
586
let item_layout = Layout::new::<Foo>();
587
let drop = drop_ptr::<Foo>;
588
// SAFETY: drop is able to drop a value of its `item_layout`
589
let mut blob_vec = unsafe { BlobVec::new(item_layout, Some(drop), 2) };
590
assert_eq!(blob_vec.capacity, 2);
591
// SAFETY: the following code only deals with values of type `Foo`, which satisfies the safety requirement of `push`, `get_mut` and `swap_remove` that the
592
// values have a layout compatible to the blob vec's `item_layout`.
593
// Every index is in range.
594
unsafe {
595
let foo1 = Foo {
596
a: 42,
597
b: "abc".to_string(),
598
drop_counter: drop_counter.clone(),
599
};
600
push(&mut blob_vec, foo1.clone());
601
assert_eq!(blob_vec.len(), 1);
602
assert_eq!(get_mut::<Foo>(&mut blob_vec, 0), &foo1);
603
604
let mut foo2 = Foo {
605
a: 7,
606
b: "xyz".to_string(),
607
drop_counter: drop_counter.clone(),
608
};
609
push::<Foo>(&mut blob_vec, foo2.clone());
610
assert_eq!(blob_vec.len(), 2);
611
assert_eq!(blob_vec.capacity, 2);
612
assert_eq!(get_mut::<Foo>(&mut blob_vec, 0), &foo1);
613
assert_eq!(get_mut::<Foo>(&mut blob_vec, 1), &foo2);
614
615
get_mut::<Foo>(&mut blob_vec, 1).a += 1;
616
assert_eq!(get_mut::<Foo>(&mut blob_vec, 1).a, 8);
617
618
let foo3 = Foo {
619
a: 16,
620
b: "123".to_string(),
621
drop_counter: drop_counter.clone(),
622
};
623
624
push(&mut blob_vec, foo3.clone());
625
assert_eq!(blob_vec.len(), 3);
626
assert_eq!(blob_vec.capacity, 4);
627
628
let last_index = blob_vec.len() - 1;
629
let value = swap_remove::<Foo>(&mut blob_vec, last_index);
630
assert_eq!(foo3, value);
631
632
assert_eq!(blob_vec.len(), 2);
633
assert_eq!(blob_vec.capacity, 4);
634
635
let value = swap_remove::<Foo>(&mut blob_vec, 0);
636
assert_eq!(foo1, value);
637
assert_eq!(blob_vec.len(), 1);
638
assert_eq!(blob_vec.capacity, 4);
639
640
foo2.a = 8;
641
assert_eq!(get_mut::<Foo>(&mut blob_vec, 0), &foo2);
642
}
643
}
644
645
assert_eq!(*drop_counter.borrow(), 6);
646
}
647
648
#[test]
649
fn blob_vec_drop_empty_capacity() {
650
let item_layout = Layout::new::<Foo>();
651
let drop = drop_ptr::<Foo>;
652
// SAFETY: drop is able to drop a value of its `item_layout`
653
let _ = unsafe { BlobVec::new(item_layout, Some(drop), 0) };
654
}
655
656
#[test]
657
#[should_panic(expected = "capacity overflow")]
658
fn blob_vec_zst_size_overflow() {
659
// SAFETY: no drop is correct drop for `()`.
660
let mut blob_vec = unsafe { BlobVec::new(Layout::new::<()>(), None, 0) };
661
662
assert_eq!(usize::MAX, blob_vec.capacity, "Self-check");
663
664
// SAFETY: Because `()` is a ZST trivial drop type, and because `BlobVec` capacity
665
// is always `usize::MAX` for ZSTs, we can arbitrarily set the length
666
// and still be sound.
667
blob_vec.len = usize::MAX;
668
669
// SAFETY: `BlobVec` was initialized for `()`, so it is safe to push `()` to it.
670
unsafe {
671
OwningPtr::make((), |ptr| {
672
// This should panic because len is usize::MAX, remaining capacity is 0.
673
blob_vec.push(ptr);
674
});
675
}
676
}
677
678
#[test]
679
#[should_panic(expected = "capacity overflow")]
680
fn blob_vec_capacity_overflow() {
681
// SAFETY: no drop is correct drop for `u32`.
682
let mut blob_vec = unsafe { BlobVec::new(Layout::new::<u32>(), None, 0) };
683
684
assert_eq!(0, blob_vec.capacity, "Self-check");
685
686
OwningPtr::make(17u32, |ptr| {
687
// SAFETY: we push the value of correct type.
688
unsafe {
689
blob_vec.push(ptr);
690
}
691
});
692
693
blob_vec.reserve_exact(usize::MAX);
694
}
695
696
#[test]
697
fn aligned_zst() {
698
// NOTE: This test is explicitly for uncovering potential UB with miri.
699
700
#[derive(Component)]
701
#[repr(align(32))]
702
struct Zst;
703
704
let mut world = World::default();
705
world.spawn(Zst);
706
world.spawn(Zst);
707
world.spawn(Zst);
708
world.spawn_empty();
709
710
let mut count = 0;
711
712
let mut q = world.query::<&Zst>();
713
for zst in q.iter(&world) {
714
// Ensure that the references returned are properly aligned.
715
assert_eq!(
716
core::ptr::from_ref::<Zst>(zst) as usize % align_of::<Zst>(),
717
0
718
);
719
count += 1;
720
}
721
722
assert_eq!(count, 3);
723
}
724
}
725
726