Path: blob/main/crates/bevy_ecs/src/storage/blob_vec.rs
6604 views
use alloc::alloc::handle_alloc_error;1use bevy_ptr::{OwningPtr, Ptr, PtrMut};2use bevy_utils::OnDrop;3use core::{alloc::Layout, cell::UnsafeCell, num::NonZero, ptr::NonNull};45/// A flat, type-erased data storage type6///7/// Used to densely store homogeneous ECS data. A blob is usually just an arbitrary block of contiguous memory without any identity, and8/// could be used to represent any arbitrary data (i.e. string, arrays, etc). This type is an extendable and re-allocatable blob, which makes9/// it a blobby Vec, a `BlobVec`.10pub(super) struct BlobVec {11item_layout: Layout,12capacity: usize,13/// Number of elements, not bytes14len: usize,15// the `data` ptr's layout is always `array_layout(item_layout, capacity)`16data: NonNull<u8>,17// None if the underlying type doesn't need to be dropped18drop: Option<unsafe fn(OwningPtr<'_>)>,19}2021// We want to ignore the `drop` field in our `Debug` impl22impl core::fmt::Debug for BlobVec {23fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {24f.debug_struct("BlobVec")25.field("item_layout", &self.item_layout)26.field("capacity", &self.capacity)27.field("len", &self.len)28.field("data", &self.data)29.finish()30}31}3233impl BlobVec {34/// Creates a new [`BlobVec`] with the specified `capacity`.35///36/// `drop` is an optional function pointer that is meant to be invoked when any element in the [`BlobVec`]37/// should be dropped. For all Rust-based types, this should match 1:1 with the implementation of [`Drop`]38/// if present, and should be `None` if `T: !Drop`. For non-Rust based types, this should match any cleanup39/// processes typically associated with the stored element.40///41/// # Safety42///43/// `drop` should be safe to call with an [`OwningPtr`] pointing to any item that's been pushed into this [`BlobVec`].44///45/// If `drop` is `None`, the items will be leaked. This should generally be set as None based on [`needs_drop`].46///47/// [`needs_drop`]: std::mem::needs_drop48pub unsafe fn new(49item_layout: Layout,50drop: Option<unsafe fn(OwningPtr<'_>)>,51capacity: usize,52) -> BlobVec {53let align = NonZero::<usize>::new(item_layout.align()).expect("alignment must be > 0");54let data = bevy_ptr::dangling_with_align(align);55if item_layout.size() == 0 {56BlobVec {57data,58// ZST `BlobVec` max size is `usize::MAX`, and `reserve_exact` for ZST assumes59// the capacity is always `usize::MAX` and panics if it overflows.60capacity: usize::MAX,61len: 0,62item_layout,63drop,64}65} else {66let mut blob_vec = BlobVec {67data,68capacity: 0,69len: 0,70item_layout,71drop,72};73blob_vec.reserve_exact(capacity);74blob_vec75}76}7778/// Returns the number of elements in the vector.79#[inline]80pub fn len(&self) -> usize {81self.len82}8384/// Returns `true` if the vector contains no elements.85#[inline]86pub fn is_empty(&self) -> bool {87self.len == 088}8990/// Returns the [`Layout`] of the element type stored in the vector.91#[inline]92pub fn layout(&self) -> Layout {93self.item_layout94}9596/// Reserves the minimum capacity for at least `additional` more elements to be inserted in the given `BlobVec`.97/// After calling `reserve_exact`, capacity will be greater than or equal to `self.len() + additional`. Does nothing if98/// the capacity is already sufficient.99///100/// Note that the allocator may give the collection more space than it requests. Therefore, capacity can not be relied upon101/// to be precisely minimal.102///103/// # Panics104///105/// Panics if new capacity overflows `usize`.106pub fn reserve_exact(&mut self, additional: usize) {107let available_space = self.capacity - self.len;108if available_space < additional {109// SAFETY: `available_space < additional`, so `additional - available_space > 0`110let increment =111unsafe { NonZero::<usize>::new_unchecked(additional - available_space) };112self.grow_exact(increment);113}114}115116/// Reserves the minimum capacity for at least `additional` more elements to be inserted in the given `BlobVec`.117#[inline]118pub fn reserve(&mut self, additional: usize) {119/// Similar to `reserve_exact`. This method ensures that the capacity will grow at least `self.capacity()` if there is no120/// enough space to hold `additional` more elements.121#[cold]122fn do_reserve(slf: &mut BlobVec, additional: usize) {123let increment = slf.capacity.max(additional - (slf.capacity - slf.len));124let increment = NonZero::<usize>::new(increment).unwrap();125slf.grow_exact(increment);126}127128if self.capacity - self.len < additional {129do_reserve(self, additional);130}131}132133/// Grows the capacity by `increment` elements.134///135/// # Panics136///137/// Panics if the new capacity overflows `usize`.138/// For ZST it panics unconditionally because ZST `BlobVec` capacity139/// is initialized to `usize::MAX` and always stays that way.140fn grow_exact(&mut self, increment: NonZero<usize>) {141let new_capacity = self142.capacity143.checked_add(increment.get())144.expect("capacity overflow");145let new_layout =146array_layout(&self.item_layout, new_capacity).expect("array layout should be valid");147let new_data = if self.capacity == 0 {148// SAFETY:149// - layout has non-zero size as per safety requirement150unsafe { alloc::alloc::alloc(new_layout) }151} else {152// SAFETY:153// - ptr was be allocated via this allocator154// - the layout of the ptr was `array_layout(self.item_layout, self.capacity)`155// - `item_layout.size() > 0` and `new_capacity > 0`, so the layout size is non-zero156// - "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)",157// since the item size is always a multiple of its alignment, the rounding cannot happen158// here and the overflow is handled in `array_layout`159unsafe {160alloc::alloc::realloc(161self.get_ptr_mut().as_ptr(),162array_layout(&self.item_layout, self.capacity)163.expect("array layout should be valid"),164new_layout.size(),165)166}167};168169self.data = NonNull::new(new_data).unwrap_or_else(|| handle_alloc_error(new_layout));170self.capacity = new_capacity;171}172173/// Initializes the value at `index` to `value`. This function does not do any bounds checking.174///175/// # Safety176/// - index must be in bounds177/// - the memory in the [`BlobVec`] starting at index `index`, of a size matching this [`BlobVec`]'s178/// `item_layout`, must have been previously allocated.179#[inline]180pub unsafe fn initialize_unchecked(&mut self, index: usize, value: OwningPtr<'_>) {181debug_assert!(index < self.len());182let ptr = self.get_unchecked_mut(index);183core::ptr::copy_nonoverlapping::<u8>(value.as_ptr(), ptr.as_ptr(), self.item_layout.size());184}185186/// Replaces the value at `index` with `value`. This function does not do any bounds checking.187///188/// # Safety189/// - index must be in-bounds190/// - the memory in the [`BlobVec`] starting at index `index`, of a size matching this191/// [`BlobVec`]'s `item_layout`, must have been previously initialized with an item matching192/// this [`BlobVec`]'s `item_layout`193/// - the memory at `*value` must also be previously initialized with an item matching this194/// [`BlobVec`]'s `item_layout`195pub unsafe fn replace_unchecked(&mut self, index: usize, value: OwningPtr<'_>) {196debug_assert!(index < self.len());197198// Pointer to the value in the vector that will get replaced.199// SAFETY: The caller ensures that `index` fits in this vector.200let destination = NonNull::from(unsafe { self.get_unchecked_mut(index) });201let source = value.as_ptr();202203if let Some(drop) = self.drop {204// Temporarily set the length to zero, so that if `drop` panics the caller205// will not be left with a `BlobVec` containing a dropped element within206// its initialized range.207let old_len = self.len;208self.len = 0;209210// Transfer ownership of the old value out of the vector, so it can be dropped.211// SAFETY:212// - `destination` was obtained from a `PtrMut` in this vector, which ensures it is non-null,213// well-aligned for the underlying type, and has proper provenance.214// - The storage location will get overwritten with `value` later, which ensures215// that the element will not get observed or double dropped later.216// - If a panic occurs, `self.len` will remain `0`, which ensures a double-drop217// does not occur. Instead, all elements will be forgotten.218let old_value = unsafe { OwningPtr::new(destination) };219220// This closure will run in case `drop()` panics,221// which ensures that `value` does not get forgotten.222let on_unwind = OnDrop::new(|| drop(value));223224drop(old_value);225226// If the above code does not panic, make sure that `value` doesn't get dropped.227core::mem::forget(on_unwind);228229// Make the vector's contents observable again, since panics are no longer possible.230self.len = old_len;231}232233// Copy the new value into the vector, overwriting the previous value.234// SAFETY:235// - `source` and `destination` were obtained from `OwningPtr`s, which ensures they are236// valid for both reads and writes.237// - The value behind `source` will only be dropped if the above branch panics,238// so it must still be initialized and it is safe to transfer ownership into the vector.239// - `source` and `destination` were obtained from different memory locations,240// both of which we have exclusive access to, so they are guaranteed not to overlap.241unsafe {242core::ptr::copy_nonoverlapping::<u8>(243source,244destination.as_ptr(),245self.item_layout.size(),246);247}248}249250/// Appends an element to the back of the vector.251///252/// # Safety253/// The `value` must match the [`layout`](`BlobVec::layout`) of the elements in the [`BlobVec`].254#[inline]255pub unsafe fn push(&mut self, value: OwningPtr<'_>) {256self.reserve(1);257let index = self.len;258self.len += 1;259self.initialize_unchecked(index, value);260}261262/// Performs a "swap remove" at the given `index`, which removes the item at `index` and moves263/// the last item in the [`BlobVec`] to `index` (if `index` is not the last item). It is the264/// caller's responsibility to drop the returned pointer, if that is desirable.265///266/// # Safety267/// It is the caller's responsibility to ensure that `index` is less than `self.len()`.268#[must_use = "The returned pointer should be used to dropped the removed element"]269pub unsafe fn swap_remove_and_forget_unchecked(&mut self, index: usize) -> OwningPtr<'_> {270debug_assert!(index < self.len());271// Since `index` must be strictly less than `self.len` and `index` is at least zero,272// `self.len` must be at least one. Thus, this cannot underflow.273let new_len = self.len - 1;274let size = self.item_layout.size();275if index != new_len {276core::ptr::swap_nonoverlapping::<u8>(277self.get_unchecked_mut(index).as_ptr(),278self.get_unchecked_mut(new_len).as_ptr(),279size,280);281}282self.len = new_len;283// Cannot use get_unchecked here as this is technically out of bounds after changing len.284// SAFETY:285// - `new_len` is less than the old len, so it must fit in this vector's allocation.286// - `size` is a multiple of the erased type's alignment,287// so adding a multiple of `size` will preserve alignment.288// - The removed element lives as long as this vector's mutable reference.289let p = unsafe { self.get_ptr_mut().byte_add(new_len * size) };290// SAFETY: The removed element is unreachable by this vector so it's safe to promote the291// `PtrMut` to an `OwningPtr`.292unsafe { p.promote() }293}294295/// Removes the value at `index` and drops it.296/// Does not do any bounds checking on `index`.297/// The removed element is replaced by the last element of the `BlobVec`.298///299/// # Safety300/// It is the caller's responsibility to ensure that `index` is `< self.len()`.301#[inline]302pub unsafe fn swap_remove_and_drop_unchecked(&mut self, index: usize) {303debug_assert!(index < self.len());304let drop = self.drop;305let value = self.swap_remove_and_forget_unchecked(index);306if let Some(drop) = drop {307drop(value);308}309}310311/// Returns a reference to the element at `index`, without doing bounds checking.312///313/// # Safety314/// It is the caller's responsibility to ensure that `index < self.len()`.315#[inline]316pub unsafe fn get_unchecked(&self, index: usize) -> Ptr<'_> {317debug_assert!(index < self.len());318let size = self.item_layout.size();319// SAFETY:320// - The caller ensures that `index` fits in this vector,321// so this operation will not overflow the original allocation.322// - `size` is a multiple of the erased type's alignment,323// so adding a multiple of `size` will preserve alignment.324// - The element at `index` outlives this vector's reference.325unsafe { self.get_ptr().byte_add(index * size) }326}327328/// Returns a mutable reference to the element at `index`, without doing bounds checking.329///330/// # Safety331/// It is the caller's responsibility to ensure that `index < self.len()`.332#[inline]333pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> PtrMut<'_> {334debug_assert!(index < self.len());335let size = self.item_layout.size();336// SAFETY:337// - The caller ensures that `index` fits in this vector,338// so this operation will not overflow the original allocation.339// - `size` is a multiple of the erased type's alignment,340// so adding a multiple of `size` will preserve alignment.341// - The element at `index` outlives this vector's mutable reference.342unsafe { self.get_ptr_mut().byte_add(index * size) }343}344345/// Gets a [`Ptr`] to the start of the vec346#[inline]347pub fn get_ptr(&self) -> Ptr<'_> {348// SAFETY: the inner data will remain valid for as long as 'self.349unsafe { Ptr::new(self.data) }350}351352/// Gets a [`PtrMut`] to the start of the vec353#[inline]354pub fn get_ptr_mut(&mut self) -> PtrMut<'_> {355// SAFETY: the inner data will remain valid for as long as 'self.356unsafe { PtrMut::new(self.data) }357}358359/// Get a reference to the entire [`BlobVec`] as if it were an array with elements of type `T`360///361/// # Safety362/// The type `T` must be the type of the items in this [`BlobVec`].363pub unsafe fn get_slice<T>(&self) -> &[UnsafeCell<T>] {364// SAFETY: the inner data will remain valid for as long as 'self.365unsafe { core::slice::from_raw_parts(self.data.as_ptr() as *const UnsafeCell<T>, self.len) }366}367368/// Returns the drop function for values stored in the vector,369/// or `None` if they don't need to be dropped.370#[inline]371pub fn get_drop(&self) -> Option<unsafe fn(OwningPtr<'_>)> {372self.drop373}374375/// Clears the vector, removing (and dropping) all values.376///377/// Note that this method has no effect on the allocated capacity of the vector.378pub fn clear(&mut self) {379let len = self.len;380// We set len to 0 _before_ dropping elements for unwind safety. This ensures we don't381// accidentally drop elements twice in the event of a drop impl panicking.382self.len = 0;383if let Some(drop) = self.drop {384let size = self.item_layout.size();385for i in 0..len {386// SAFETY:387// * 0 <= `i` < `len`, so `i * size` must be in bounds for the allocation.388// * `size` is a multiple of the erased type's alignment,389// so adding a multiple of `size` will preserve alignment.390// * The item lives until it's dropped.391// * The item is left unreachable so it can be safely promoted to an `OwningPtr`.392// NOTE: `self.get_unchecked_mut(i)` cannot be used here, since the `debug_assert`393// would panic due to `self.len` being set to 0.394let item = unsafe { self.get_ptr_mut().byte_add(i * size).promote() };395// SAFETY: `item` was obtained from this `BlobVec`, so its underlying type must match `drop`.396unsafe { drop(item) };397}398}399}400}401402impl Drop for BlobVec {403fn drop(&mut self) {404self.clear();405let array_layout =406array_layout(&self.item_layout, self.capacity).expect("array layout should be valid");407if array_layout.size() > 0 {408// SAFETY: data ptr layout is correct, swap_scratch ptr layout is correct409unsafe {410alloc::alloc::dealloc(self.get_ptr_mut().as_ptr(), array_layout);411}412}413}414}415416/// From <https://doc.rust-lang.org/beta/src/core/alloc/layout.rs.html>417pub(super) fn array_layout(layout: &Layout, n: usize) -> Option<Layout> {418let (array_layout, offset) = repeat_layout(layout, n)?;419debug_assert_eq!(layout.size(), offset);420Some(array_layout)421}422423// TODO: replace with `Layout::repeat` if/when it stabilizes424/// From <https://doc.rust-lang.org/beta/src/core/alloc/layout.rs.html>425fn repeat_layout(layout: &Layout, n: usize) -> Option<(Layout, usize)> {426// This cannot overflow. Quoting from the invariant of Layout:427// > `size`, when rounded up to the nearest multiple of `align`,428// > must not overflow (i.e., the rounded value must be less than429// > `usize::MAX`)430let padded_size = layout.size() + padding_needed_for(layout, layout.align());431let alloc_size = padded_size.checked_mul(n)?;432433// SAFETY: self.align is already known to be valid and alloc_size has been434// padded already.435unsafe {436Some((437Layout::from_size_align_unchecked(alloc_size, layout.align()),438padded_size,439))440}441}442443/// From <https://doc.rust-lang.org/beta/src/core/alloc/layout.rs.html>444/// # Safety445/// The caller must ensure that:446/// - The resulting [`Layout`] is valid, by ensuring that `(layout.size() + padding_needed_for(layout, layout.align())) * n` doesn't overflow.447pub(super) unsafe fn array_layout_unchecked(layout: &Layout, n: usize) -> Layout {448let (array_layout, offset) = repeat_layout_unchecked(layout, n);449debug_assert_eq!(layout.size(), offset);450array_layout451}452453// TODO: replace with `Layout::repeat` if/when it stabilizes454/// From <https://doc.rust-lang.org/beta/src/core/alloc/layout.rs.html>455/// # Safety456/// The caller must ensure that:457/// - The resulting [`Layout`] is valid, by ensuring that `(layout.size() + padding_needed_for(layout, layout.align())) * n` doesn't overflow.458unsafe fn repeat_layout_unchecked(layout: &Layout, n: usize) -> (Layout, usize) {459// This cannot overflow. Quoting from the invariant of Layout:460// > `size`, when rounded up to the nearest multiple of `align`,461// > must not overflow (i.e., the rounded value must be less than462// > `usize::MAX`)463let padded_size = layout.size() + padding_needed_for(layout, layout.align());464// This may overflow in release builds, that's why this function is unsafe.465let alloc_size = padded_size * n;466467// SAFETY: self.align is already known to be valid and alloc_size has been468// padded already.469unsafe {470(471Layout::from_size_align_unchecked(alloc_size, layout.align()),472padded_size,473)474}475}476477/// From <https://doc.rust-lang.org/beta/src/core/alloc/layout.rs.html>478const fn padding_needed_for(layout: &Layout, align: usize) -> usize {479let len = layout.size();480481// Rounded up value is:482// len_rounded_up = (len + align - 1) & !(align - 1);483// and then we return the padding difference: `len_rounded_up - len`.484//485// We use modular arithmetic throughout:486//487// 1. align is guaranteed to be > 0, so align - 1 is always488// valid.489//490// 2. `len + align - 1` can overflow by at most `align - 1`,491// so the &-mask with `!(align - 1)` will ensure that in the492// case of overflow, `len_rounded_up` will itself be 0.493// Thus the returned padding, when added to `len`, yields 0,494// which trivially satisfies the alignment `align`.495//496// (Of course, attempts to allocate blocks of memory whose497// size and padding overflow in the above manner should cause498// the allocator to yield an error anyway.)499500let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);501len_rounded_up.wrapping_sub(len)502}503504#[cfg(test)]505mod tests {506use super::BlobVec;507use crate::{component::Component, ptr::OwningPtr, world::World};508use alloc::{509rc::Rc,510string::{String, ToString},511};512use core::{alloc::Layout, cell::RefCell};513514/// # Safety515///516/// The pointer `x` must point to a valid value of type `T` and it must be safe to drop this value.517unsafe fn drop_ptr<T>(x: OwningPtr<'_>) {518// SAFETY: It is guaranteed by the caller that `x` points to a519// valid value of type `T` and it is safe to drop this value.520unsafe {521x.drop_as::<T>();522}523}524525/// # Safety526///527/// `blob_vec` must have a layout that matches `Layout::new::<T>()`528unsafe fn push<T>(blob_vec: &mut BlobVec, value: T) {529OwningPtr::make(value, |ptr| {530blob_vec.push(ptr);531});532}533534/// # Safety535///536/// `blob_vec` must have a layout that matches `Layout::new::<T>()`537unsafe fn swap_remove<T>(blob_vec: &mut BlobVec, index: usize) -> T {538assert!(index < blob_vec.len());539let value = blob_vec.swap_remove_and_forget_unchecked(index);540value.read::<T>()541}542543/// # Safety544///545/// `blob_vec` must have a layout that matches `Layout::new::<T>()`, it most store a valid `T`546/// value at the given `index`547unsafe fn get_mut<T>(blob_vec: &mut BlobVec, index: usize) -> &mut T {548assert!(index < blob_vec.len());549blob_vec.get_unchecked_mut(index).deref_mut::<T>()550}551552#[test]553fn resize_test() {554let item_layout = Layout::new::<usize>();555// SAFETY: `drop` fn is `None`, usize doesn't need dropping556let mut blob_vec = unsafe { BlobVec::new(item_layout, None, 64) };557// SAFETY: `i` is a usize, i.e. the type corresponding to `item_layout`558unsafe {559for i in 0..1_000 {560push(&mut blob_vec, i as usize);561}562}563564assert_eq!(blob_vec.len(), 1_000);565assert_eq!(blob_vec.capacity, 1_024);566}567568#[derive(Debug, Eq, PartialEq, Clone)]569struct Foo {570a: u8,571b: String,572drop_counter: Rc<RefCell<usize>>,573}574575impl Drop for Foo {576fn drop(&mut self) {577*self.drop_counter.borrow_mut() += 1;578}579}580581#[test]582fn blob_vec() {583let drop_counter = Rc::new(RefCell::new(0));584{585let item_layout = Layout::new::<Foo>();586let drop = drop_ptr::<Foo>;587// SAFETY: drop is able to drop a value of its `item_layout`588let mut blob_vec = unsafe { BlobVec::new(item_layout, Some(drop), 2) };589assert_eq!(blob_vec.capacity, 2);590// SAFETY: the following code only deals with values of type `Foo`, which satisfies the safety requirement of `push`, `get_mut` and `swap_remove` that the591// values have a layout compatible to the blob vec's `item_layout`.592// Every index is in range.593unsafe {594let foo1 = Foo {595a: 42,596b: "abc".to_string(),597drop_counter: drop_counter.clone(),598};599push(&mut blob_vec, foo1.clone());600assert_eq!(blob_vec.len(), 1);601assert_eq!(get_mut::<Foo>(&mut blob_vec, 0), &foo1);602603let mut foo2 = Foo {604a: 7,605b: "xyz".to_string(),606drop_counter: drop_counter.clone(),607};608push::<Foo>(&mut blob_vec, foo2.clone());609assert_eq!(blob_vec.len(), 2);610assert_eq!(blob_vec.capacity, 2);611assert_eq!(get_mut::<Foo>(&mut blob_vec, 0), &foo1);612assert_eq!(get_mut::<Foo>(&mut blob_vec, 1), &foo2);613614get_mut::<Foo>(&mut blob_vec, 1).a += 1;615assert_eq!(get_mut::<Foo>(&mut blob_vec, 1).a, 8);616617let foo3 = Foo {618a: 16,619b: "123".to_string(),620drop_counter: drop_counter.clone(),621};622623push(&mut blob_vec, foo3.clone());624assert_eq!(blob_vec.len(), 3);625assert_eq!(blob_vec.capacity, 4);626627let last_index = blob_vec.len() - 1;628let value = swap_remove::<Foo>(&mut blob_vec, last_index);629assert_eq!(foo3, value);630631assert_eq!(blob_vec.len(), 2);632assert_eq!(blob_vec.capacity, 4);633634let value = swap_remove::<Foo>(&mut blob_vec, 0);635assert_eq!(foo1, value);636assert_eq!(blob_vec.len(), 1);637assert_eq!(blob_vec.capacity, 4);638639foo2.a = 8;640assert_eq!(get_mut::<Foo>(&mut blob_vec, 0), &foo2);641}642}643644assert_eq!(*drop_counter.borrow(), 6);645}646647#[test]648fn blob_vec_drop_empty_capacity() {649let item_layout = Layout::new::<Foo>();650let drop = drop_ptr::<Foo>;651// SAFETY: drop is able to drop a value of its `item_layout`652let _ = unsafe { BlobVec::new(item_layout, Some(drop), 0) };653}654655#[test]656#[should_panic(expected = "capacity overflow")]657fn blob_vec_zst_size_overflow() {658// SAFETY: no drop is correct drop for `()`.659let mut blob_vec = unsafe { BlobVec::new(Layout::new::<()>(), None, 0) };660661assert_eq!(usize::MAX, blob_vec.capacity, "Self-check");662663// SAFETY: Because `()` is a ZST trivial drop type, and because `BlobVec` capacity664// is always `usize::MAX` for ZSTs, we can arbitrarily set the length665// and still be sound.666blob_vec.len = usize::MAX;667668// SAFETY: `BlobVec` was initialized for `()`, so it is safe to push `()` to it.669unsafe {670OwningPtr::make((), |ptr| {671// This should panic because len is usize::MAX, remaining capacity is 0.672blob_vec.push(ptr);673});674}675}676677#[test]678#[should_panic(expected = "capacity overflow")]679fn blob_vec_capacity_overflow() {680// SAFETY: no drop is correct drop for `u32`.681let mut blob_vec = unsafe { BlobVec::new(Layout::new::<u32>(), None, 0) };682683assert_eq!(0, blob_vec.capacity, "Self-check");684685OwningPtr::make(17u32, |ptr| {686// SAFETY: we push the value of correct type.687unsafe {688blob_vec.push(ptr);689}690});691692blob_vec.reserve_exact(usize::MAX);693}694695#[test]696fn aligned_zst() {697// NOTE: This test is explicitly for uncovering potential UB with miri.698699#[derive(Component)]700#[repr(align(32))]701struct Zst;702703let mut world = World::default();704world.spawn(Zst);705world.spawn(Zst);706world.spawn(Zst);707world.spawn_empty();708709let mut count = 0;710711let mut q = world.query::<&Zst>();712for zst in q.iter(&world) {713// Ensure that the references returned are properly aligned.714assert_eq!(715core::ptr::from_ref::<Zst>(zst) as usize % align_of::<Zst>(),7160717);718count += 1;719}720721assert_eq!(count, 3);722}723}724725726