Skip to main content

Bump

Struct Bump 

Source
pub struct Bump<T> { /* private fields */ }
Expand description

A bump allocator whose storage capacity and alignment is given by T.

This type dereferences to the generic BumpSlice that implements the allocation behavior. Note that BumpSlice is an unsized type. In contrast this type is sized so it is possible to construct an instance on the stack or leak one from another bump allocator such as a global one.

§Usage

For on-stack usage this works the same as Bump. Note that it is not possible to use as a global allocator though.

One interesting use case for this struct is as scratch space for subroutines. This ensures good locality and cache usage. It can also allows such subroutines to use a dynamic amount of space without the need to actually allocate. Contrary to other methods where the caller provides some preallocated memory it will also not ‘leak’ private data types. This could be used in handling web requests.

use static_alloc::unsync::Bump;

let mut stack_buffer: Bump<[usize; 64]> = Bump::uninit();
subroutine_one(&stack_buffer);
stack_buffer.reset();
subroutine_two(&stack_buffer);

Note that you need not use the stack for the Bump itself. Indeed, you could allocate a large contiguous instance from the global (synchronized) allocator and then do subsequent allocations from the Bump you’ve obtained. This avoids potential contention on a lock of the global allocator, especially in case you must do many small allocations. If you’re writing an allocator yourself you might use this technique as an internal optimization.

use static_alloc::unsync::{Bump, BumpSlice};
let mut local_page: Box<Bump<[u64; 64]>> = Box::new(Bump::uninit());

for request in iterate_recv() {
    local_page.reset();
    handle_request(&local_page, request);
}

§Coercion into BumpSlice

This allocator nominally implements Deref into BumpSlice. However, the layout of these two structs is equivalent only for types that have at most an alignment of usize (e.g. arrays of u8, u16, or more integers depending on the platform pointer size).

Warning: An attempt to use this dereference with an invalid type will trigger a post-monomorphization error! This choice was made to avoid complicated encoding of the precondition into a viral trait bound and considering you’re likely to use very concrete instances that either work, or would have been UB.

For instance, this will fail to compile:

use static_alloc::unsync::{Bump, BumpSlice};

#[repr(align(32))]
struct HighlyAligned([u8; 128]);

let mut arena: Bump<HighlyAligned> = Bump::uninit();
// Fails here, attempting to resolve `impl Deref for Bump<HighlyAligned>`.
let _ = arena.get::<u32>();

Implementations§

Source§

impl<T> Bump<T>

Source

pub fn uninit() -> Self

Create an allocator with uninitialized memory.

All allocations coming from the allocator will need to be initialized manually.

Source

pub fn zeroed() -> Self

Create an allocator with zeroed memory.

The caller can rely on all allocations to be zeroed.

Source

pub const fn from_maybe_uninit(data: &mut MaybeUninit<Self>) -> &mut Self

Construct a bump allocator into an uninitialized memory location.

This fills in only a constant sized header. The rest of the allocation is left-as, i.e. if remains initialized exactly in those spots the caller may have initialized with external means.

Note that this method is const (though this is not particularly useful yet as of 0.3.0).

§Usage

This method allows Bump to be used together with interfaces that require an outer MaybeUninit for their safety proofs, e.g. [Box::new_uninit_slice].

type Allocator = Bump<[u32; 128]>;

// 4 independent allocators, e.g. for four components of your software.
// Still guaranteed to live in consecutive memory.
let mut allocators = Box::<[Allocator]>::new_uninit_slice(num_components);

// The index here might be a runtime address.
// Now this arena can be used without initializing the others already.
let c0 = Bump::from_maybe_uninit(&mut allocators[0]);
// Etc. Use this temporary stack allocator.
let _ = c0.bump_box::<usize>();

Methods from Deref<Target = BumpSlice>§

Source

pub fn capacity(&self) -> usize

Returns capacity of this BumpSlice. This is how many bytes can be allocated within this node.

Source

pub fn data_ptr(&self) -> NonNull<u8>

Get a raw pointer to the data.

Note that any use of the pointer must be done with extreme care as it may invalidate existing references into the allocated region. Furthermore, bytes may not be initialized. The length of the valid region is BumpSlice::capacity.

Prefer BumpSlice::get_unchecked for reconstructing a prior allocation.

Source

pub fn alloc(&self, layout: Layout) -> Option<NonNull<u8>>

Allocate a region of memory.

This is a safe alternative to GlobalAlloc::alloc.

§Panics

This function will panic if the requested layout has a size of 0. For the use in a GlobalAlloc this is explicitely forbidden to request and would allow any behaviour but we instead strictly check it.

FIXME(breaking): this could well be a Result<_, Failure>.

Source

pub fn alloc_at( &self, layout: Layout, level: Level, ) -> Result<NonNull<u8>, Failure>

Try to allocate some layout with a precise base location.

The base location is the currently consumed byte count, without correction for the alignment of the allocation. This will succeed if it can be allocate exactly at the expected location.

§Panics

This function may panic if the provided level is from a different slab.

Source

pub fn get<V>(&self) -> Option<Allocation<'_, V>>

Get an allocation for a specific type.

It is not yet initialized but provides an interface for that initialization.

§Usage
use core::cell::{Ref, RefCell};

