Skip to main content

bump_scope/
bump_scope.rs

1use core::{
2    ffi::CStr,
3    fmt::{self, Debug},
4    marker::PhantomData,
5    mem::MaybeUninit,
6    panic::{RefUnwindSafe, UnwindSafe},
7    ptr,
8};
9
10#[cfg(feature = "alloc")]
11use core::ptr::NonNull;
12
13#[cfg(feature = "nightly-clone-to-uninit")]
14use core::clone::CloneToUninit;
15
16use crate::{
17    BumpBox, BumpClaimGuard, BumpScopeGuard, Checkpoint, ErrorBehavior, NoDrop, SizedTypeProperties,
18    alloc::{AllocError, Allocator},
19    down_align_usize, maybe_default_allocator,
20    owned_slice::OwnedSlice,
21    polyfill::{non_null, transmute_mut, transmute_ref, transmute_value},
22    raw_bump::RawBump,
23    settings::{BumpAllocatorSettings, BumpSettings, MinimumAlignment, SupportedMinimumAlignment},
24    stats::{AnyStats, Stats},
25    traits::{
26        self, BumpAllocator, BumpAllocatorCore, BumpAllocatorScope, BumpAllocatorTyped, BumpAllocatorTypedScope,
27        MutBumpAllocatorTypedScope,
28    },
29    up_align_usize_unchecked,
30};
31
32#[cfg(feature = "alloc")]
33use crate::alloc::Global;
34
35#[cfg(feature = "panic-on-alloc")]
36use crate::panic_on_error;
37
38macro_rules! make_type {
39    ($($allocator_parameter:tt)*) => {
40        /// A bump allocation scope.
41        ///
42        /// A `BumpScope`'s allocations are live for `'a`, which is the lifetime of its associated `BumpScopeGuard` or `scoped` closure.
43        ///
44        /// `BumpScope` has mostly same api as [`Bump`].
45        ///
46        /// This type is provided as a parameter to the closure of [`scoped`], or created
47        /// by [`BumpScopeGuard::scope`]. A [`Bump`] can also be turned into a `BumpScope` using
48        /// [`as_scope`], [`as_mut_scope`] or `from` / `into`.
49        ///
50        /// [`scoped`]: crate::traits::BumpAllocator::scoped
51        /// [`BumpScopeGuard::scope`]: crate::BumpScopeGuard::scope
52        /// [`Bump`]: crate::Bump
53        /// [`as_scope`]: crate::Bump::as_scope
54        /// [`as_mut_scope`]: crate::Bump::as_mut_scope
55        /// [`reset`]: crate::Bump::reset
56        #[repr(transparent)]
57        pub struct BumpScope<'a, $($allocator_parameter)*, S = BumpSettings>
58        where
59            S: BumpAllocatorSettings,
60        {
61            pub(crate) raw: RawBump<A, S>,
62
63            /// Marks the lifetime of the mutably borrowed `BumpScopeGuard`.
64            pub(crate) marker: PhantomData<&'a ()>,
65        }
66    };
67}
68
69maybe_default_allocator!(make_type);
70
71impl<A, S> UnwindSafe for BumpScope<'_, A, S>
72where
73    A: RefUnwindSafe,
74    S: BumpAllocatorSettings,
75{
76}
77
78impl<A, S> RefUnwindSafe for BumpScope<'_, A, S>
79where
80    A: RefUnwindSafe,
81    S: BumpAllocatorSettings,
82{
83}
84
85impl<A, S> Debug for BumpScope<'_, A, S>
86where
87    S: BumpAllocatorSettings,
88{
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        AnyStats::from(self.stats()).debug_format("BumpScope", f)
91    }
92}
93
94impl<A, S> BumpScope<'_, A, S>
95where
96    A: Allocator,
97    S: BumpAllocatorSettings,
98{
99    /// Returns this `&mut BumpScope` as a `BumpScope`.
100    ///
101    /// This requires allocating a chunk if none has been allocated yet.
102    ///
103    /// This method exists so you can have `BumpScope<'a>` function parameters and
104    /// struct fields instead of `&'b mut BumpScope<'a>` so you don't have to deal with `'b`.
105    ///
106    /// It also enables more settings conversions since [`with_settings`] can do more than
107    /// [`borrow_mut_with_settings`].
108    ///
109    /// # Panics
110    /// Panics if the bump allocator is currently [claimed].
111    ///
112    /// Panics if the allocation fails.
113    ///
114    /// [claimed]: crate::traits::BumpAllocatorScope::claim
115    /// [`with_settings`]: BumpScope::with_settings
116    /// [`borrow_mut_with_settings`]: BumpScope::borrow_mut_with_settings
117    #[must_use]
118    #[inline(always)]
119    #[cfg(feature = "panic-on-alloc")]
120    pub fn by_value(&mut self) -> BumpScope<'_, A, S> {
121        panic_on_error(self.raw.make_allocated());
122
123        BumpScope {
124            raw: self.raw.clone(),
125            marker: PhantomData,
126        }
127    }
128
129    /// Returns this `&mut BumpScope` as a `BumpScope`.
130    ///
131    /// This requires allocating a chunk if none has been allocated yet.
132    ///
133    /// This method exists so you can have `BumpScope<'a>` function parameters and
134    /// struct fields instead of `&'b mut BumpScope<'a>` so you don't have to deal with `'b`.
135    ///
136    /// It also enables more settings conversions since [`with_settings`] can do more than
137    /// [`borrow_mut_with_settings`].
138    ///
139    /// # Errors
140    /// Errors if the bump allocator is currently [claimed].
141    ///
142    /// Errors if the allocation fails.
143    ///
144    /// [claimed]: crate::traits::BumpAllocatorScope::claim
145    /// [`with_settings`]: BumpScope::with_settings
146    /// [`borrow_mut_with_settings`]: BumpScope::borrow_mut_with_settings
147    #[inline(always)]
148    pub fn try_by_value(&mut self) -> Result<BumpScope<'_, A, S>, AllocError> {
149        self.raw.make_allocated::<AllocError>()?;
150
151        Ok(BumpScope {
152            raw: self.raw.clone(),
153            marker: PhantomData,
154        })
155    }
156}
157
158impl<'a, A, S> BumpScope<'a, A, S>
159where
160    S: BumpAllocatorSettings,
161{
162    /// Returns a type which provides statistics about the memory usage of the bump allocator.
163    #[must_use]
164    #[inline(always)]
165    pub fn stats(&self) -> Stats<'a, S> {
166        self.raw.stats()
167    }
168
169    #[inline(always)]
170    pub(crate) fn align<const ALIGN: usize>(&self)
171    where
172        MinimumAlignment<ALIGN>: SupportedMinimumAlignment,
173    {
174        self.raw.align::<ALIGN>();
175    }
176
177    #[inline(always)]
178    pub(crate) unsafe fn cast<S2>(self) -> BumpScope<'a, A, S2>
179    where
180        S2: BumpAllocatorSettings,
181    {
182        unsafe { transmute_value(self) }
183    }
184
185    #[inline(always)]
186    pub(crate) unsafe fn cast_mut<S2>(&mut self) -> &mut BumpScope<'a, A, S2>
187    where
188        S2: BumpAllocatorSettings,
189    {
190        unsafe { &mut *ptr::from_mut(self).cast::<BumpScope<'a, A, S2>>() }
191    }
192
193    /// Will error at compile time if `NEW_MIN_ALIGN < MIN_ALIGN`.
194    #[inline(always)]
195    pub(crate) fn must_align_more<const NEW_MIN_ALIGN: usize>(&self)
196    where
197        MinimumAlignment<NEW_MIN_ALIGN>: SupportedMinimumAlignment,
198    {
199        const {
200            assert!(
201                NEW_MIN_ALIGN >= S::MIN_ALIGN,
202                "`into_aligned` or `as_mut_aligned` can't decrease the minimum alignment"
203            );
204        }
205
206        self.align::<NEW_MIN_ALIGN>();
207    }
208
209    /// Mutably borrows `BumpScope` with a new minimum alignment.
210    ///
211    /// **This cannot decrease the alignment.** Trying to decrease alignment will result in a compile error.
212    /// You can use [`aligned`](Self::aligned) or [`scoped_aligned`](Self::scoped_aligned) to decrease the alignment.
213    ///
214    /// When decreasing the alignment we need to make sure that the bump position is realigned to the original alignment.
215    /// That can only be ensured by having a function that takes a closure, like the methods mentioned above do.
216    #[inline(always)]
217    pub fn as_mut_aligned<const NEW_MIN_ALIGN: usize>(
218        &mut self,
219    ) -> &mut BumpScope<'a, A, S::WithMinimumAlignment<NEW_MIN_ALIGN>>
220    where
221        MinimumAlignment<NEW_MIN_ALIGN>: SupportedMinimumAlignment,
222    {
223        self.must_align_more::<NEW_MIN_ALIGN>();
224        unsafe { self.cast_mut() }
225    }
226
227    /// Converts this `BumpScope` into a `BumpScope` with a new minimum alignment.
228    ///
229    /// **This cannot decrease the alignment.** Trying to decrease alignment will result in a compile error.
230    /// You can use [`aligned`](Self::aligned) or [`scoped_aligned`](Self::scoped_aligned) to decrease the alignment.
231    ///
232    /// When decreasing the alignment we need to make sure that the bump position is realigned to the original alignment.
233    /// That can only be ensured by having a function that takes a closure, like the methods mentioned above do.
234    ///
235    /// If this was allowed to decrease the alignment it would break minimum alignment:
236    ///
237    /// ```ignore
238    /// # // We can't `compile_fail,E0080` this doc test because it does not do the compile step
239    /// # // that triggers the error.
240    /// # use bump_scope::{Bump, alloc::Global};
241    /// let mut bump: Bump<Global, 8, true> = Bump::new();
242    /// let mut guard = bump.scope_guard();
243    ///
244    /// {
245    ///     let scope = guard.scope().into_aligned::<1>();
246    ///     scope.alloc(0u8);
247    /// }
248    ///
249    /// {
250    ///     let scope = guard.scope();
251    ///     // scope is not aligned to `MIN_ALIGN`!!
252    /// }
253    ///
254    /// ```
255    #[inline(always)]
256    pub fn into_aligned<const NEW_MIN_ALIGN: usize>(self) -> BumpScope<'a, A, S::WithMinimumAlignment<NEW_MIN_ALIGN>>
257    where
258        MinimumAlignment<NEW_MIN_ALIGN>: SupportedMinimumAlignment,
259    {
260        self.must_align_more::<NEW_MIN_ALIGN>();
261        unsafe { self.cast() }
262    }
263
264    /// Converts this `BumpScope` into a `BumpScope` with new settings.
265    ///
266    /// Not all settings can be converted to. This function will fail to compile if:
267    /// - `NewS::MIN_ALIGN < S::MIN_ALIGN`
268    /// - `NewS::UP != S::UP`
269    /// - `NewS::GUARANTEED_ALLOCATED > S::GUARANTEED_ALLOCATED`
270    /// - `NewS::CLAIMABLE < S::CLAIMABLE`
271    #[inline]
272    pub fn with_settings<NewS>(self) -> BumpScope<'a, A, NewS>
273    where
274        NewS: BumpAllocatorSettings,
275    {
276        self.raw.ensure_scope_satisfies_settings::<NewS>();
277        unsafe { transmute_value(self) }
278    }
279
280    /// Borrows this `BumpScope` with new settings.
281    ///
282    /// Not all settings can be converted to. This function will fail to compile if:
283    /// - `NewS::MIN_ALIGN != S::MIN_ALIGN`
284    /// - `NewS::UP != S::UP`
285    /// - `NewS::GUARANTEED_ALLOCATED > S::GUARANTEED_ALLOCATED`
286    /// - `NewS::CLAIMABLE != S::CLAIMABLE`
287    #[inline]
288    pub fn borrow_with_settings<NewS>(&self) -> &BumpScope<'a, A, NewS>
289    where
290        NewS: BumpAllocatorSettings,
291    {
292        self.raw.ensure_satisfies_settings_for_borrow::<NewS>();
293        unsafe { transmute_ref(self) }
294    }
295
296    /// Borrows this `BumpScope` mutably with new settings.
297    ///
298    /// Not all settings can be converted to. This function will fail to compile if:
299    /// - `NewS::MIN_ALIGN < S::MIN_ALIGN`
300    /// - `NewS::UP != S::UP`
301    /// - `NewS::GUARANTEED_ALLOCATED != S::GUARANTEED_ALLOCATED`
302    /// - `NewS::CLAIMABLE != S::CLAIMABLE`
303    #[inline]
304    pub fn borrow_mut_with_settings<NewS>(&mut self) -> &mut BumpScope<'a, A, NewS>
305    where
306        NewS: BumpAllocatorSettings,
307    {
308        self.raw.ensure_satisfies_settings_for_borrow_mut::<NewS>();
309        unsafe { transmute_mut(self) }
310    }
311}
312
313#[cfg(feature = "alloc")]
314impl<S> BumpScope<'_, Global, S>
315where
316    S: BumpAllocatorSettings,
317{
318    /// Converts this `BumpScope` into a raw pointer.
319    #[inline]
320    #[must_use]
321    pub fn into_raw(self) -> NonNull<()> {
322        self.raw.into_raw()
323    }
324
325    /// Converts the raw pointer that was created with [`into_raw`](Self::into_raw) back into a `BumpScope`.
326    ///
327    /// # Safety
328    /// This is highly unsafe, due to the number of invariants that aren't checked:
329    /// - `ptr` must have been created with `Self::into_raw`.
330    /// - This function must only be called once with this `ptr`.
331    /// - Nothing must have been allocated since then.
332    /// - The lifetime must match the original one.
333    /// - The settings must match the original ones.
334    #[inline]
335    #[must_use]
336    pub unsafe fn from_raw(ptr: NonNull<()>) -> Self {
337        Self {
338            raw: unsafe { RawBump::from_raw(ptr) },
339            marker: PhantomData,
340        }
341    }
342}
343
344impl<A, S> NoDrop for BumpScope<'_, A, S> where S: BumpAllocatorSettings {}
345
346/// Methods that forward to traits.
347// Documentation is in the forwarded to methods.
348#[allow(clippy::missing_errors_doc, clippy::missing_safety_doc)]
349impl<'a, A, S> BumpScope<'a, A, S>
350where
351    A: Allocator,
352    S: BumpAllocatorSettings,
353{
354    traits::forward_methods! {
355        self: self
356        access: {self}
357        access_mut: {self}
358        lifetime: 'a
359    }
360}
361
362/// Additional `alloc` methods that are not available in traits.
363impl<'a, A, S> BumpScope<'a, A, S>
364where
365    A: Allocator,
366    S: BumpAllocatorSettings,
367{
368    /// Allocates the result of `f` in the bump allocator, then moves `E` out of it and deallocates the space it took up.
369    ///
370    /// This can be more performant than allocating `T` after the fact, as `Result<T, E>` may be constructed in the bump allocators memory instead of on the stack and then copied over.
371    ///
372    /// There is also [`alloc_try_with_mut`](Self::alloc_try_with_mut), optimized for a mutable reference.
373    ///
374    /// # Panics
375    /// Panics if the allocation fails.
376    ///
377    /// # Examples
378    #[cfg_attr(feature = "nightly-tests", doc = "```")]
379    #[cfg_attr(not(feature = "nightly-tests"), doc = "```ignore")]
380    /// # #![feature(offset_of_enum)]
381    /// # use core::mem::offset_of;
382    /// # use bump_scope::Bump;
383    /// # let bump: Bump = Bump::new();
384    /// let result = bump.alloc_try_with(|| -> Result<i32, i32> { Ok(123) });
385    /// assert_eq!(result.unwrap(), 123);
386    /// assert_eq!(bump.stats().allocated(), offset_of!(Result<i32, i32>, Ok.0) + size_of::<i32>());
387    /// ```
388    #[cfg_attr(feature = "nightly-tests", doc = "```")]
389    #[cfg_attr(not(feature = "nightly-tests"), doc = "```ignore")]
390    /// # use bump_scope::Bump;
391    /// # let bump: Bump = Bump::new();
392    /// let result = bump.alloc_try_with(|| -> Result<i32, i32> { Err(123) });
393    /// assert_eq!(result.unwrap_err(), 123);
394    /// assert_eq!(bump.stats().allocated(), 0);
395    /// ```
396    #[inline(always)]
397    #[cfg(feature = "panic-on-alloc")]
398    #[expect(clippy::missing_errors_doc)]
399    pub fn alloc_try_with<T, E>(&self, f: impl FnOnce() -> Result<T, E>) -> Result<BumpBox<'a, T>, E> {
400        panic_on_error(self.generic_alloc_try_with(f))
401    }
402
403    /// Allocates the result of `f` in the bump allocator, then moves `E` out of it and deallocates the space it took up.
404    ///
405    /// This can be more performant than allocating `T` after the fact, as `Result<T, E>` may be constructed in the bump allocators memory instead of on the stack and then copied over.
406    ///
407    /// There is also [`try_alloc_try_with_mut`](Self::try_alloc_try_with_mut), optimized for a mutable reference.
408    ///
409    /// # Errors
410    /// Errors if the allocation fails.
411    ///
412    /// # Examples
413    #[cfg_attr(feature = "nightly-tests", doc = "```")]
414    #[cfg_attr(not(feature = "nightly-tests"), doc = "```ignore")]
415    /// # #![feature(offset_of_enum)]
416    /// # use core::mem::offset_of;
417    /// # use bump_scope::Bump;
418    /// # let bump: Bump = Bump::new();
419    /// let result = bump.try_alloc_try_with(|| -> Result<i32, i32> { Ok(123) })?;
420    /// assert_eq!(result.unwrap(), 123);
421    /// assert_eq!(bump.stats().allocated(), offset_of!(Result<i32, i32>, Ok.0) + size_of::<i32>());
422    /// # Ok::<(), bump_scope::alloc::AllocError>(())
423    /// ```
424    #[cfg_attr(feature = "nightly-tests", doc = "```")]
425    #[cfg_attr(not(feature = "nightly-tests"), doc = "```ignore")]
426    /// # use bump_scope::Bump;
427    /// # let bump: Bump = Bump::new();
428    /// let result = bump.try_alloc_try_with(|| -> Result<i32, i32> { Err(123) })?;
429    /// assert_eq!(result.unwrap_err(), 123);
430    /// assert_eq!(bump.stats().allocated(), 0);
431    /// # Ok::<(), bump_scope::alloc::AllocError>(())
432    /// ```
433    #[inline(always)]
434    pub fn try_alloc_try_with<T, E>(
435        &self,
436        f: impl FnOnce() -> Result<T, E>,
437    ) -> Result<Result<BumpBox<'a, T>, E>, AllocError> {
438        self.generic_alloc_try_with(f)
439    }
440
441    #[inline(always)]
442    pub(crate) fn generic_alloc_try_with<B: ErrorBehavior, T, E>(
443        &self,
444        f: impl FnOnce() -> Result<T, E>,
445    ) -> Result<Result<BumpBox<'a, T>, E>, B> {
446        if T::IS_ZST {
447            return match f() {
448                Ok(value) => Ok(Ok(BumpBox::zst(value))),
449                Err(error) => Ok(Err(error)),
450            };
451        }
452
453        let checkpoint_before_alloc = self.checkpoint();
454        let uninit = self.generic_alloc_uninit::<B, Result<T, E>>()?;
455        let ptr = BumpBox::into_raw(uninit).cast::<Result<T, E>>();
456
457        // When bumping downwards the chunk's position is the same as `ptr`.
458        // Using `ptr` is faster so we use that.
459        let pos = if S::UP { self.raw.chunk.get().pos() } else { ptr.cast() };
460
461        Ok(unsafe {
462            non_null::write_with(ptr, f);
463
464            // If `f` made allocations on this bump allocator we can't shrink the allocation.
465            let can_shrink = pos == self.raw.chunk.get().pos();
466
467            match non_null::result(ptr) {
468                Ok(value) => Ok({
469                    if can_shrink {
470                        let new_pos = if S::UP {
471                            let pos = value.add(1).addr().get();
472                            up_align_usize_unchecked(pos, S::MIN_ALIGN)
473                        } else {
474                            let pos = value.addr().get();
475                            down_align_usize(pos, S::MIN_ALIGN)
476                        };
477
478                        // The allocation of was successful, so our chunk must be allocated.
479                        let chunk = self.raw.chunk.get().as_non_dummy_unchecked();
480                        chunk.set_pos_addr(new_pos);
481                    }
482
483                    BumpBox::from_raw(value)
484                }),
485                Err(error) => Err({
486                    let error = error.read();
487
488                    if can_shrink {
489                        self.reset_to(checkpoint_before_alloc);
490                    }
491
492                    error
493                }),
494            }
495        })
496    }
497
498    /// Allocates the result of `f` in the bump allocator, then moves `E` out of it and deallocates the space it took up.
499    ///
500    /// This can be more performant than allocating `T` after the fact, as `Result<T, E>` may be constructed in the bump allocators memory instead of on the stack and then copied over.
501    ///
502    /// This is just like [`alloc_try_with`](Self::alloc_try_with), but optimized for a mutable reference.
503    ///
504    /// # Panics
505    /// Panics if the allocation fails.
506    ///
507    /// # Examples
508    #[cfg_attr(feature = "nightly-tests", doc = "```")]
509    #[cfg_attr(not(feature = "nightly-tests"), doc = "```ignore")]
510    /// # #![feature(offset_of_enum)]
511    /// # use core::mem::offset_of;
512    /// # use bump_scope::Bump;
513    /// # let mut bump: Bump = Bump::new();
514    /// let result = bump.alloc_try_with_mut(|| -> Result<i32, i32> { Ok(123) });
515    /// assert_eq!(result.unwrap(), 123);
516    /// assert_eq!(bump.stats().allocated(), offset_of!(Result<i32, i32>, Ok.0) + size_of::<i32>());
517    /// ```
518    #[cfg_attr(feature = "nightly-tests", doc = "```")]
519    #[cfg_attr(not(feature = "nightly-tests"), doc = "```ignore")]
520    /// # use bump_scope::Bump;
521    /// # let mut bump: Bump = Bump::new();
522    /// let result = bump.alloc_try_with_mut(|| -> Result<i32, i32> { Err(123) });
523    /// assert_eq!(result.unwrap_err(), 123);
524    /// assert_eq!(bump.stats().allocated(), 0);
525    /// ```
526    #[inline(always)]
527    #[cfg(feature = "panic-on-alloc")]
528    #[expect(clippy::missing_errors_doc)]
529    pub fn alloc_try_with_mut<T, E>(&mut self, f: impl FnOnce() -> Result<T, E>) -> Result<BumpBox<'a, T>, E> {
530        panic_on_error(self.generic_alloc_try_with_mut(f))
531    }
532
533    /// Allocates the result of `f` in the bump allocator, then moves `E` out of it and deallocates the space it took up.
534    ///
535    /// This can be more performant than allocating `T` after the fact, as `Result<T, E>` may be constructed in the bump allocators memory instead of on the stack and then copied over.
536    ///
537    /// This is just like [`try_alloc_try_with`](Self::try_alloc_try_with), but optimized for a mutable reference.
538    ///
539    /// # Errors
540    /// Errors if the allocation fails.
541    ///
542    /// # Examples
543    #[cfg_attr(feature = "nightly-tests", doc = "```")]
544    #[cfg_attr(not(feature = "nightly-tests"), doc = "```ignore")]
545    /// # #![feature(offset_of_enum)]
546    /// # use core::mem::offset_of;
547    /// # use bump_scope::Bump;
548    /// # let mut bump: Bump = Bump::new();
549    /// let result = bump.try_alloc_try_with_mut(|| -> Result<i32, i32> { Ok(123) })?;
550    /// assert_eq!(result.unwrap(), 123);
551    /// assert_eq!(bump.stats().allocated(), offset_of!(Result<i32, i32>, Ok.0) + size_of::<i32>());
552    /// # Ok::<(), bump_scope::alloc::AllocError>(())
553    /// ```
554    #[cfg_attr(feature = "nightly-tests", doc = "```")]
555    #[cfg_attr(not(feature = "nightly-tests"), doc = "```ignore")]
556    /// # use bump_scope::Bump;
557    /// # let mut bump: Bump = Bump::new();
558    /// let result = bump.try_alloc_try_with_mut(|| -> Result<i32, i32> { Err(123) })?;
559    /// assert_eq!(result.unwrap_err(), 123);
560    /// assert_eq!(bump.stats().allocated(), 0);
561    /// # Ok::<(), bump_scope::alloc::AllocError>(())
562    /// ```
563    #[inline(always)]
564    pub fn try_alloc_try_with_mut<T, E>(
565        &mut self,
566        f: impl FnOnce() -> Result<T, E>,
567    ) -> Result<Result<BumpBox<'a, T>, E>, AllocError> {
568        self.generic_alloc_try_with_mut(f)
569    }
570
571    #[inline(always)]
572    pub(crate) fn generic_alloc_try_with_mut<B: ErrorBehavior, T, E>(
573        &mut self,
574        f: impl FnOnce() -> Result<T, E>,
575    ) -> Result<Result<BumpBox<'a, T>, E>, B> {
576        if T::IS_ZST {
577            return match f() {
578                Ok(value) => Ok(Ok(BumpBox::zst(value))),
579                Err(error) => Ok(Err(error)),
580            };
581        }
582
583        let checkpoint = self.checkpoint();
584        let ptr = self.raw.prepare_sized_allocation::<B, Result<T, E>>()?;
585
586        Ok(unsafe {
587            non_null::write_with(ptr, f);
588
589            // There is no need for `can_shrink` checks, because we have a mutable reference
590            // so there's no way anyone else has allocated in `f`.
591            match non_null::result(ptr) {
592                Ok(value) => Ok({
593                    let new_pos = if S::UP {
594                        let pos = value.add(1).addr().get();
595                        up_align_usize_unchecked(pos, S::MIN_ALIGN)
596                    } else {
597                        let pos = value.addr().get();
598                        down_align_usize(pos, S::MIN_ALIGN)
599                    };
600
601                    // The allocation was successful, so our chunk must be allocated.
602                    let chunk = self.raw.chunk.get().as_non_dummy_unchecked();
603                    chunk.set_pos_addr(new_pos);
604
605                    BumpBox::from_raw(value)
606                }),
607                Err(error) => Err({
608                    let error = error.read();
609                    self.reset_to(checkpoint);
610                    error
611                }),
612            }
613        })
614    }
615
616    #[inline(always)]
617    pub(crate) fn generic_alloc_uninit<B: ErrorBehavior, T>(&self) -> Result<BumpBox<'a, MaybeUninit<T>>, B> {
618        if T::IS_ZST {
619            return Ok(BumpBox::zst(MaybeUninit::uninit()));
620        }
621
622        let ptr = self.raw.alloc_sized::<B, T>()?.cast::<MaybeUninit<T>>();
623        unsafe { Ok(BumpBox::from_raw(ptr)) }
624    }
625}