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}