Skip to main content

BumpSlice

Struct BumpSlice 

Source
pub struct BumpSlice { /* private fields */ }
Expand description

An unsized bump allocator arena.

This does not enforce any particular alignment on its storage. You can, in general, expect that it is at least 4-byte aligned but should not rely on it for soundness purposes.

Implementations§

Source§

impl BumpSlice

Source

pub fn from_memory(data: &mut [MaybeUninit<u8>]) -> Option<&mut Self>

Construct a bump allocator into an existing dynamically sized arena of memory.

§Usage

This way you may re-use storage from some arbitrary existing span of memory, provided it has at least enough room to hold an aligned header.

use core::mem::MaybeUninit;
use static_alloc::bump::BumpSlice;

let mut buffer = MaybeUninit::<[u8; 256]>::uninit();
let bump = BumpSlice::from_memory(buffer.as_mut())?;

// Slightly less than 256 free bytes of memory to use.
// Exact number is unstable and depends on the align of `buffer`.
let allocated_slice = bump.get_slice::<u32>(50)?;
Source

pub fn reset(&mut self)

Reset the bump allocator.

Requires a mutable reference, as no allocations can be active when doing it. This behaves as if a fresh instance was assigned but it does not overwrite the bytes in the backing storage. (You can unsafely rely on this).

§Usage
let mut stack_buf = Bump::<usize>::uninit();
let stack_buf = stack_buf.as_mut_bump_slice().unwrap();

let bytes = stack_buf.leak(0usize.to_be_bytes()).unwrap();
// Now the bump allocator is full.
assert!(stack_buf.leak(0u8).is_err());

// We can reuse if we are okay with forgetting the previous value.
stack_buf.reset();
let val = stack_buf.leak(0usize).unwrap();

Trying to use the previous value does not work, as the stack is still borrowed. Note that any user unsafely tracking the lifetime must also ensure this through proper lifetimes that guarantee that borrows are alive for appropriate times.

// error[E0502]: cannot borrow `stack_buf` as mutable because it is also borrowed as immutable
let mut stack_buf = Bump::<usize>::uninit();
let stack_buf = stack_buf.as_mut_bump_slice().unwrap();

let bytes = stack_buf.leak(0usize).unwrap();
//          --------- immutably borrow occurs here
stack_buf.reset();
// ^^^^^^^ mutable borrow occurs here.
let other = stack_buf.leak(0usize).unwrap();

*bytes += *other;
// ------------- immutable borrow later used here
Source

pub const fn capacity(&self) -> usize

Returns capacity of this allocator.

This is how many bytes can be allocated within this allocator in total, with no information about the currently consumed count.

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 Self::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.

Source

pub fn alloc_at( &self, layout: Layout, level: Level, ) -> Result<Allocation<'_>, 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_layout(&self, layout: Layout) -> Option<Allocation<'_>>

Get an allocation with detailed layout.

Provides an Uninit wrapping several aspects of initialization in a safe interface, bound by the lifetime of the reference to the allocator.

Source

pub fn get_layout_at( &self, layout: Layout, at: Level, ) -> Result<Allocation<'_>, Failure>

Get an allocation with detailed layout at a specific level.

Provides an Uninit wrapping several aspects of initialization in a safe interface, bound by the lifetime of the reference to the allocator.

Since the underlying allocation is the same, it would be unsafe but justified to fuse this allocation with the preceding or succeeding one.

Source

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

Get an allocation for a specific type.

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

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

let backing: Bump<[Ref<'static, usize>; 1]> = Bump::uninit();
let slab = backing.as_bump_slice().unwrap();

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);
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.

Source

pub fn get_slice<V>(&self, len: usize) -> Option<Allocation<'_, [V]>>

Get an allocation for a slice of a type.

Returns None if the allocation fails (see Self::get) or if the slice layout can not be computed due to an overflow with this size.

§Examples

let backing: Bump<[usize; 6]> = Bump::uninit();
let slab = backing.as_bump_slice().unwrap();

let first = slab.get_slice::<usize>(4).unwrap();
let second = slab.get_slice::<usize>(2).unwrap();
assert!(slab.get_slice::<usize>(1).is_none());

assert_eq!(first.ptr.len(), 4);
assert_eq!(second.ptr.len(), 2);

let backing: Bump<[usize; 1]> = Bump::uninit();
let slab = backing.as_bump_slice().unwrap();

let lots_of_empty = slab.get_slice::<()>(usize::MAX).unwrap();
assert_eq!(lots_of_empty.ptr.len(), usize::MAX);

let backing: Bump<[usize; 1]> = Bump::uninit();
let slab = backing.as_bump_slice().unwrap();

let _exhaust = slab.get_slice::<usize>(1).unwrap();
assert!(slab.get_slice::<usize>(1).is_none());
let empty_slice = slab.get_slice::<usize>(0).unwrap();
Source

pub fn leak_box<V>(&self, val: V) -> Option<LeakBox<'_, V>>

Move a value into an owned allocation.

For safely initializing a value after a successful allocation, see LeakBox::write.

§Usage

This can be used to push the value into a caller provided stack buffer where it lives longer than the current stack frame. For example, you might create a linked list with a dynamic number of values living in the frame below while still being dropped properly. This is impossible to do with a return value.

fn rand() -> usize { 4 }

enum Chain<'buf, T> {
   Tail,
   Link(T, LeakBox<'buf, Self>),
}

