use core::alloc::Layout;
use core::cell::Cell;
use core::cmp::max;
use core::ptr::NonNull;
use allocator_api2::alloc::{AllocError, Allocator};
#[derive(Default)]
pub struct Bump {
cur: Cell<Option<NonNull<Footer>>>,
}
struct Footer {
start: NonNull<u8>,
pos: Cell<NonNull<u8>>,
prev: Option<NonNull<Footer>>,
layout: Layout,
}
const FOOTER_SIZE: usize = core::mem::size_of::<Footer>();
unsafe impl Allocator for Bump {
fn allocate(&self, layout: allocator_api2::alloc::Layout) -> Result<NonNull<[u8]>, AllocError> {
unsafe {
match self.cur.get() {
None => {
const START_SIZE: usize = 4096;
self.alloc_chunk(START_SIZE, layout)
}
Some(mut footer) => {
let pos = footer.as_ref().pos.get();
let offset = pos.align_offset(layout.align());
let pos = pos.add(offset);
let new_pos = pos.add(layout.size());
if new_pos <= footer.cast() {
footer.as_mut().pos.set(new_pos);
Ok(NonNull::slice_from_raw_parts(pos, layout.size()))
} else {
self.alloc_chunk(footer.as_ref().layout.size() * 2, layout)
}
}
}
}
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
}
}
impl Bump {
unsafe fn alloc_chunk(&self, start_size: usize, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let mut size = start_size;
let needed = layout.size() + FOOTER_SIZE;
while size < needed {
size <<= 1;
}
let align = max(layout.align(), 16);
let chunk_layout = Layout::from_size_align(size, align).map_err(|_| AllocError)?;
let start = NonNull::new_unchecked(alloc::alloc::alloc(chunk_layout));
let pos = start.add(layout.size());
let end = start.add(size);
let footer = end.sub(FOOTER_SIZE).cast::<Footer>();
assert_eq!(0, footer.align_offset(align_of::<Footer>()));
footer.write(Footer {
start,
pos: Cell::new(pos),
prev: self.cur.get(),
layout: chunk_layout,
});
self.cur.set(Some(footer));
Ok(NonNull::slice_from_raw_parts(start, layout.size()))
}
}
impl Drop for Bump {
fn drop(&mut self) {
let mut maybe_footer = self.cur.get();
while let Some(footer) = maybe_footer {
let footer = unsafe { footer.as_ref() };
maybe_footer = footer.prev;
unsafe {
alloc::alloc::dealloc(footer.start.as_ptr(), footer.layout);
}
}
}
}