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

bevy_ecs/storage/
blob_vec.rs

1use alloc::alloc::handle_alloc_error;
2use bevy_ptr::{OwningPtr, Ptr, PtrMut};
3use bevy_utils::OnDrop;
4use 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`.
11pub(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
23impl 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
34impl 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
403impl 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>
418pub(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>
426fn 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.
448pub(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.
459unsafe 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>
479const 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)]
506mod 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}