fn make_chain<T>(buf: &BumpSlice, mut new_node: impl FnMut() -> T)
    -> Option<Chain<'_, T>>
{
    let count = rand();
    let mut chain = Chain::Tail;
    for _ in 0..count {
        let node = new_node();
        chain = Chain::Link(node, buf.leak_box(chain)?);
    }
    Some(chain)
}

struct Node (usize);
impl Drop for Node {
    fn drop(&mut self) {
        println!("Dropped {}", self.0);
    }
}
let mut counter = 0..;
let new_node = || Node(counter.next().unwrap());

let buffer: Bump<[u8; 128]> = Bump::uninit();
let buffer = buffer.as_bump_slice().unwrap();
let head = make_chain(buffer, new_node).unwrap();

// Prints the message in reverse order.
// Dropped 3
// Dropped 2
// Dropped 1
// Dropped 0
drop(head);
Source

pub fn leak_box_at<V>( &self, val: V, level: Level, ) -> Result<LeakBox<'_, V>, Failure>

Move a value into an owned allocation.

See leak_box for usage.

Source

pub fn level(&self) -> Level

Observe the current level.

Keep in mind that concurrent usage of the same slab may modify the level before you are able to use it in alloc_at. Calling this method provides also no other guarantees on synchronization of memory accesses, only that the values observed by the caller are a monotonically increasing seequence while a shared reference exists.

Source

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

Get a pointer to an existing allocation at a specific level.

The resulting pointer may be used to access an arbitrary allocation starting at the pointer (i.e. including additional allocations immediately afterwards) but the caller is responsible for ensuring that these accesses do not overlap other accesses. There must be no more life LeakBox to any allocation being accessed this way.

§Safety
  • The level must refer to an existing allocation, i.e. it must previously have been returned in Allocation::level.
  • As a corollary, particular it must be in-bounds of the allocator’s memory.
  • Another consequence, the result pointer must be aligned for the requested type.
Source

pub fn leak<V>(&self, val: V) -> Result<&mut V, LeakError<V>>

Allocate a value for the lifetime of the allocator.

The value is leaked in the sense that

  1. the drop implementation of the allocated value is never called;
  2. reusing the memory for another allocation in the same Bump requires manual unsafe code to handle dropping and reinitialization.

However, it does not mean that the underlying memory used for the allocated value is never reclaimed. If the Bump itself is a stack value then it will get reclaimed together with it.

§Safety notice

It is important to understand that it is undefined behaviour to reuse the allocation for the whole lifetime of the returned reference. That is, dropping the allocation in-place while the reference is still within its lifetime comes with the exact same unsafety caveats as ManuallyDrop::drop.

#[derive(Debug, Default)]
struct FooBar {
    // ...
}

let local: Bump<[FooBar; 3]> = Bump::uninit();
let local = local.as_bump_slice().unwrap();
let one = local.leak(FooBar::default()).unwrap();

// Dangerous but justifiable.
let one = unsafe {
    // Ensures there is no current mutable borrow.
    core::ptr::drop_in_place(&mut *one);
};
§Usage
use static_alloc::bump::{Bump, BumpSlice};

let local: Bump<[u64; 3]> = Bump::uninit();
let local = local.as_bump_slice().unwrap();

let one = local.leak(0_u64).unwrap();
assert_eq!(*one, 0);
*one = 42;
§Limitations

Only sized values can be allocated in this manner for now, unsized values are blocked on stabilization of ptr::slice_from_raw_parts. We can not otherwise get a fat pointer to the allocated region.

TODO: will be deprecated sooner or later in favor of a method that does not move the resource on failure.

Source

pub fn leak_at<V>( &self, val: V, level: Level, ) -> Result<(&mut V, Level), LeakError<V>>

Allocate a value with a precise location.

See leak for basics on allocation of values.

The level is an identifer for a base location (more at level). This will succeed if it can be allocate exactly at the expected location.

This method will return the new level of the slab allocator. A next allocation at the returned level will be placed next to this allocation, only separated by necessary padding from alignment. In particular, this is the same strategy as applied for the placement of #[repr(C)] struct members. (Except for the final padding at the last member to the full struct alignment.)

§Usage
use static_alloc::bump::{Bump, BumpSlice};

let local: Bump<[u64; 3]> = Bump::uninit();
let local = local.as_bump_slice().unwrap();

let base = local.level();
let (one, level) = local.leak_at(1_u64, base).unwrap();
// Will panic when an allocation happens in between.
let (two, _) = local.leak_at(2_u64, level).unwrap();

assert_eq!((one as *const u64).wrapping_offset(1), two);

TODO: will be deprecated sooner or later in favor of a method that does not move the resource on failure.

Trait Implementations§

Source§

impl Allocator for BumpSlice

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§

impl GlobalAlloc for &'static BumpSlice

Source§

unsafe fn alloc(&self, layout: Layout) -> *mut u8

Allocates memory as described by the given layout. Read more
Source§

unsafe fn realloc( &self, ptr: *mut u8, current: Layout, new_size: usize, ) -> *mut u8

Shrinks or grows a block of memory to the given new_size in bytes. The block is described by the given ptr pointer and layout. Read more
Source§

unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout)

Deallocates the block of memory at the given ptr pointer with the given layout. Read more
1.28.0 · Source§

unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8

Behaves like alloc, but also ensures that the contents are set to zero before being returned. Read more

Auto Trait Implementations§

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