bump_into/
lib.rs

1/*!
2
3A `no_std` bump allocator sourcing space from a user-provided mutable
4slice rather than from a global allocator, making it suitable for use
5in embedded applications and tight loops.
6
7## Drop behavior
8
9Values held in `BumpInto` allocations are never dropped. If they must
10be dropped, you can use [`core::mem::ManuallyDrop::drop`] or
11[`core::ptr::drop_in_place`] to drop them explicitly (and unsafely).
12In safe code, you can allocate an [`Option`] and drop the value inside
13by overwriting it with `None`.
14
15[`Option`]: core::option::Option
16
17## Example
18
19```rust
20use bump_into::{self, BumpInto};
21
22// allocate 64 bytes of uninitialized space on the stack
23let mut bump_into_space = bump_into::space_uninit!(64);
24let bump_into = BumpInto::from_slice(&mut bump_into_space[..]);
25
26// allocating an object produces a mutable reference with
27// a lifetime borrowed from `bump_into_space`, or gives
28// back its argument in `Err` if there isn't enough space
29let number: &mut u64 = bump_into
30    .alloc_with(|| 123)
31    .ok()
32    .expect("not enough space");
33assert_eq!(*number, 123);
34*number = 50000;
35assert_eq!(*number, 50000);
36
37// slices can be allocated as well
38let slice: &mut [u16] = bump_into
39    .alloc_n_with(5, core::iter::repeat(10))
40    .expect("not enough space");
41assert_eq!(slice, &[10; 5]);
42slice[2] = 100;
43assert_eq!(slice, &[10, 10, 100, 10, 10]);
44```
45
46*/
47
48#![no_std]
49
50pub mod layout_of;
51
52use core::alloc::Layout;
53use core::cell::UnsafeCell;
54use core::fmt;
55use core::mem::{self, MaybeUninit};
56use core::ptr::{self, NonNull};
57
58/// A bump allocator sourcing space from an `&mut [MaybeUninit]`.
59///
60/// Allocation methods produce mutable references with lifetimes
61/// tied to the lifetime of the backing slice, meaning the
62/// `BumpInto` itself can be freely moved around, including
63/// between threads.
64///
65/// Values held in `BumpInto` allocations are never dropped.
66/// If they must be dropped, you can use
67/// [`core::mem::ManuallyDrop::drop`] or [`core::ptr::drop_in_place`]
68/// to drop them explicitly (and unsafely). In safe code, you can
69/// allocate an [`Option`] and drop the value inside by overwriting
70/// it with `None`.
71///
72/// [`Option`]: core::option::Option
73pub struct BumpInto<'a> {
74    array: UnsafeCell<&'a mut [MaybeUninit<u8>]>,
75}
76
77// functions for complying with the (as of writing) experimental
78// "strict provenance" rules. the implementations are taken from
79// the `sptr` crate by Aria Beingessner and Ralf Jung.
80mod sptr {
81    use core::mem::transmute;
82
83    // get the address of a pointer, discarding provenance info.
84    #[inline]
85    pub(crate) fn addr_of<T>(source: *const T) -> usize {
86        unsafe { transmute::<*const T, usize>(source) }
87    }
88
89    // create a pointer that must not be dereferenced, with the
90    // given address.
91    #[inline]
92    pub(crate) fn invalid_with_addr<T>(addr: usize) -> *mut T {
93        unsafe { transmute::<usize, *mut T>(addr) }
94    }
95
96    // create a pointer with the provenance of `source` and the
97    // address `addr`.
98    #[inline]
99    pub(crate) unsafe fn with_addr<T>(source: *mut T, addr: usize) -> *mut T {
100        let source_addr = transmute::<*mut T, isize>(source);
101        let dest_addr = addr as isize;
102
103        let offset = dest_addr.wrapping_sub(source_addr);
104
105        // the `sptr` crate does this part differently in order
106        // to support rustc versions that this crate doesn't
107        // support anyway.
108        // the canonical way of performing this operation (at
109        // the time of writing, since this is all still
110        // experimental) is to use `wrapping_offset`; we use
111        // plain `offset`, which is stricter, because all our
112        // uses of `with_addr` remain in the bounds of the
113        // respective allocations, as required by `offset`.
114        // slices like our `array` field also cannot be more
115        // than `isize::MAX` bytes long, which is critical to
116        // the safety of using `offset` here, since it can't
117        // handle offsets larger than that.
118        source.cast::<u8>().offset(offset).cast::<T>()
119    }
120}
121
122/// This macro is used internally to implement
123/// `alloc_copy_concat_slices` and `alloc_copy_concat_strs`.
124macro_rules! alloc_copy_concat_impl {
125    ($self:ident, $xs_s:ident, [$t:ty] $(=> $from_slice:path)?) => {{
126        let total_len = match $xs_s
127            .iter()
128            .try_fold(0usize, |acc, xs| acc.checked_add(xs.len()))
129        {
130            Some(total_len) => total_len,
131            None => return None,
132        };
133
134        if mem::size_of::<$t>() == 0 {
135            unsafe {
136                return Some($($from_slice)?(core::slice::from_raw_parts_mut(
137                    NonNull::dangling().as_ptr(),
138                    total_len,
139                )));
140            }
141        }
142
143        let pointer = $self.alloc_space_for_n::<$t>(total_len);
144
145        if pointer.is_null() {
146            return None;
147        }
148
149        unsafe {
150            let mut dest_pointer = pointer;
151
152            for &xs in $xs_s {
153                ptr::copy_nonoverlapping(xs.as_ptr(), dest_pointer, xs.len());
154
155                dest_pointer = dest_pointer.add(xs.len());
156            }
157
158            Some($($from_slice)?(core::slice::from_raw_parts_mut(pointer, total_len)))
159        }
160    }};
161}
162
163impl<'a> BumpInto<'a> {
164    /// Creates a new `BumpInto`, wrapping a slice of `MaybeUninit<S>`.
165    #[inline]
166    pub fn from_slice<S>(array: &'a mut [MaybeUninit<S>]) -> Self {
167        let size = mem::size_of_val(array);
168        let ptr = array.as_mut_ptr().cast();
169        let array = unsafe { core::slice::from_raw_parts_mut(ptr, size) };
170
171        BumpInto {
172            array: UnsafeCell::new(array),
173        }
174    }
175
176    /// Returns the number of bytes remaining in the allocator's space.
177    #[inline]
178    pub fn available_bytes(&self) -> usize {
179        unsafe { (*self.array.get()).len() }
180    }
181
182    /// Returns the number of spaces of the given layout that
183    /// could be allocated in a contiguous region within the
184    /// allocator's remaining space.
185    ///
186    /// Returns `usize::MAX` if the layout is zero-sized.
187    ///
188    /// # Notes
189    ///
190    /// This function is safe, but it will return an unspecified
191    /// value if `layout` has a size that isn't a multiple of its
192    /// alignment. All types in Rust have sizes that are multiples
193    /// of their alignments, so this function will behave as
194    /// expected when given `Layout`s corresponding to actual
195    /// types, such as those created with [`Layout::new`] or
196    /// [`Layout::array`]. You can use [`Layout::pad_to_align`] to
197    /// make sure a `Layout` meets the requirement.
198    pub fn available_spaces<L: Into<Option<Layout>>>(&self, layout: L) -> usize {
199        let layout = match layout.into() {
200            Some(layout) => layout,
201            None => return 0,
202        };
203
204        let size = layout.size();
205        let align = layout.align();
206
207        if size == 0 {
208            return usize::MAX;
209        }
210
211        let array = unsafe { &*self.array.get() };
212
213        let array_start = sptr::addr_of(array.as_ptr());
214        let current_end = array_start + array.len();
215        let aligned_end = current_end & !(align - 1);
216
217        if aligned_end <= array_start {
218            return 0;
219        }
220
221        let usable_bytes = aligned_end - array_start;
222        usable_bytes / size
223    }
224
225    /// Returns the number of `T` that could be allocated in a
226    /// contiguous region within the allocator's remaining space.
227    ///
228    /// Returns `usize::MAX` if `T` is a zero-sized type.
229    #[inline]
230    pub fn available_spaces_for<T>(&self) -> usize {
231        self.available_spaces(layout_of::Single::<T>::new())
232    }
233
234    /// Tries to allocate a space of the given layout.
235    /// Returns a null pointer on failure.
236    pub fn alloc_space<L: Into<Option<Layout>>>(&self, layout: L) -> *mut MaybeUninit<u8> {
237        let layout = match layout.into() {
238            Some(layout) => layout,
239            None => return ptr::null_mut(),
240        };
241
242        let size = layout.size();
243        let align = layout.align();
244
245        if size == 0 {
246            // optimization for zero-sized types, as pointers to
247            // such types don't have to be distinct in Rust
248            return sptr::invalid_with_addr(align);
249        }
250
251        let array = unsafe { &mut *self.array.get() };
252
253        // since we have to do math to align our output properly,
254        // we use `usize` instead of pointers
255        let array_start = sptr::addr_of(array.as_ptr());
256        let current_end = array_start + array.len();
257
258        // the highest address with enough space to store `size` bytes
259        let preferred_ptr = match current_end.checked_sub(size) {
260            Some(preferred_ptr) => preferred_ptr,
261            None => return ptr::null_mut(),
262        };
263
264        // round down to the nearest multiple of `align`
265        let aligned_ptr = preferred_ptr & !(align - 1);
266
267        if aligned_ptr < array_start {
268            // not enough space -- do nothing and return null
269            ptr::null_mut()
270        } else {
271            // bump the bump pointer and return the allocation
272
273            // by splitting into two slices, we can create two
274            // pointers with appropriate provenances.
275            let (remaining_space, allocated_space) = array.split_at_mut(aligned_ptr - array_start);
276
277            *array = remaining_space;
278
279            unsafe { sptr::with_addr(allocated_space.as_mut_ptr(), aligned_ptr) }
280        }
281    }
282
283    /// Tries to allocate enough space to store a `T`.
284    /// Returns a properly aligned pointer to uninitialized `T` if
285    /// there was enough space; otherwise returns a null pointer.
286    #[inline]
287    pub fn alloc_space_for<T>(&self) -> *mut T {
288        self.alloc_space(layout_of::Single::<T>::new()).cast()
289    }
290
291    /// Tries to allocate enough space to store `count` number of `T`.
292    /// Returns a properly aligned pointer to uninitialized `T` if
293    /// there was enough space; otherwise returns a null pointer.
294    pub fn alloc_space_for_n<T>(&self, count: usize) -> *mut T {
295        if mem::size_of::<T>() == 0 {
296            return NonNull::dangling().as_ptr();
297        }
298
299        self.alloc_space(layout_of::Array::<T>::from_len(count))
300            .cast()
301    }
302
303    /// Allocates space for as many aligned `T` as will fit in the
304    /// free space of this `BumpInto`. Returns a tuple holding a
305    /// pointer to the lowest `T`-space that was just allocated and
306    /// the count of `T` that will fit (which may be zero).
307    ///
308    /// This method will produce a count of `usize::MAX` if `T` is
309    /// a zero-sized type.
310    pub fn alloc_space_to_limit_for<T>(&self) -> (NonNull<T>, usize) {
311        if mem::size_of::<T>() == 0 {
312            return (NonNull::dangling(), usize::MAX);
313        }
314
315        let count = self.available_spaces(layout_of::Single::<T>::new());
316
317        let pointer = self
318            .alloc_space(layout_of::Array::<T>::from_len(count))
319            .cast();
320
321        unsafe { (NonNull::new_unchecked(pointer), count) }
322    }
323
324    /// Tries to allocate enough space to store a `T` and place `x` there.
325    ///
326    /// On success (i.e. if there was enough space) produces a mutable
327    /// reference to `x` with the lifetime of this `BumpInto`'s backing
328    /// slice (`'a`).
329    ///
330    /// On failure, produces `x`.
331    #[inline]
332    pub fn alloc<T>(&self, x: T) -> Result<&'a mut T, T> {
333        let pointer = self.alloc_space_for::<T>();
334
335        if pointer.is_null() {
336            return Err(x);
337        }
338
339        unsafe {
340            ptr::write(pointer, x);
341
342            Ok(&mut *pointer)
343        }
344    }
345
346    /// Tries to allocate enough space to store a `T` and place the result
347    /// of calling `f` there.
348    ///
349    /// On success (i.e. if there was enough space) produces a mutable
350    /// reference to the stored result with the lifetime of this
351    /// `BumpInto`'s backing slice (`'a`).
352    ///
353    /// On failure, produces `f`.
354    ///
355    /// Allocating within `f` is allowed; `f` is only called after the
356    /// initial allocation succeeds.
357    pub fn alloc_with<T, F: FnOnce() -> T>(&self, f: F) -> Result<&'a mut T, F> {
358        #[inline(always)]
359        unsafe fn eval_and_write<T, F: FnOnce() -> T>(pointer: *mut T, f: F) {
360            // this is an optimization borrowed from bumpalo by fitzgen
361            // it's meant to help the compiler realize it can avoid a copy by
362            // evaluating `f` directly into the allocated space
363            ptr::write(pointer, f());
364        }
365
366        let pointer = self.alloc_space_for::<T>();
367
368        if pointer.is_null() {
369            return Err(f);
370        }
371
372        unsafe {
373            eval_and_write(pointer, f);
374
375            Ok(&mut *pointer)
376        }
377    }
378
379    /// Tries to allocate enough space to store a `T` and copy the value
380    /// pointed to by `x` into it.
381    ///
382    /// On success (i.e. if there was enough space) produces a mutable
383    /// reference to the copy with the lifetime of this `BumpInto`'s
384    /// backing slice (`'a`).
385    #[inline]
386    pub fn alloc_copy<T: Copy>(&self, x: &T) -> Option<&'a mut T> {
387        let pointer = self.alloc_space_for::<T>();
388
389        if pointer.is_null() {
390            return None;
391        }
392
393        unsafe {
394            ptr::copy_nonoverlapping(x, pointer, 1);
395
396            Some(&mut *pointer)
397        }
398    }
399
400    /// Tries to allocate enough space to store a copy of `xs` and copy
401    /// `xs` into it.
402    ///
403    /// On success (i.e. if there was enough space) produces a mutable
404    /// reference to the copy with the lifetime of this `BumpInto`'s
405    /// backing slice (`'a`).
406    #[inline]
407    pub fn alloc_copy_slice<T: Copy>(&self, xs: &[T]) -> Option<&'a mut [T]> {
408        if mem::size_of::<T>() == 0 {
409            unsafe {
410                return Some(core::slice::from_raw_parts_mut(
411                    NonNull::dangling().as_ptr(),
412                    xs.len(),
413                ));
414            }
415        }
416
417        let pointer = self
418            .alloc_space(layout_of::Array::<T>::from_len(xs.len()))
419            .cast::<T>();
420
421        if pointer.is_null() {
422            return None;
423        }
424
425        unsafe {
426            ptr::copy_nonoverlapping(xs.as_ptr(), pointer, xs.len());
427
428            Some(core::slice::from_raw_parts_mut(pointer, xs.len()))
429        }
430    }
431
432    /// Tries to allocate enough space to store the concatenation of
433    /// the slices in `xs_s` and build said concatenation by copying
434    /// the contents of each slice in order.
435    ///
436    /// On success (i.e. if there was enough space) produces a mutable
437    /// reference to the copy with the lifetime of this `BumpInto`'s
438    /// backing slice (`'a`).
439    ///
440    /// ## Example
441    ///
442    /// ```rust
443    /// use bump_into::{self, BumpInto};
444    ///
445    /// let mut space = bump_into::space_uninit!(16);
446    /// let bump_into = BumpInto::from_slice(&mut space[..]);
447    /// let bytestring = b"Hello, World!";
448    ///
449    /// let null_terminated_bytestring = bump_into
450    ///     .alloc_copy_concat_slices(&[bytestring, &[0]])
451    ///     .unwrap();
452    ///
453    /// assert_eq!(null_terminated_bytestring, b"Hello, World!\0");
454    /// ```
455    #[inline]
456    pub fn alloc_copy_concat_slices<T: Copy>(&self, xs_s: &[&[T]]) -> Option<&'a mut [T]> {
457        alloc_copy_concat_impl!(self, xs_s, [T])
458    }
459
460    /// Tries to allocate enough space to store the concatenation of
461    /// the `str`s in `strs` and build said concatenation by copying
462    /// the contents of each `str` in order.
463    ///
464    /// On success (i.e. if there was enough space) produces a mutable
465    /// reference to the copy with the lifetime of this `BumpInto`'s
466    /// backing slice (`'a`).
467    ///
468    /// ## Example
469    ///
470    /// ```rust
471    /// use bump_into::{self, BumpInto};
472    ///
473    /// let mut space = bump_into::space_uninit!(16);
474    /// let bump_into = BumpInto::from_slice(&mut space[..]);
475    /// let string = "Hello, World!";
476    ///
477    /// let null_terminated_string = bump_into
478    ///     .alloc_copy_concat_strs(&[string, "\0"])
479    ///     .unwrap();
480    ///
481    /// assert_eq!(null_terminated_string, "Hello, World!\0");
482    /// ```
483    #[inline]
484    pub fn alloc_copy_concat_strs(&self, strs: &[&str]) -> Option<&'a mut str> {
485        alloc_copy_concat_impl!(self, strs, [u8] => core::str::from_utf8_unchecked_mut)
486    }
487
488    /// Tries to allocate enough space to store `count` number of `T` and
489    /// fill it with the values produced by `iter.into_iter()`.
490    ///
491    /// On success (i.e. if there was enough space) produces a mutable
492    /// reference to the stored results as a slice, with the lifetime of
493    /// this `BumpInto`'s backing slice (`'a`).
494    ///
495    /// On failure, produces `iter`.
496    ///
497    /// If the iterator ends before producing enough items to fill the
498    /// allocated space, the same amount of space is allocated, but the
499    /// returned slice is just long enough to hold the items that were
500    /// actually produced.
501    ///
502    /// Allocating within the iterator's `next` method is allowed;
503    /// iteration only begins after the initial allocation succeeds.
504    #[inline]
505    pub fn alloc_n_with<T, I: IntoIterator<Item = T>>(
506        &self,
507        count: usize,
508        iter: I,
509    ) -> Result<&'a mut [T], I> {
510        let pointer = self.alloc_space_for_n::<T>(count);
511
512        if pointer.is_null() {
513            return Err(iter);
514        }
515
516        let iter = iter.into_iter();
517
518        unsafe { Ok(Self::alloc_n_with_inner(pointer, count, iter)) }
519    }
520
521    // the core loop of alloc_n_with is split into its own non-inline-
522    // annotated function. since it's possible for different types to
523    // implement IntoIterator with the same IntoIter associated type,
524    // this may reduce the number of monomorphized functions in the
525    // generated code, in addition to making the compiler more likely
526    // to inline the parts of alloc_n_with that will benefit most from
527    // inlining. the split function is here instead of inside
528    // alloc_n_with because it's reused in alloc_down_with_shared for
529    // the ZST case.
530    unsafe fn alloc_n_with_inner<'b, T, I: Iterator<Item = T>>(
531        pointer: *mut T,
532        count: usize,
533        mut iter: I,
534    ) -> &'b mut [T] {
535        #[inline(always)]
536        unsafe fn iter_and_write<T, I: Iterator<Item = T>>(pointer: *mut T, mut iter: I) -> bool {
537            match iter.next() {
538                Some(item) => {
539                    ptr::write(pointer, item);
540
541                    false
542                }
543                None => true,
544            }
545        }
546
547        for index in 0..count {
548            if iter_and_write(pointer.add(index), &mut iter) {
549                // iterator ended before we could fill the whole space.
550                return core::slice::from_raw_parts_mut(pointer, index);
551            }
552        }
553
554        core::slice::from_raw_parts_mut(pointer, count)
555    }
556
557    /// Allocates enough space to store as many of the values produced
558    /// by `iter.into_iter()` as possible. Produces a mutable reference
559    /// to the stored results as a slice, in the opposite order to the
560    /// order they were produced in, with the lifetime of this
561    /// `BumpInto`'s backing slice (`'a`).
562    ///
563    /// This method will create a slice of up to `usize::MAX` elements
564    /// if `T` is a zero-sized type. This means it technically will not
565    /// try to exhaust an infinite iterator, but it may still take much
566    /// longer than expected in generic code if you don't check whether
567    /// `T` is zero-sized!
568    ///
569    /// If you have only a shared reference, and especially if you must
570    /// call *non-allocating* methods of `self` within the iterator,
571    /// you can use the unsafe [`alloc_down_with_shared`].
572    ///
573    /// [`alloc_down_with_shared`]: Self::alloc_down_with_shared
574    #[inline]
575    pub fn alloc_down_with<T, I: IntoIterator<Item = T>>(&mut self, iter: I) -> &'a mut [T] {
576        unsafe { self.alloc_down_with_shared(iter) }
577    }
578
579    /// Unsafe version of [`alloc_down_with`], taking `self` as a
580    /// shared reference instead of a mutable reference.
581    ///
582    /// [`alloc_down_with`]: Self::alloc_down_with
583    ///
584    /// # Safety
585    ///
586    /// Undefined behavior may result if any methods of this `BumpInto`
587    /// are called from within the `next` method of the iterator, with
588    /// the exception of the `available_bytes`, `available_spaces`,
589    /// and `available_spaces_for` methods, which are safe.
590    #[inline]
591    pub unsafe fn alloc_down_with_shared<T, I: IntoIterator<Item = T>>(
592        &self,
593        iter: I,
594    ) -> &'a mut [T] {
595        unsafe fn alloc_down_with_shared_inner<'a, T, I: Iterator<Item = T>>(
596            bump_into: &BumpInto<'a>,
597            mut iter: I,
598            array_start: usize,
599            mut cur_space: usize,
600        ) -> &'a mut [T] {
601            let array = (*bump_into.array.get()).as_mut_ptr();
602
603            let mut count = 0;
604
605            loop {
606                let next_space = cur_space.checked_sub(mem::size_of::<T>());
607
608                let finished = match next_space {
609                    Some(next_space) if next_space >= array_start => {
610                        // our slice must be appropriately sized when calling
611                        // `iter.next()` in case it calls one of the
612                        // `available_*` methods. it may also panic; we'll be
613                        // left in a valid state if it does, but the space
614                        // allocated up to that point will be wasted.
615                        *bump_into.array.get() =
616                            core::slice::from_raw_parts_mut(array, cur_space - array_start);
617
618                        match iter.next() {
619                            Some(item) => {
620                                cur_space = next_space;
621
622                                ptr::write(sptr::with_addr(array, cur_space).cast(), item);
623
624                                false
625                            }
626                            None => true,
627                        }
628                    }
629                    _ => true,
630                };
631
632                if finished {
633                    *bump_into.array.get() =
634                        core::slice::from_raw_parts_mut(array, cur_space - array_start);
635
636                    return core::slice::from_raw_parts_mut(
637                        sptr::with_addr(array, cur_space).cast(),
638                        count,
639                    );
640                }
641
642                count += 1;
643            }
644        }
645
646        let iter = iter.into_iter();
647
648        if mem::size_of::<T>() == 0 {
649            // this is both meant as an optimization and to bypass
650            // the `aligned_end <= array_start` check, since
651            // allocating ZSTs shouldn't depend on alignment or
652            // remaining space.
653            // we also enforce here that an allocation have no more
654            // than `usize::MAX` objects, which is obviously implicit
655            // on the positive-size path.
656            return Self::alloc_n_with_inner(NonNull::dangling().as_ptr(), usize::MAX, iter);
657        }
658
659        let (array_start, current_end) = {
660            let array = &*self.array.get();
661
662            // since we have to do math to align our output properly,
663            // we use `usize` instead of pointers
664            let array_start = sptr::addr_of(array.as_ptr());
665            let current_end = array_start + array.len();
666
667            (array_start, current_end)
668        };
669
670        let aligned_end = current_end & !(mem::align_of::<T>() - 1);
671
672        if aligned_end <= array_start {
673            return &mut [];
674        }
675
676        alloc_down_with_shared_inner(self, iter, array_start, aligned_end)
677    }
678}
679
680impl<'a> fmt::Debug for BumpInto<'a> {
681    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
682        write!(f, "BumpInto {{ {} bytes free }}", self.available_bytes())
683    }
684}
685
686/// Creates an uninitialized array of `MaybeUninit` without allocating
687/// on the heap, suitable for taking a slice of to pass into
688/// `BumpInto::from_slice`.
689///
690/// # Example
691///
692/// ```rust
693/// use bump_into::space_uninit;
694/// use core::mem;
695///
696/// // an array of MaybeUninit<u8> is created by default:
697/// let mut space = space_uninit!(64);
698/// assert_eq!(mem::size_of_val(&space), 64);
699/// assert_eq!(mem::align_of_val(&space), 1);
700/// // if you need your space to have the alignment of a
701/// // particular type, you can use an array-like syntax.
702/// // this line will create an array of MaybeUninit<u32>:
703/// let mut space = space_uninit!(u32; 16);
704/// assert_eq!(mem::size_of_val(&space), 64);
705/// assert_eq!(mem::align_of_val(&space), 4);
706/// ```
707#[macro_export]
708macro_rules! space_uninit {
709    ($capacity:expr) => {{
710        extern crate core;
711
712        let outer_maybe_uninit = core::mem::MaybeUninit::<
713            [core::mem::MaybeUninit<core::primitive::u8>; $capacity],
714        >::uninit();
715
716        unsafe { outer_maybe_uninit.assume_init() }
717    }};
718
719    ($like_ty:ty; $capacity:expr) => {{
720        extern crate core;
721
722        let outer_maybe_uninit =
723            core::mem::MaybeUninit::<[core::mem::MaybeUninit<$like_ty>; $capacity]>::uninit();
724
725        unsafe { outer_maybe_uninit.assume_init() }
726    }};
727}
728
729/// Creates a zeroed array of `MaybeUninit` without allocating on the
730/// heap, suitable for taking a slice of to pass into
731/// `BumpInto::from_slice`.
732///
733/// # Example
734///
735/// ```rust
736/// use bump_into::space_zeroed;
737/// use core::mem;
738///
739/// // an array of MaybeUninit<u8> is created by default:
740/// let mut space = space_zeroed!(64);
741/// assert_eq!(mem::size_of_val(&space), 64);
742/// assert_eq!(mem::align_of_val(&space), 1);
743/// // if you need your space to have the alignment of a
744/// // particular type, you can use an array-like syntax.
745/// // this line will create an array of MaybeUninit<u32>:
746/// let mut space = space_zeroed!(u32; 16);
747/// assert_eq!(mem::size_of_val(&space), 64);
748/// assert_eq!(mem::align_of_val(&space), 4);
749/// ```
750#[macro_export]
751macro_rules! space_zeroed {
752    ($capacity:expr) => {{
753        extern crate core;
754
755        let outer_maybe_uninit = core::mem::MaybeUninit::<
756            [core::mem::MaybeUninit<core::primitive::u8>; $capacity],
757        >::zeroed();
758
759        unsafe { outer_maybe_uninit.assume_init() }
760    }};
761
762    ($like_ty:ty; $capacity:expr) => {{
763        extern crate core;
764
765        let outer_maybe_uninit =
766            core::mem::MaybeUninit::<[core::mem::MaybeUninit<$like_ty>; $capacity]>::zeroed();
767
768        unsafe { outer_maybe_uninit.assume_init() }
769    }};
770}
771
772/// Creates an uninitialized array of one `MaybeUninit` without
773/// allocating on the heap, with the given size and alignment,
774/// suitable for taking a slice of to pass into `BumpInto::from_slice`.
775///
776/// The size will be rounded up to the nearest multiple of the
777/// given alignment.
778///
779/// The size must be a const expression of type `usize`.
780/// The alignment must be a power-of-two integer literal.
781///
782/// # Example
783///
784/// ```rust
785/// use bump_into::space_uninit_aligned;
786/// use core::mem;
787///
788/// let mut space = space_uninit_aligned!(size: 64, align: 4);
789/// assert_eq!(mem::size_of_val(&space), 64);
790/// assert_eq!(mem::align_of_val(&space), 4);
791/// ```
792#[macro_export]
793macro_rules! space_uninit_aligned {
794    (size: $size:expr, align: $align:literal $(,)?) => {{
795        extern crate core;
796
797        #[repr(C, align($align))]
798        struct __BUMP_INTO_Space {
799            _contents: [core::primitive::u8; $size],
800        }
801
802        unsafe {
803            core::mem::MaybeUninit::<[core::mem::MaybeUninit<__BUMP_INTO_Space>; 1]>::uninit()
804                .assume_init()
805        }
806    }};
807}
808
809/// Creates a zeroed array of one `MaybeUninit` without allocating on
810/// the heap, with the given size and alignment, suitable for taking a
811/// slice of to pass into `BumpInto::from_slice`.
812///
813/// The size will be rounded up to the nearest multiple of the
814/// given alignment.
815///
816/// The size must be a const expression of type `usize`.
817/// The alignment must be a power-of-two integer literal.
818///
819/// # Example
820///
821/// ```rust
822/// use bump_into::space_zeroed_aligned;
823/// use core::mem;
824///
825/// let mut space = space_zeroed_aligned!(size: 64, align: 4);
826/// assert_eq!(mem::size_of_val(&space), 64);
827/// assert_eq!(mem::align_of_val(&space), 4);
828/// ```
829#[macro_export]
830macro_rules! space_zeroed_aligned {
831    (size: $size:expr, align: $align:literal $(,)?) => {{
832        extern crate core;
833
834        #[repr(C, align($align))]
835        struct __BUMP_INTO_Space {
836            _contents: [core::primitive::u8; $size],
837        }
838
839        unsafe {
840            core::mem::MaybeUninit::<[core::mem::MaybeUninit<__BUMP_INTO_Space>; 1]>::zeroed()
841                .assume_init()
842        }
843    }};
844}
845
846#[cfg(test)]
847mod tests {
848    use crate::*;
849
850    use core::alloc::Layout;
851
852    #[test]
853    fn alloc() {
854        let mut space = space_uninit!(128);
855        let bump_into = BumpInto::from_slice(&mut space[..]);
856
857        let something1 = bump_into.alloc(123u64).expect("allocation 1 failed");
858
859        assert_eq!(*something1, 123u64);
860
861        let something2 = bump_into.alloc(7775u16).expect("allocation 2 failed");
862
863        assert_eq!(*something1, 123u64);
864        assert_eq!(*something2, 7775u16);
865
866        let something3 = bump_into
867            .alloc_with(|| 251222u64)
868            .ok()
869            .expect("allocation 3 failed");
870
871        assert_eq!(*something1, 123u64);
872        assert_eq!(*something2, 7775u16);
873        assert_eq!(*something3, 251222u64);
874
875        let something4 = bump_into
876            .alloc_copy(&289303u32)
877            .expect("allocation 4 failed");
878
879        assert_eq!(*something1, 123u64);
880        assert_eq!(*something2, 7775u16);
881        assert_eq!(*something3, 251222u64);
882        assert_eq!(*something4, 289303u32);
883
884        if bump_into.alloc_with(|| [0u32; 128]).is_ok() {
885            panic!("allocation 5 succeeded");
886        }
887
888        let something6 = bump_into.alloc(123523u32).expect("allocation 6 failed");
889
890        assert_eq!(*something1, 123u64);
891        assert_eq!(*something2, 7775u16);
892        assert_eq!(*something3, 251222u64);
893        assert_eq!(*something4, 289303u32);
894        assert_eq!(*something6, 123523u32);
895
896        let something7 = bump_into.alloc_space(Layout::from_size_align(17, 8).unwrap());
897
898        assert!(!something7.is_null());
899        assert_eq!(sptr::addr_of(something7) & 7, 0);
900        assert!(sptr::addr_of(something7).checked_add(17).unwrap() <= sptr::addr_of(something6));
901
902        // for miri
903        unsafe {
904            ptr::write(something7.cast::<i64>(), -1);
905        }
906
907        assert_eq!(*something1, 123u64);
908        assert_eq!(*something2, 7775u16);
909        assert_eq!(*something3, 251222u64);
910        assert_eq!(*something4, 289303u32);
911        assert_eq!(*something6, 123523u32);
912    }
913
914    #[test]
915    fn alloc_n() {
916        let mut space = space_uninit!(1024);
917        let bump_into = BumpInto::from_slice(&mut space[..]);
918
919        let something1 = bump_into
920            .alloc_copy_slice(&[1u32, 258909, 1000])
921            .expect("allocation 1 failed");
922
923        assert_eq!(something1, &[1u32, 258909, 1000]);
924
925        let something2 = bump_into
926            .alloc_copy_slice(&[1u64, 258909, 1000, 0])
927            .expect("allocation 2 failed");
928
929        assert_eq!(something1, &[1u32, 258909, 1000]);
930        assert_eq!(something2, &[1u64, 258909, 1000, 0]);
931
932        let something3 = bump_into
933            .alloc_n_with(5, core::iter::repeat(61921u16))
934            .expect("allocation 3 failed");
935
936        assert_eq!(something1, &[1u32, 258909, 1000]);
937        assert_eq!(something2, &[1u64, 258909, 1000, 0]);
938        assert_eq!(something3, &[61921u16; 5]);
939
940        let something4 = bump_into
941            .alloc_n_with(6, core::iter::once(71u64))
942            .expect("allocation 4 failed");
943
944        assert_eq!(something1, &[1u32, 258909, 1000]);
945        assert_eq!(something2, &[1u64, 258909, 1000, 0]);
946        assert_eq!(something3, &[61921u16; 5]);
947        assert_eq!(something4, &[71u64]);
948
949        if bump_into.alloc_n_with::<u64, _>(1000, None).is_ok() {
950            panic!("allocation 5 succeeded");
951        }
952
953        let something6 = bump_into
954            .alloc_n_with::<u64, _>(6, None)
955            .expect("allocation 6 failed");
956
957        assert_eq!(something1, &[1u32, 258909, 1000]);
958        assert_eq!(something2, &[1u64, 258909, 1000, 0]);
959        assert_eq!(something3, &[61921u16; 5]);
960        assert_eq!(something4, &[71u64]);
961        assert_eq!(something6, &[]);
962
963        let something7 = bump_into
964            .alloc_copy_concat_slices(&[&[1u32, 258909, 1000]])
965            .expect("allocation 7 failed");
966
967        assert_eq!(something7, &[1u32, 258909, 1000]);
968
969        let something8 = bump_into
970            .alloc_copy_concat_slices(&[&[1u32, 258909, 1000], &[9999], &[]])
971            .expect("allocation 8 failed");
972
973        assert_eq!(something8, &[1u32, 258909, 1000, 9999]);
974
975        let something9 = bump_into
976            .alloc_copy_concat_slices(&[something7, something7, &[1, 2, 3], something7])
977            .expect("allocation 9 failed");
978
979        assert_eq!(
980            something9,
981            &[1u32, 258909, 1000, 1u32, 258909, 1000, 1, 2, 3, 1u32, 258909, 1000],
982        );
983
984        // check that it fails on an array whose number of
985        // bytes would slightly overflow a usize.
986        if !bump_into
987            .alloc_space_for_n::<u32>(usize::MAX / 4 + 2)
988            .is_null()
989        {
990            panic!("allocation 10 succeeded");
991        }
992
993        // the behavior of `alloc_copy_concat_strs` should
994        // have nothing to do with the content of the strs,
995        // but let's test it with different character sets
996        // and a little bit of weird whitespace. because
997        // tests are for the future, and who can say what
998        // the future holds?
999
1000        let something11 = bump_into
1001            .alloc_copy_concat_strs(&["สวัสดี"])
1002            .expect("allocation 11 failed");
1003
1004        assert_eq!(something11, "สวัสดี");
1005
1006        let something12 = bump_into
1007            .alloc_copy_concat_strs(&["你好", "\\", ""])
1008            .expect("allocation 12 failed");
1009
1010        assert_eq!(something12, "你好\\");
1011
1012        let something13 = bump_into
1013            .alloc_copy_concat_strs(&[
1014                something11,
1015                something11,
1016                "  \n (this parenthetical—is in English!)\r\n",
1017                something11,
1018            ])
1019            .expect("allocation 13 failed");
1020
1021        assert_eq!(
1022            something13,
1023            "สวัสดีสวัสดี  \n (this parenthetical—is in English!)\r\nสวัสดี",
1024        );
1025    }
1026
1027    #[cfg(target_pointer_width = "32")]
1028    #[test]
1029    fn alloc_copy_concat_strs_overflow() {
1030        const THIRTY_TWO_MIBS_OF_ZEROES: &'static [u8] = &[0; 1 << 25];
1031
1032        let mut space = space_uninit!(1024);
1033        let bump_into = BumpInto::from_slice(&mut space[..]);
1034
1035        // SAFETY:
1036        // 0 is a valid US-ASCII character, so a string
1037        // of all 0 bytes is valid US-ASCII and therefore
1038        // valid UTF-8 as well.
1039        let thirty_two_mib_string =
1040            unsafe { core::str::from_utf8_unchecked(THIRTY_TWO_MIBS_OF_ZEROES) };
1041
1042        if bump_into
1043            .alloc_copy_concat_strs(&[thirty_two_mib_string; 128])
1044            .is_some()
1045        {
1046            panic!("allocation succeeded");
1047        }
1048    }
1049
1050    #[test]
1051    fn available_bytes() {
1052        const LAYOUT_U8: Layout = Layout::new::<u8>();
1053
1054        let mut space = space_uninit!(32);
1055
1056        {
1057            let mut bump_into = BumpInto::from_slice(&mut space[..]);
1058
1059            // check that we get zero for an array whose number of
1060            // bytes would slightly overflow a usize.
1061            assert_eq!(
1062                bump_into.available_spaces(layout_of::Array::<u32>::from_len(usize::MAX / 4 + 2)),
1063                0
1064            );
1065
1066            assert_eq!(bump_into.available_bytes(), 32);
1067            assert_eq!(bump_into.available_spaces(LAYOUT_U8), 32);
1068            assert_eq!(bump_into.available_spaces_for::<u8>(), 32);
1069
1070            bump_into.alloc(0u8).expect("allocation 1 failed");
1071
1072            assert_eq!(bump_into.available_bytes(), 31);
1073            assert_eq!(bump_into.available_spaces(LAYOUT_U8), 31);
1074            assert_eq!(bump_into.available_spaces_for::<u8>(), 31);
1075
1076            let spaces_for_u32 = bump_into.available_spaces_for::<u32>();
1077
1078            bump_into.alloc(0u32).expect("allocation 2 failed");
1079
1080            assert_eq!(bump_into.available_spaces_for::<u32>(), spaces_for_u32 - 1);
1081
1082            bump_into.alloc(0u8).expect("allocation 3 failed");
1083
1084            assert_eq!(bump_into.available_spaces_for::<u32>(), spaces_for_u32 - 2);
1085
1086            {
1087                let rest = bump_into.alloc_down_with(core::iter::repeat(0u32));
1088
1089                assert_eq!(rest.len(), spaces_for_u32 - 2);
1090                assert!(rest.len() >= 5);
1091
1092                for &x in rest.iter() {
1093                    assert_eq!(x, 0u32);
1094                }
1095            }
1096
1097            assert_eq!(bump_into.available_spaces_for::<u32>(), 0);
1098            assert!(bump_into.available_bytes() < 4);
1099        }
1100
1101        {
1102            let bump_into = BumpInto::from_slice(&mut space[..]);
1103
1104            assert_eq!(bump_into.available_bytes(), 32);
1105            assert_eq!(bump_into.available_spaces(LAYOUT_U8), 32);
1106            assert_eq!(bump_into.available_spaces_for::<u8>(), 32);
1107
1108            let something4 = bump_into.alloc(0u8).expect("allocation 5 failed");
1109
1110            assert_eq!(*something4, 0);
1111
1112            let (pointer, count) = bump_into.alloc_space_to_limit_for::<i64>();
1113
1114            assert_eq!(bump_into.available_spaces_for::<i64>(), 0);
1115            assert!(bump_into.available_bytes() < 8);
1116            assert!(count >= 3);
1117
1118            let pointer = pointer.as_ptr();
1119
1120            for x in 0..count {
1121                unsafe {
1122                    core::ptr::write(pointer.add(x), -1);
1123                }
1124            }
1125
1126            assert_eq!(*something4, 0);
1127        }
1128
1129        {
1130            let bump_into = BumpInto::from_slice(&mut space[..]);
1131
1132            assert_eq!(bump_into.available_bytes(), 32);
1133            assert_eq!(bump_into.available_spaces(LAYOUT_U8), 32);
1134            assert_eq!(bump_into.available_spaces_for::<u8>(), 32);
1135
1136            let something6 = bump_into.alloc(0u8).expect("allocation 7 failed");
1137
1138            assert_eq!(*something6, 0);
1139
1140            let rest = unsafe {
1141                let mut count = 0u32;
1142
1143                bump_into.alloc_down_with_shared(core::iter::from_fn(|| {
1144                    if bump_into.available_spaces_for::<u32>() > 1 {
1145                        count += 1;
1146                        Some(count)
1147                    } else {
1148                        None
1149                    }
1150                }))
1151            };
1152
1153            assert_eq!(bump_into.available_spaces_for::<u32>(), 1);
1154            assert!(rest.len() >= 6);
1155
1156            for (a, b) in rest.iter().zip((0..rest.len() as u32).rev()) {
1157                assert_eq!(*a, b + 1);
1158            }
1159
1160            bump_into.alloc(0u32).expect("allocation 9 failed");
1161
1162            assert_eq!(bump_into.available_spaces_for::<u32>(), 0);
1163            assert!(bump_into.available_bytes() < 4);
1164        }
1165    }
1166
1167    #[test]
1168    fn space() {
1169        {
1170            let mut space = space_zeroed!(32);
1171            let bump_into = BumpInto::from_slice(&mut space[..]);
1172
1173            for _ in 0..32 {
1174                let something1 = bump_into.alloc_space_for::<u8>();
1175                if something1.is_null() {
1176                    panic!("allocation 1 (in loop) failed");
1177                }
1178                unsafe {
1179                    assert_eq!(*something1, 0);
1180                }
1181            }
1182        }
1183
1184        {
1185            let mut space = space_uninit!(u32; 32);
1186            let space_ptr = &space as *const _;
1187            let bump_into = BumpInto::from_slice(&mut space[..]);
1188
1189            let (something2_ptr, something2_size) = bump_into.alloc_space_to_limit_for::<u32>();
1190            let something2_ptr = something2_ptr.as_ptr() as *const u32;
1191            assert_eq!(space_ptr as *const u32, something2_ptr);
1192            assert_eq!(something2_size, 32);
1193        }
1194
1195        {
1196            let mut space = space_zeroed!(u32; 32);
1197            let space_ptr = &space as *const _;
1198            let bump_into = BumpInto::from_slice(&mut space[..]);
1199
1200            let (something3_ptr, something3_size) = bump_into.alloc_space_to_limit_for::<u32>();
1201            let something3_ptr = something3_ptr.as_ptr() as *const u32;
1202            assert_eq!(space_ptr as *const u32, something3_ptr);
1203            assert_eq!(something3_size, 32);
1204
1205            unsafe {
1206                for x in core::slice::from_raw_parts(something3_ptr, something3_size) {
1207                    assert_eq!(*x, 0);
1208                }
1209            }
1210        }
1211
1212        {
1213            let mut space = space_uninit_aligned!(size: 32 * 4, align: 4);
1214            let space_ptr = &space as *const _;
1215            let bump_into = BumpInto::from_slice(&mut space[..]);
1216
1217            let (something4_ptr, something4_size) = bump_into.alloc_space_to_limit_for::<u32>();
1218            let something4_ptr = something4_ptr.as_ptr() as *const u32;
1219            assert_eq!(space_ptr as *const u32, something4_ptr);
1220            assert_eq!(something4_size, 32);
1221        }
1222
1223        {
1224            let mut space = space_zeroed_aligned!(size: 32 * 4, align: 4);
1225            let space_ptr = &space as *const _;
1226            let bump_into = BumpInto::from_slice(&mut space[..]);
1227
1228            let (something5_ptr, something5_size) = bump_into.alloc_space_to_limit_for::<u32>();
1229            let something5_ptr = something5_ptr.as_ptr() as *const u32;
1230            assert_eq!(space_ptr as *const u32, something5_ptr);
1231            assert_eq!(something5_size, 32);
1232
1233            unsafe {
1234                for x in core::slice::from_raw_parts(something5_ptr, something5_size) {
1235                    assert_eq!(*x, 0);
1236                }
1237            }
1238        }
1239    }
1240
1241    #[test]
1242    fn single() {
1243        use core::mem::MaybeUninit;
1244
1245        let mut space = MaybeUninit::<u32>::uninit();
1246
1247        {
1248            let bump_into = BumpInto::from_slice(core::slice::from_mut(&mut space));
1249            let something1 = bump_into.alloc(0x8359u16).expect("allocation 1 failed");
1250            let something2 = bump_into.alloc(0x1312u16).expect("allocation 2 failed");
1251            assert_eq!(bump_into.available_bytes(), 0);
1252            assert_eq!(*something1, 0x8359);
1253            assert_eq!(*something2, 0x1312);
1254            *something1 = 0xACAB;
1255            assert_eq!(*something1, 0xACAB);
1256            assert_eq!(*something2, 0x1312);
1257        }
1258
1259        unsafe {
1260            #[cfg(target_endian = "little")]
1261            assert_eq!(space.assume_init(), 0xACAB1312);
1262            #[cfg(target_endian = "big")]
1263            assert_eq!(space.assume_init(), 0x1312ACAB);
1264        }
1265    }
1266
1267    #[test]
1268    fn moving() {
1269        let mut space = space_uninit!(32);
1270        let bump_into = BumpInto::from_slice(&mut space[..]);
1271
1272        let something1 = bump_into.alloc(123u64).expect("allocation 1 failed");
1273
1274        assert_eq!(*something1, 123u64);
1275
1276        core::mem::drop(bump_into);
1277
1278        assert_eq!(*something1, 123u64);
1279    }
1280
1281    #[test]
1282    fn alloc_inside_alloc_with() {
1283        let mut space = space_uninit!(u32; 8);
1284
1285        {
1286            let bump_into = BumpInto::from_slice(&mut space[..]);
1287
1288            let mut something2: Option<&mut [u32]> = None;
1289            let something1: &mut u32 = bump_into
1290                .alloc_with(|| {
1291                    let inner_something = bump_into
1292                        .alloc_n_with(bump_into.available_spaces_for::<u32>(), 0u32..)
1293                        .expect("inner allocation failed");
1294
1295                    let something1 = inner_something.iter().sum();
1296
1297                    something2 = Some(inner_something);
1298
1299                    something1
1300                })
1301                .ok()
1302                .expect("allocation 1 failed");
1303
1304            assert_eq!(*something1, (0..7).sum());
1305            assert_eq!(something2, Some(&mut [0, 1, 2, 3, 4, 5, 6][..]));
1306        }
1307
1308        {
1309            let bump_into = BumpInto::from_slice(&mut space[..]);
1310
1311            let mut something4: Option<&mut [u32]> = None;
1312            let something3: &mut [u32] = bump_into
1313                .alloc_n_with(
1314                    4,
1315                    core::iter::from_fn(|| {
1316                        let inner_something = bump_into
1317                            .alloc_n_with(bump_into.available_spaces_for::<u32>() / 2 + 1, 0u32..);
1318
1319                        inner_something.ok().map(|inner_something| {
1320                            let something3 = inner_something.iter().sum();
1321
1322                            something4 = Some(inner_something);
1323
1324                            something3
1325                        })
1326                    }),
1327                )
1328                .ok()
1329                .expect("allocation 3 failed");
1330
1331            assert_eq!(something3, &mut [(0..3).sum(), 0]);
1332            assert_eq!(something4, Some(&mut [0][..]));
1333        }
1334    }
1335
1336    #[derive(Debug)]
1337    struct ZstWithDrop;
1338
1339    impl Drop for ZstWithDrop {
1340        fn drop(&mut self) {
1341            panic!("ZstWithDrop was dropped!");
1342        }
1343    }
1344
1345    fn zero_sized(space: &mut [MaybeUninit<u8>]) {
1346        let big_number = if cfg!(miri) { 0x100 } else { 0x10000 };
1347
1348        let space_len = space.len();
1349        let bump_into = BumpInto::from_slice(space);
1350
1351        assert_eq!(bump_into.available_bytes(), space_len);
1352        assert_eq!(
1353            bump_into.available_spaces(Layout::from_size_align(0, 0x100).unwrap()),
1354            usize::MAX
1355        );
1356        assert_eq!(bump_into.available_spaces_for::<ZstWithDrop>(), usize::MAX);
1357
1358        let (nothing1_ptr, nothing1_count) = bump_into.alloc_space_to_limit_for::<ZstWithDrop>();
1359        assert!(!nothing1_ptr.as_ptr().is_null());
1360        assert_eq!(nothing1_count, usize::MAX);
1361
1362        let _nothing2 = bump_into.alloc(ZstWithDrop).expect("allocation 2 failed");
1363
1364        let _nothing3 = bump_into
1365            .alloc_with(|| ZstWithDrop)
1366            .ok()
1367            .expect("allocation 3 failed");
1368
1369        let nothing4 = bump_into
1370            .alloc_copy_slice(&[(), (), (), ()])
1371            .expect("allocation 4 failed");
1372        assert_eq!(nothing4, &[(), (), (), ()]);
1373
1374        let nothing5 = bump_into
1375            .alloc_n_with(big_number, core::iter::repeat_with(|| ZstWithDrop))
1376            .ok()
1377            .expect("allocation 5 failed");
1378        assert_eq!(nothing5.len(), big_number);
1379
1380        let nothing6 = unsafe {
1381            bump_into
1382                .alloc_down_with_shared(core::iter::repeat_with(|| ZstWithDrop).take(big_number))
1383        };
1384        assert_eq!(nothing6.len(), big_number);
1385
1386        let nothing7_array = [(); usize::MAX];
1387        let nothing7 = bump_into
1388            .alloc_copy_slice(&nothing7_array)
1389            .expect("allocation 7 failed");
1390        assert_eq!(nothing7.len(), usize::MAX);
1391
1392        let nothing8 = bump_into
1393            .alloc_copy_concat_slices(&[&[(), ()], &[(), (), ()], &[]])
1394            .expect("allocation 8 failed");
1395        assert_eq!(nothing8, &[(), (), (), (), ()]);
1396
1397        let nothing9_array = [(); usize::MAX];
1398        let nothing9 = bump_into
1399            .alloc_copy_concat_slices(&[&nothing9_array])
1400            .expect("allocation 9 failed");
1401        assert_eq!(nothing9.len(), usize::MAX);
1402
1403        let nothing10_array = [(); usize::MAX >> 4];
1404        let nothing10 = bump_into
1405            .alloc_copy_concat_slices(&[&nothing10_array[..]; 16])
1406            .expect("allocation 10 failed");
1407        assert_eq!(nothing10.len(), usize::MAX & !15);
1408
1409        let nothing11_array = [(); usize::MAX];
1410        let nothing11 = bump_into.alloc_copy_concat_slices(&[&nothing11_array, &[(); 1][..]]);
1411        if nothing11.is_some() {
1412            panic!("allocation 11 succeeded");
1413        }
1414
1415        let nothing12_array = [(); usize::MAX];
1416        let nothing12 = bump_into.alloc_copy_concat_slices(&[&nothing12_array[..]; 2]);
1417        if nothing12.is_some() {
1418            panic!("allocation 12 succeeded");
1419        }
1420    }
1421
1422    #[test]
1423    fn zero_sized_0_space() {
1424        let mut space = space_uninit!(0);
1425        zero_sized(&mut space[..]);
1426    }
1427
1428    #[test]
1429    fn zero_sized_32_space() {
1430        let mut space = space_uninit!(32);
1431        zero_sized(&mut space[..]);
1432    }
1433
1434    #[test]
1435    #[ignore = "hangs when optimizations are off"]
1436    fn zero_sized_usize_max() {
1437        let mut space = space_uninit!(0);
1438        let mut bump_into = BumpInto::from_slice(&mut space[..]);
1439
1440        let nothing1 = bump_into
1441            .alloc_n_with(usize::MAX, core::iter::repeat_with(|| ZstWithDrop))
1442            .ok()
1443            .expect("allocation 1 failed");
1444        assert_eq!(nothing1.len(), usize::MAX);
1445
1446        let nothing2 = bump_into.alloc_down_with(core::iter::repeat_with(|| ZstWithDrop));
1447        assert_eq!(nothing2.len(), usize::MAX);
1448    }
1449
1450    #[test]
1451    fn iteration_count() {
1452        let mut space = space_uninit!(u32; 32);
1453        let mut bump_into = BumpInto::from_slice(&mut space[..]);
1454
1455        let mut iteration_count = 0u32;
1456        let something1 = bump_into
1457            .alloc_n_with(
1458                16,
1459                core::iter::repeat_with(|| {
1460                    iteration_count += 1;
1461                    iteration_count
1462                }),
1463            )
1464            .ok()
1465            .expect("allocation 1 failed");
1466        assert_eq!(
1467            something1,
1468            &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
1469        );
1470        assert_eq!(iteration_count, 16);
1471
1472        let mut iteration_count = 0usize;
1473        let nothing2 = bump_into
1474            .alloc_n_with(
1475                256,
1476                core::iter::repeat_with(|| {
1477                    iteration_count += 1;
1478                    ZstWithDrop
1479                }),
1480            )
1481            .ok()
1482            .expect("allocation 2 failed");
1483        assert_eq!(nothing2.len(), 256);
1484        assert_eq!(iteration_count, 256);
1485
1486        let mut iteration_count = 0u32;
1487        let something3 = bump_into.alloc_down_with(core::iter::repeat_with(|| {
1488            iteration_count += 1;
1489            iteration_count
1490        }));
1491        assert_eq!(
1492            something3,
1493            &[16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
1494        );
1495        assert_eq!(iteration_count, 16);
1496    }
1497
1498    #[test]
1499    #[ignore = "hangs when optimizations are off"]
1500    fn iteration_count_usize_max() {
1501        let mut space = space_uninit!(u32; 32);
1502        let mut bump_into = BumpInto::from_slice(&mut space[..]);
1503
1504        let mut iteration_count = 0u128;
1505        let nothing1 = bump_into
1506            .alloc_n_with(
1507                usize::MAX,
1508                core::iter::repeat_with(|| {
1509                    iteration_count += 1;
1510                    ZstWithDrop
1511                }),
1512            )
1513            .ok()
1514            .expect("allocation 1 failed");
1515        assert_eq!(nothing1.len(), usize::MAX);
1516        assert_eq!(iteration_count, usize::MAX as u128);
1517
1518        let mut iteration_count = 0u128;
1519        let nothing2 = bump_into.alloc_down_with(core::iter::repeat_with(|| {
1520            iteration_count += 1;
1521            ZstWithDrop
1522        }));
1523        assert_eq!(nothing2.len(), usize::MAX);
1524        assert_eq!(iteration_count, usize::MAX as u128);
1525    }
1526}