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}