I've been working on a crate called bump-into for a while and using it in a project that's currently private. bump-into is a no-std, no-alloc crate providing a bump allocator over a chunk of memory that may be situated anywhere you can move a Rust value to, most importantly the stack. It can be used with no unsafe
; here's the usage example from the README:
use bump_into::{self, BumpInto};
// allocate 64 bytes of uninitialized space on the stack
let mut bump_into_space = bump_into::space!(64);
let bump_into = BumpInto::from_slice(&mut bump_into_space[..]);
// allocating an object produces a mutable reference with
// the same lifetime as the `BumpInto` instance, or gives
// back its argument in `Err` if there isn't enough space
let number: &mut u64 = bump_into
.alloc_with(|| 123)
.ok()
.expect("not enough space");
assert_eq!(*number, 123);
*number = 50000;
assert_eq!(*number, 50000);
// slices can be allocated as well
let slice: &mut [u16] = bump_into
.alloc_n_with(5, core::iter::repeat(10))
.expect("not enough space");
assert_eq!(slice, &[10; 5]);
slice[2] = 100;
assert_eq!(slice, &[10, 10, 100, 10, 10]);
This isn't something the language really provides for, and I'd like to see what people think about how sound my solution is.
Unlike a chunk of memory allocated using the alloc
APIs, a chunk of memory on the stack needs to have a type in Rust. This is a problem if it's going to be written over with values of other arbitrary types, because reinterpreting the bytes of non-repr(C)
values is undefined behavior.
I avoid reinterpreting bytes by giving the chunk of memory a type of either MaybeUninit
or [MaybeUninit; N]
(depending on which API is being used to allocate the space). When a BumpInto
is constructed, it takes a pointer to the chunk of memory and turns it into a &mut [MaybeUninit<u8>]
, which is then carved up. The allocation methods are meant to respect alignment, and to avoid letting live mutable references point to overlapping regions of memory.
Hopefully that introduces the design and doesn't read too badly. For more insight, you can read the docs and the source. The tests currently fail under miri because of issue #1074. Values stored in the allocated spaces are never dropped (which I should put in the docs, now that I think about it).
So what do you think? Is the basic idea sound? There are a lot of moving parts here that could lead to unsoundness, and if anyone is interested in poking around in the source for fatal mistakes, I would appreciate that, too.