#![no_std]
mod size_align;
use core::cell::UnsafeCell;
use core::fmt;
use core::mem::{self, MaybeUninit};
use core::ptr::{self, NonNull};
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 available_bytes(&'this self) -> usize {
unsafe { (*self.array.get()).len() }
}
pub fn available_spaces<S: Into<usize>, A: Into<usize>>(
&'this self,
size: S,
align: A,
) -> usize {
let size = size.into();
let align = align.into();
if align == 0 {
panic!("alignment must not be zero")
}
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 aligned_end = (current_end / align) * align;
if aligned_end <= array_start {
return 0;
}
let mut available_bytes = aligned_end - array_start;
if available_bytes > isize::max_value() as usize {
available_bytes = isize::max_value() as usize;
}
available_bytes / size
}
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 align == 0 {
panic!("alignment must not be zero")
}
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_space_to_limit_for<T>(&'this self) -> (NonNull<T>, usize) {
let count = self.available_spaces(SizeOf::<T>::new(), AlignOf::<T>::new());
let pointer = self.alloc_space(count * mem::size_of::<T>(), AlignOf::<T>::new());
match NonNull::new(pointer as *mut T) {
Some(pointer) => (pointer, count),
None => (NonNull::dangling(), 0),
}
}
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))
}
}
pub fn alloc_down_with<T, I: IntoIterator<Item = T>>(
&'this mut self,
iter: I,
) -> &'this mut [T] {
unsafe { self.alloc_down_with_shared(iter) }
}
pub unsafe fn alloc_down_with_shared<T, I: IntoIterator<Item = T>>(
&'this self,
iter: I,
) -> &'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 (array_start, current_end) = {
let array = &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();
(array_start, current_end)
};
let aligned_end = (current_end / mem::align_of::<T>()) * mem::align_of::<T>();
if aligned_end <= array_start {
return &mut [];
}
let mut iter = iter.into_iter();
let mut count = 0;
let mut cur_space = aligned_end;
loop {
cur_space -= mem::size_of::<T>();
if cur_space < array_start || iter_and_write(cur_space as *mut T, &mut iter) {
cur_space += mem::size_of::<T>();
return core::slice::from_raw_parts_mut(cur_space as *mut T, count);
}
{
let array = &mut *self.array.get();
*array = &mut array[..cur_space - array_start];
}
count += 1;
}
}
}
impl<'a> fmt::Debug for BumpInto<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "BumpInto {{ {} bytes free }}", self.available_bytes())
}
}
#[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")
}
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 available_bytes() {
let mut space = space!(32);
{
let mut bump_into = BumpInto::new(&mut space[..]);
assert_eq!(bump_into.available_bytes(), 32);
assert_eq!(bump_into.available_spaces(1usize, 1usize), 32);
bump_into.alloc(0u8).expect("allocation 1 failed");
assert_eq!(bump_into.available_bytes(), 31);
assert_eq!(bump_into.available_spaces(1usize, 1usize), 31);
let spaces_for_u32 = bump_into.available_spaces(4usize, 4usize);
bump_into.alloc(0u32).expect("allocation 2 failed");
assert_eq!(
bump_into.available_spaces(4usize, 4usize),
spaces_for_u32 - 1
);
{
let rest = bump_into.alloc_down_with(core::iter::repeat(0u32));
assert_eq!(rest.len(), spaces_for_u32 - 1);
assert!(rest.len() >= 6);
}
assert_eq!(bump_into.available_spaces(4usize, 4usize), 0);
assert!(bump_into.available_bytes() < 4);
}
{
let bump_into = BumpInto::new(&mut space[..]);
assert_eq!(bump_into.available_bytes(), 32);
assert_eq!(bump_into.available_spaces(1usize, 1usize), 32);
let something4 = bump_into.alloc(0u8).expect("allocation 4 failed");
assert_eq!(*something4, 0);
let (pointer, count) = bump_into.alloc_space_to_limit_for::<i64>();
assert_eq!(bump_into.available_spaces(8usize, 8usize), 0);
assert!(bump_into.available_bytes() < 8);
assert!(count >= 3);
let pointer = pointer.as_ptr();
for x in 0..count {
unsafe {
core::ptr::write(pointer.add(x), -1);
}
}
assert_eq!(*something4, 0);
}
{
let bump_into = BumpInto::new(&mut space[..]);
assert_eq!(bump_into.available_bytes(), 32);
assert_eq!(bump_into.available_spaces(1usize, 1usize), 32);
let something6 = bump_into.alloc(0u8).expect("allocation 6 failed");
assert_eq!(*something6, 0);
let rest = unsafe {
let mut count = 0;
bump_into.alloc_down_with_shared(core::iter::from_fn(|| {
if bump_into.available_spaces(4usize, 4usize) > 1 {
count += 1;
Some(count)
} else {
None
}
}))
};
assert_eq!(bump_into.available_spaces(4usize, 4usize), 1);
assert!(rest.len() >= 6);
bump_into.alloc(0u32).expect("allocation 8 failed");
assert_eq!(bump_into.available_spaces(4usize, 4usize), 0);
assert!(bump_into.available_bytes() < 4);
}
}
#[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]);
}
}