let slab: Bump<[Ref<'static, usize>; 1]> = Bump::uninit();
let data = RefCell::new(0xff);

// We can place a `Ref` here but we did not yet.
let alloc = slab.get::<Ref<usize>>().unwrap();
let cell_ref = unsafe {
    alloc.leak(data.borrow())
};

assert_eq!(**cell_ref, 0xff);

FIXME(breaking): this could well be a Result<_, Failure>.

Source

pub fn get_at<V>(&self, level: Level) -> Result<Allocation<'_, V>, Failure>

Get an allocation for a specific type at a specific level.

See get for usage. This can be used to ensure that data is contiguous in concurrent access to the allocator.

Source

pub unsafe fn get_unchecked<V>(&self, level: Level) -> Allocation<'_, V>

Reacquire an allocation that has been performed previously.

This call won’t invalidate any other allocations.

§Safety

The caller must guarantee that no other pointers to this prior allocation are alive, or can be created. This is guaranteed if the allocation was performed previously, has since been discarded, and reset can not be called (for example, the caller holds a shared reference).

§Usage
// Create an initial allocation.
let level = alloc.level();
let allocation = alloc.get_at::<usize>(level)?;
let address = allocation.ptr.as_ptr() as usize;
// pretend to lose the owning pointer of the allocation.
let _ = { allocation };

// Restore our access.
let renewed = unsafe { alloc.get_unchecked::<usize>(level) };
assert_eq!(address, renewed.ptr.as_ptr() as usize);

Crucially, you can rely on other allocations to stay valid. The caller is responsible of using the returning pointer to only refer to allocations that are not referenced through any other way.

let level = alloc.level();
alloc.get_at::<usize>(level)?;

let other_val = alloc.bump_box()?;
let other_val = LeakBox::write(other_val, 0usize);

let renew = unsafe { alloc.get_unchecked::<usize>(level) };
assert_eq!(*other_val, 0); // Not UB!
Source

pub fn bump_box<'bump, T: 'bump>( &'bump self, ) -> Result<LeakBox<'bump, MaybeUninit<T>>, Failure>

Allocate space for one T without initializing it.

Note that the returned MaybeUninit can be unwrapped from LeakBox. Or you can store an arbitrary value and ensure it is safely dropped before the borrow ends.

§Usage
use core::cell::RefCell;
use static_alloc::leaked::LeakBox;

let slab: Bump<[usize; 4]> = Bump::uninit();
let data = RefCell::new(0xff);

let slot = slab.bump_box().unwrap();
let cell_box = LeakBox::write(slot, data.borrow());

assert_eq!(**cell_box, 0xff);
drop(cell_box);

assert!(data.try_borrow_mut().is_ok());

FIXME(breaking): should return evidence of the level (observed, and post). Something similar to Allocation but containing a LeakBox<T> instead? Introduce that to the sync Bump allocator as well.

FIXME(breaking): align with sync Bump::get (probably rename get to bump_box).

Source

pub fn bump_array<'bump, T: 'bump>( &'bump self, n: usize, ) -> Result<LeakBox<'bump, [MaybeUninit<T>]>, Failure>

Allocate space for a slice of Ts without initializing any.

Retrieve individual MaybeUninit elements and wrap them as a LeakBox to store values. Or use the slice as backing memory for one of the containers from without-alloc. Or manually initialize them.

§Usage

Quicksort, implemented recursively, requires a maximum of log n stack frames in the worst case when implemented optimally. Since each frame is quite large this is wasteful. We can use a properly sized buffer instead and implement an iterative solution. (Left as an exercise to the reader, or see the examples for without-alloc where we use such a dynamic allocation with an inline vector as our stack).

Source

pub fn level(&self) -> Level

Get the number of already allocated bytes.

Source

pub fn reset(&mut self)

Reset the bump allocator.

This requires a unique reference to the allocator hence no allocation can be alive at this point. It will reset the internal count of used bytes to zero.

Trait Implementations§

Source§

impl<T> Allocator for Bump<T>

Source§

fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>

Attempts to allocate a block of memory. Read more
Source§

unsafe fn deallocate(&self, _: NonNull<u8>, _: Layout)

Deallocates the memory referenced by ptr. Read more
Source§

unsafe fn shrink( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, ) -> Result<NonNull<[u8]>, AllocError>

Attempts to shrink the memory block. Read more
Source§

fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>

Behaves like allocate, but also ensures that the returned memory is zero-initialized. Read more
Source§

unsafe fn grow( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, ) -> Result<NonNull<[u8]>, AllocError>

Attempts to extend the memory block. Read more
Source§

unsafe fn grow_zeroed( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, ) -> Result<NonNull<[u8]>, AllocError>

Behaves like grow, but also ensures that the new contents are set to zero before being returned. Read more
Source§

fn by_ref(&self) -> &Self
where Self: Sized,

Creates a “by reference” adapter for this instance of Allocator. Read more
Source§

impl<T> Deref for Bump<T>

Source§

type Target = BumpSlice

The resulting type after dereferencing.
Source§

fn deref(&self) -> &BumpSlice

Dereferences the value.
Source§

impl<T> DerefMut for Bump<T>

Source§

fn deref_mut(&mut self) -> &mut BumpSlice

Mutably dereferences the value.

Auto Trait Implementations§

§

impl<T> !Freeze for Bump<T>

§

impl<T> !RefUnwindSafe for Bump<T>

§

impl<T> !Sync for Bump<T>

§

impl<T> Send for Bump<T>

§

impl<T> Unpin for Bump<T>

§

impl<T> UnsafeUnpin for Bump<T>

§

impl<T> UnwindSafe for Bump<T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.