bump_scope/
alloc.rs

1//! Memory allocation APIs.
2//!
3//! This crate's [`Allocator`], [`AllocError`] and [`Global`] resemble the types and traits from
4//! the nightly allocator api of the standard library (28dc797 2025-12-10).
5//!
6//! `bump-scope` provides compatibility with the allocator apis of:
7//! - the nightly standard library via the feature `nightly-allocator-api`
8//! - `allocator_api2` version 0.2 via the feature `allocator-api2-02`
9//! - `allocator_api2` version 0.3 via the feature `allocator-api2-03`
10//! - `allocator_api2` version 0.4 via the feature `allocator-api2-04`
11//!
12//! `Bump` and `Bump(Scope)` will implement those foreign `Allocator` traits when the respective feature is enabled.
13//! You can also use implementors of their `Allocator` trait as base allocators via the [compat] wrapper types.
14//!
15//! You can convert between this crate's `AllocError` and foreign one's via the `From` and `Into` traits.
16
17#[cfg(feature = "alloc")]
18mod global;
19#[cfg(feature = "std")]
20mod system;
21
22use core::{
23    alloc::Layout,
24    error::Error,
25    fmt,
26    ptr::{self, NonNull},
27};
28
29use crate::polyfill::non_null;
30
31#[cfg(feature = "alloc")]
32pub use global::Global;
33
34/// Contains wrappers that makes implementors of foreign `Allocator` traits
35/// implement this crate's [`Allocator`] and vice versa.
36///
37/// Note that the bump allocator itself already implements foreign `Allocator` traits, so you
38/// generally only need this for a base allocator.
39pub mod compat {
40    #[cfg(feature = "allocator-api2-02")]
41    pub use crate::features::allocator_api2_02::AllocatorApi2V02Compat;
42    #[cfg(feature = "allocator-api2-03")]
43    pub use crate::features::allocator_api2_03::AllocatorApi2V03Compat;
44    #[cfg(feature = "allocator-api2-04")]
45    pub use crate::features::allocator_api2_04::AllocatorApi2V04Compat;
46    #[cfg(all(feature = "alloc", feature = "nightly-allocator-api"))]
47    pub use crate::features::nightly_allocator_api::AllocatorNightlyCompat;
48}
49
50/// The `AllocError` error indicates an allocation failure
51/// that may be due to resource exhaustion or to
52/// something wrong when combining the given input arguments with this
53/// allocator.
54#[derive(Copy, Clone, PartialEq, Eq, Debug)]
55pub struct AllocError;
56
57impl Error for AllocError {}
58
59// (we need this for downstream impl of trait Error)
60impl fmt::Display for AllocError {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        f.write_str("memory allocation failed")
63    }
64}
65
66/// An implementation of `Allocator` can allocate, grow, shrink, and deallocate arbitrary blocks of
67/// data described via [`Layout`][].
68///
69/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers.
70/// An allocator for `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the
71/// allocated memory.
72///
73/// In contrast to [`GlobalAlloc`][alloc_crate::alloc::GlobalAlloc], `Allocator` allows zero-sized allocations. If an underlying
74/// allocator does not support this (like jemalloc) or responds by returning a null pointer
75/// (such as `libc::malloc`), this must be caught by the implementation.
76///
77/// ### Currently allocated memory
78///
79/// Some of the methods require that a memory block is *currently allocated* by an allocator.
80/// This means that:
81///  * the starting address for that memory block was previously
82///    returned by [`allocate`], [`grow`], or [`shrink`], and
83///  * the memory block has not subsequently been deallocated.
84///
85/// A memory block is deallocated by a call to [`deallocate`],
86/// or by a call to [`grow`] or [`shrink`] that returns `Ok`.
87/// A call to `grow` or `shrink` that returns `Err`,
88/// does not deallocate the memory block passed to it.
89///
90/// [`allocate`]: Allocator::allocate
91/// [`grow`]: Allocator::grow
92/// [`shrink`]: Allocator::shrink
93/// [`deallocate`]: Allocator::deallocate
94///
95/// ### Memory fitting
96///
97/// Some of the methods require that a `layout` *fit* a memory block or vice versa. This means that the
98/// following conditions must hold:
99///  * the memory block must be *currently allocated* with alignment of [`layout.align()`], and
100///  * [`layout.size()`] must fall in the range `min ..= max`, where:
101///    - `min` is the size of the layout used to allocate the block, and
102///    - `max` is the actual size returned from [`allocate`], [`grow`], or [`shrink`].
103///
104/// [`layout.align()`]: Layout::align
105/// [`layout.size()`]: Layout::size
106///
107/// # Safety
108///
109/// Memory blocks that are [*currently allocated*] by an allocator,
110/// must point to valid memory, and retain their validity until either:
111///  - the memory block is deallocated, or
112///  - the allocator is dropped.
113///
114/// Copying, cloning, or moving the allocator must not invalidate memory blocks returned from it.
115/// A copied or cloned allocator must behave like the original allocator.
116///
117/// A memory block which is [*currently allocated*] may be passed to
118/// any method of the allocator that accepts such an argument.
119///
120/// [*currently allocated*]: #currently-allocated-memory
121pub unsafe trait Allocator {
122    /// Attempts to allocate a block of memory.
123    ///
124    /// On success, returns a [`NonNull<[u8]>`][NonNull] meeting the size and alignment guarantees of `layout`.
125    ///
126    /// The returned block may have a larger size than specified by `layout.size()`, and may or may
127    /// not have its contents initialized.
128    ///
129    /// The returned block of memory remains valid as long as it is [*currently allocated*] and the shorter of:
130    ///   - the borrow-checker lifetime of the allocator type itself.
131    ///   - as long as the allocator and all its clones have not been dropped.
132    ///
133    /// [*currently allocated*]: #currently-allocated-memory
134    ///
135    /// # Errors
136    ///
137    /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
138    /// allocator's size or alignment constraints.
139    ///
140    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
141    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
142    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
143    ///
144    /// Clients wishing to abort computation in response to an allocation error are encouraged to
145    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
146    ///
147    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
148    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
149
150    /// Behaves like `allocate`, but also ensures that the returned memory is zero-initialized.
151    ///
152    /// # Errors
153    ///
154    /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
155    /// allocator's size or alignment constraints.
156    ///
157    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
158    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
159    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
160    ///
161    /// Clients wishing to abort computation in response to an allocation error are encouraged to
162    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
163    ///
164    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
165    fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
166        let ptr = self.allocate(layout)?;
167        // SAFETY: `alloc` returns a valid memory block
168        unsafe { non_null::as_non_null_ptr(ptr).as_ptr().write_bytes(0, ptr.len()) }
169        Ok(ptr)
170    }
171
172    /// Deallocates the memory referenced by `ptr`.
173    ///
174    /// # Safety
175    ///
176    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, and
177    /// * `layout` must [*fit*] that block of memory.
178    ///
179    /// [*currently allocated*]: #currently-allocated-memory
180    /// [*fit*]: #memory-fitting
181    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
182
183    /// Attempts to extend the memory block.
184    ///
185    /// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated
186    /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
187    /// this, the allocator may extend the allocation referenced by `ptr` to fit the new layout.
188    ///
189    /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
190    /// transferred to this allocator. Any access to the old `ptr` is Undefined Behavior, even if the
191    /// allocation was grown in-place. The newly returned pointer is the only valid pointer
192    /// for accessing this memory now.
193    ///
194    /// If this method returns `Err`, then ownership of the memory block has not been transferred to
195    /// this allocator, and the contents of the memory block are unaltered.
196    ///
197    /// # Safety
198    ///
199    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
200    /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
201    /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`.
202    ///
203    /// Note that `new_layout.align()` need not be the same as `old_layout.align()`.
204    ///
205    /// [*currently allocated*]: #currently-allocated-memory
206    /// [*fit*]: #memory-fitting
207    ///
208    /// # Errors
209    ///
210    /// Returns `Err` if the new layout does not meet the allocator's size and alignment
211    /// constraints of the allocator, or if growing otherwise fails.
212    ///
213    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
214    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
215    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
216    ///
217    /// Clients wishing to abort computation in response to an allocation error are encouraged to
218    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
219    ///
220    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
221    unsafe fn grow(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
222        debug_assert!(
223            new_layout.size() >= old_layout.size(),
224            "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
225        );
226
227        let new_ptr = self.allocate(new_layout)?;
228
229        // SAFETY: because `new_layout.size()` must be greater than or equal to
230        // `old_layout.size()`, both the old and new memory allocation are valid for reads and
231        // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet
232        // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
233        // safe. The safety contract for `dealloc` must be upheld by the caller.
234        unsafe {
235            ptr::copy_nonoverlapping(ptr.as_ptr(), non_null::as_mut_ptr(new_ptr), old_layout.size());
236            self.deallocate(ptr, old_layout);
237        }
238
239        Ok(new_ptr)
240    }
241
242    /// Behaves like `grow`, but also ensures that the new contents are set to zero before being
243    /// returned.
244    ///
245    /// The memory block will contain the following contents after a successful call to
246    /// `grow_zeroed`:
247    ///   * Bytes `0..old_layout.size()` are preserved from the original allocation.
248    ///   * Bytes `old_layout.size()..old_size` will either be preserved or zeroed, depending on
249    ///     the allocator implementation. `old_size` refers to the size of the memory block prior
250    ///     to the `grow_zeroed` call, which may be larger than the size that was originally
251    ///     requested when it was allocated.
252    ///   * Bytes `old_size..new_size` are zeroed. `new_size` refers to the size of the memory
253    ///     block returned by the `grow_zeroed` call.
254    ///
255    /// # Safety
256    ///
257    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
258    /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
259    /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`.
260    ///
261    /// Note that `new_layout.align()` need not be the same as `old_layout.align()`.
262    ///
263    /// [*currently allocated*]: #currently-allocated-memory
264    /// [*fit*]: #memory-fitting
265    ///
266    /// # Errors
267    ///
268    /// Returns `Err` if the new layout does not meet the allocator's size and alignment
269    /// constraints of the allocator, or if growing otherwise fails.
270    ///
271    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
272    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
273    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
274    ///
275    /// Clients wishing to abort computation in response to an allocation error are encouraged to
276    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
277    ///
278    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
279    unsafe fn grow_zeroed(
280        &self,
281        ptr: NonNull<u8>,
282        old_layout: Layout,
283        new_layout: Layout,
284    ) -> Result<NonNull<[u8]>, AllocError> {
285        debug_assert!(
286            new_layout.size() >= old_layout.size(),
287            "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
288        );
289
290        let new_ptr = self.allocate_zeroed(new_layout)?;
291
292        // SAFETY: because `new_layout.size()` must be greater than or equal to
293        // `old_layout.size()`, both the old and new memory allocation are valid for reads and
294        // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet
295        // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
296        // safe. The safety contract for `dealloc` must be upheld by the caller.
297        unsafe {
298            ptr::copy_nonoverlapping(ptr.as_ptr(), non_null::as_mut_ptr(new_ptr), old_layout.size());
299            self.deallocate(ptr, old_layout);
300        }
301
302        Ok(new_ptr)
303    }
304
305    /// Attempts to shrink the memory block.
306    ///
307    /// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated
308    /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
309    /// this, the allocator may shrink the allocation referenced by `ptr` to fit the new layout.
310    ///
311    /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
312    /// transferred to this allocator. Any access to the old `ptr` is Undefined Behavior, even if the
313    /// allocation was shrunk in-place. The newly returned pointer is the only valid pointer
314    /// for accessing this memory now.
315    ///
316    /// If this method returns `Err`, then ownership of the memory block has not been transferred to
317    /// this allocator, and the contents of the memory block are unaltered.
318    ///
319    /// # Safety
320    ///
321    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
322    /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
323    /// * `new_layout.size()` must be smaller than or equal to `old_layout.size()`.
324    ///
325    /// Note that `new_layout.align()` need not be the same as `old_layout.align()`.
326    ///
327    /// [*currently allocated*]: #currently-allocated-memory
328    /// [*fit*]: #memory-fitting
329    ///
330    /// # Errors
331    ///
332    /// Returns `Err` if the new layout does not meet the allocator's size and alignment
333    /// constraints of the allocator, or if shrinking otherwise fails.
334    ///
335    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
336    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
337    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
338    ///
339    /// Clients wishing to abort computation in response to an allocation error are encouraged to
340    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
341    ///
342    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
343    unsafe fn shrink(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
344        debug_assert!(
345            new_layout.size() <= old_layout.size(),
346            "`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
347        );
348
349        let new_ptr = self.allocate(new_layout)?;
350
351        // SAFETY: because `new_layout.size()` must be lower than or equal to
352        // `old_layout.size()`, both the old and new memory allocation are valid for reads and
353        // writes for `new_layout.size()` bytes. Also, because the old allocation wasn't yet
354        // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
355        // safe. The safety contract for `dealloc` must be upheld by the caller.
356        unsafe {
357            ptr::copy_nonoverlapping(ptr.as_ptr(), non_null::as_mut_ptr(new_ptr), new_layout.size());
358            self.deallocate(ptr, old_layout);
359        }
360
361        Ok(new_ptr)
362    }
363
364    /// Creates a "by reference" adapter for this instance of `Allocator`.
365    ///
366    /// The returned adapter also implements `Allocator` and will simply borrow this.
367    #[inline(always)]
368    fn by_ref(&self) -> &Self
369    where
370        Self: Sized,
371    {
372        self
373    }
374}
375
376unsafe impl<A> Allocator for &A
377where
378    A: Allocator + ?Sized,
379{
380    #[inline]
381    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
382        (**self).allocate(layout)
383    }
384
385    #[inline]
386    fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
387        (**self).allocate_zeroed(layout)
388    }
389
390    #[inline]
391    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
392        // SAFETY: the safety contract must be upheld by the caller
393        unsafe { (**self).deallocate(ptr, layout) }
394    }
395
396    #[inline]
397    unsafe fn grow(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
398        // SAFETY: the safety contract must be upheld by the caller
399        unsafe { (**self).grow(ptr, old_layout, new_layout) }
400    }
401
402    #[inline]
403    unsafe fn grow_zeroed(
404        &self,
405        ptr: NonNull<u8>,
406        old_layout: Layout,
407        new_layout: Layout,
408    ) -> Result<NonNull<[u8]>, AllocError> {
409        // SAFETY: the safety contract must be upheld by the caller
410        unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) }
411    }
412
413    #[inline]
414    unsafe fn shrink(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
415        // SAFETY: the safety contract must be upheld by the caller
416        unsafe { (**self).shrink(ptr, old_layout, new_layout) }
417    }
418}
419
420unsafe impl<A> Allocator for &mut A
421where
422    A: Allocator + ?Sized,
423{
424    #[inline]
425    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
426        (**self).allocate(layout)
427    }
428
429    #[inline]
430    fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
431        (**self).allocate_zeroed(layout)
432    }
433
434    #[inline]
435    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
436        // SAFETY: the safety contract must be upheld by the caller
437        unsafe { (**self).deallocate(ptr, layout) }
438    }
439
440    #[inline]
441    unsafe fn grow(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
442        // SAFETY: the safety contract must be upheld by the caller
443        unsafe { (**self).grow(ptr, old_layout, new_layout) }
444    }
445
446    #[inline]
447    unsafe fn grow_zeroed(
448        &self,
449        ptr: NonNull<u8>,
450        old_layout: Layout,
451        new_layout: Layout,
452    ) -> Result<NonNull<[u8]>, AllocError> {
453        // SAFETY: the safety contract must be upheld by the caller
454        unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) }
455    }
456
457    #[inline]
458    unsafe fn shrink(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
459        // SAFETY: the safety contract must be upheld by the caller
460        unsafe { (**self).shrink(ptr, old_layout, new_layout) }
461    }
462}
463
464// Used for static assertions.
465#[cfg(test)]
466#[derive(Clone)]
467pub(crate) struct NoopAllocator;
468
469#[cfg(test)]
470unsafe impl Allocator for NoopAllocator {
471    fn allocate(&self, _layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
472        Err(AllocError)
473    }
474
475    unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {}
476}
477
478pub(crate) mod box_like {
479    pub trait Sealed {
480        type T: ?Sized;
481        type A;
482
483        unsafe fn from_raw_in(ptr: *mut Self::T, allocator: Self::A) -> Self;
484    }
485}
486
487/// A type that behaves like [`Box`](alloc_crate::boxed::Box).
488///
489/// This is used for <code>BumpBox::[into_box](crate::BumpBox::into_box)</code>.
490///
491/// **Note:** This trait is also implemented for `allocator_api2` version 0.2's [`Box`](allocator_api2_02::boxed::Box)
492/// but it won't show up in the implementations below because the documentation is built with the `nightly-allocator-api`
493/// feature which makes `allocator_api2` version 0.2's `Box` become nightly's `Box`.
494pub trait BoxLike: box_like::Sealed {}