use super::chunk::Chunk;
use alloc::alloc::Layout;
use core::ptr;
use core::ptr::NonNull;
unsafe fn allocate_in_chunk(
layout: Layout,
chunk: &Chunk,
offset: &mut usize,
) -> NonNull<[u8]> {
let new_offset = (*offset - layout.size()) & !(layout.align() - 1);
let storage: NonNull<u8> = chunk.storage();
let start = unsafe { storage.as_ptr().add(new_offset) };
let len = *offset - new_offset;
*offset = new_offset;
let ptr = ptr::slice_from_raw_parts_mut(start, len);
unsafe { NonNull::new_unchecked(ptr) }
}
pub struct BumpInner<L: Copy + Into<Layout>> {
chunk: Option<Chunk>,
offset: usize,
layout: L,
}
impl<L: Copy + Into<Layout>> BumpInner<L> {
pub fn new(layout: L) -> Self {
Self {
chunk: None,
offset: 0,
layout,
}
}
pub fn layout(&self) -> Layout {
self.layout.into()
}
fn chunk_size(&self) -> usize {
Chunk::layout(self.layout()).size()
}
fn chunk_align(&self) -> usize {
Chunk::layout(self.layout()).align()
}
pub fn allocate(&mut self, layout: Layout) -> Option<NonNull<[u8]>> {
if layout.align() > self.chunk_align() {
return None;
}
if let Some(chunk) = self.chunk.as_mut() {
if self.offset >= layout.size() {
return Some(unsafe {
allocate_in_chunk(layout, chunk, &mut self.offset)
});
}
}
if layout.size() > self.chunk_size() {
return None;
}
let chunk_size = self.chunk_size();
let chunk = self.chunk.take();
let chunk = self.chunk.insert(Chunk::new(self.layout(), chunk)?);
self.offset = chunk_size;
Some(unsafe { allocate_in_chunk(layout, chunk, &mut self.offset) })
}
}
impl<L: Copy + Into<Layout>> Drop for BumpInner<L> {
fn drop(&mut self) {
let mut tail = self.chunk.take();
while let Some(mut chunk) = tail {
let prev = chunk.take_prev();
unsafe {
chunk.drop(self.layout());
}
tail = prev;
}
}
}