#![no_std]
mod size_align;
use core::cell::UnsafeCell;
use core::mem::{self, MaybeUninit};
use core::ptr;
pub use size_align::{AlignOf, SizeOf};
pub struct BumpInto<'a> {
array: UnsafeCell<&'a mut [MaybeUninit<u8>]>,
}
impl<'this, 'a: 'this> BumpInto<'a> {
pub fn new(array: &'a mut [MaybeUninit<u8>]) -> Self {
BumpInto {
array: UnsafeCell::new(array),
}
}
pub fn alloc_space<S: Into<usize>, A: Into<usize>>(
&'this self,
size: S,
align: A,
) -> *mut MaybeUninit<u8> {
let size = size.into();
let align = align.into();
if size == 0 {
return align as *mut MaybeUninit<u8>;
}
if size > isize::max_value() as usize {
return ptr::null_mut();
}
let array = unsafe { &mut *self.array.get() };
let array_start = *array as *mut [MaybeUninit<u8>] as *mut MaybeUninit<u8> as usize;
let current_end = array_start + array.len();
let preferred_ptr = match current_end.checked_sub(size) {
Some(preferred_ptr) => preferred_ptr,
None => return ptr::null_mut(),
};
let aligned_ptr = (preferred_ptr / align) * align;
if aligned_ptr < array_start {
ptr::null_mut()
} else {
*array = &mut array[..aligned_ptr - array_start];
aligned_ptr as *mut MaybeUninit<u8>
}
}
pub fn alloc_space_for<T>(&'this self) -> *mut T {
self.alloc_space(SizeOf::<T>::new(), AlignOf::<T>::new()) as *mut T
}
pub fn alloc_space_for_n<T>(&'this self, count: usize) -> *mut T {
let size = mem::size_of::<T>().checked_mul(count);
match size {
Some(size) => self.alloc_space(size, AlignOf::<T>::new()) as *mut T,
None => ptr::null_mut(),
}
}
pub fn alloc<T>(&'this self, x: T) -> Result<&'this mut T, T> {
let pointer = self.alloc_space_for::<T>();
if pointer.is_null() {
return Err(x);
}
unsafe {
ptr::write(pointer, x);
Ok(&mut *pointer)
}
}
pub fn alloc_with<T, F: FnOnce() -> T>(&'this self, f: F) -> Option<&'this mut T> {
#[inline(always)]
unsafe fn eval_and_write<T, F: FnOnce() -> T>(pointer: *mut T, f: F) {
ptr::write(pointer, f());
}
let pointer = self.alloc_space_for::<T>();
if pointer.is_null() {
return None;
}
unsafe {
eval_and_write(pointer, f);
Some(&mut *pointer)
}
}
pub fn alloc_n<T: Copy>(&'this self, xs: &[T]) -> Option<&'this mut [T]> {
let pointer = self.alloc_space(mem::size_of_val(xs), AlignOf::<T>::new()) as *mut T;
if pointer.is_null() {
return None;
}
unsafe {
for (index, &x) in xs.iter().enumerate() {
ptr::write(pointer.add(index), x);
}
Some(core::slice::from_raw_parts_mut(pointer, xs.len()))
}
}
pub fn alloc_n_with<T, I: IntoIterator<Item = T>>(
&'this self,
count: usize,
iter: I,
) -> Option<&'this mut [T]> {
#[inline(always)]
unsafe fn iter_and_write<T, I: Iterator<Item = T>>(pointer: *mut T, mut iter: I) -> bool {
match iter.next() {
Some(item) => {
ptr::write(pointer, item);
false
}
None => true,
}
}
let pointer = self.alloc_space_for_n::<T>(count);
if pointer.is_null() {
return None;
}
let mut iter = iter.into_iter();
unsafe {
for index in 0..count {
if iter_and_write(pointer.add(index), &mut iter) {
return Some(core::slice::from_raw_parts_mut(pointer, index));
}
}
Some(core::slice::from_raw_parts_mut(pointer, count))
}
}
}
#[macro_export]
macro_rules! space {
($capacity:expr) => {{
extern crate core;
unsafe {
core::mem::MaybeUninit::<[core::mem::MaybeUninit<u8>; $capacity]>::uninit()
.assume_init()
}
}};
}
#[macro_export]
macro_rules! space_zeroed {
($capacity:expr) => {{
extern crate core;
unsafe {
core::mem::MaybeUninit::<[core::mem::MaybeUninit<u8>; $capacity]>::zeroed()
.assume_init()
}
}};
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn alloc() {
let mut space = space!(32);
let bump_into = BumpInto::new(&mut space[..]);
let something1 = bump_into.alloc(123u64).expect("allocation 1 failed");
assert_eq!(*something1, 123u64);
let something2 = bump_into.alloc(7775u16).expect("allocation 2 failed");
assert_eq!(*something1, 123u64);
assert_eq!(*something2, 7775u16);
let something3 = bump_into
.alloc_with(|| 251222u64)
.expect("allocation 3 failed");
assert_eq!(*something1, 123u64);
assert_eq!(*something2, 7775u16);
assert_eq!(*something3, 251222u64);
if bump_into.alloc_with(|| [0; 128]).is_some() {
panic!("allocation 4 succeeded");
}
let something5 = bump_into.alloc(123523u32).expect("allocation 5 failed");
assert_eq!(*something1, 123u64);
assert_eq!(*something2, 7775u16);
assert_eq!(*something3, 251222u64);
assert_eq!(*something5, 123523u32);
}
#[test]
fn alloc_n() {
let mut space = space!(192);
let bump_into = BumpInto::new(&mut space[..]);
let something1 = bump_into.alloc_n(&[1u32, 258909, 1000][..]).expect("allocation 1 failed");
assert_eq!(something1, &[1u32, 258909, 1000][..]);
let something2 = bump_into.alloc_n(&[1u64, 258909, 1000, 0][..]).expect("allocation 2 failed");
assert_eq!(something1, &[1u32, 258909, 1000][..]);
assert_eq!(something2, &[1u64, 258909, 1000, 0][..]);
let something3 = bump_into.alloc_n_with(5, core::iter::repeat(61921u16)).expect("allocation 3 failed");
assert_eq!(something1, &[1u32, 258909, 1000][..]);
assert_eq!(something2, &[1u64, 258909, 1000, 0][..]);
assert_eq!(something3, &[61921u16; 5][..]);
let something4 = bump_into.alloc_n_with(6, core::iter::once(71u64)).expect("allocation 4 failed");
assert_eq!(something1, &[1u32, 258909, 1000][..]);
assert_eq!(something2, &[1u64, 258909, 1000, 0][..]);
assert_eq!(something3, &[61921u16; 5][..]);
assert_eq!(something4, &[71u64][..]);
if bump_into.alloc_n_with::<u64, _>(100, None).is_some() {
panic!("allocation 5 succeeded")
}
assert_eq!(something1, &[1u32, 258909, 1000][..]);
assert_eq!(something2, &[1u64, 258909, 1000, 0][..]);
assert_eq!(something3, &[61921u16; 5][..]);
assert_eq!(something4, &[71u64][..]);
let something6 = bump_into.alloc_n_with::<u64, _>(6, None).expect("allocation 6 failed");
assert_eq!(something1, &[1u32, 258909, 1000][..]);
assert_eq!(something2, &[1u64, 258909, 1000, 0][..]);
assert_eq!(something3, &[61921u16; 5][..]);
assert_eq!(something4, &[71u64][..]);
assert_eq!(something6, &[]);
}
#[test]
fn readme_example() {
let mut bump_into_space = space!(64);
let bump_into = BumpInto::new(&mut bump_into_space[..]);
let number: &mut u64 = bump_into
.alloc_with(|| 123)
.expect("not enough space");
assert_eq!(*number, 123);
*number = 50000;
assert_eq!(*number, 50000);
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]);
}
}