use std::alloc::{Layout, alloc, dealloc};
use std::cell::Cell;
use std::mem;
use std::ptr;
pub struct Bump {
chunks: Vec<Chunk>,
cursor: Cell<usize>,
}
struct Chunk {
ptr: *mut u8,
layout: Layout,
}
impl Drop for Chunk {
fn drop(&mut self) {
unsafe { dealloc(self.ptr, self.layout) };
}
}
impl Bump {
pub fn new() -> Self {
Self::with_capacity(4096)
}
pub fn with_capacity(initial_bytes: usize) -> Self {
let initial_bytes = initial_bytes.max(64);
let layout = Layout::from_size_align(initial_bytes, 16).expect("layout");
let ptr = unsafe { alloc(layout) };
assert!(!ptr.is_null(), "OOM allocating first arena chunk");
Self {
chunks: vec![Chunk { ptr, layout }],
cursor: Cell::new(0),
}
}
pub fn alloc_copy<T: Copy>(&mut self, value: T) -> &mut T {
let layout = Layout::new::<T>();
let p = self.alloc_raw(layout);
unsafe {
ptr::write(p as *mut T, value);
&mut *(p as *mut T)
}
}
pub fn alloc_raw(&mut self, layout: Layout) -> *mut u8 {
let size = layout.size();
let align = layout.align();
let cur = self.cursor.get();
let aligned = align_up(cur, align);
let end = aligned + size;
let chunk = self.chunks.last().expect("at least one chunk");
if end <= chunk.layout.size() {
self.cursor.set(end);
return unsafe { chunk.ptr.add(aligned) };
}
self.grow(size + align);
let chunk = self.chunks.last().unwrap();
let aligned = align_up(0, align);
self.cursor.set(aligned + size);
unsafe { chunk.ptr.add(aligned) }
}
fn grow(&mut self, min_bytes: usize) {
let last = self.chunks.last().expect("at least one chunk");
let new_size = (last.layout.size() * 2).max(min_bytes);
let layout = Layout::from_size_align(new_size, 16).expect("layout");
let ptr = unsafe { alloc(layout) };
assert!(!ptr.is_null(), "OOM growing arena");
self.chunks.push(Chunk { ptr, layout });
self.cursor.set(0);
}
pub fn reset(&mut self) {
if self.chunks.len() > 1 {
let largest = self
.chunks
.iter()
.enumerate()
.max_by_key(|(_, c)| c.layout.size())
.map(|(i, _)| i)
.unwrap();
let keeper = self.chunks.swap_remove(largest);
self.chunks.clear();
self.chunks.push(keeper);
}
self.cursor.set(0);
}
pub fn total_capacity(&self) -> usize {
self.chunks.iter().map(|c| c.layout.size()).sum()
}
}
impl Default for Bump {
fn default() -> Self {
Self::new()
}
}
fn align_up(p: usize, align: usize) -> usize {
debug_assert!(align.is_power_of_two(), "alignment must be a power of two");
(p + align - 1) & !(align - 1)
}
#[cfg(feature = "harness")]
pub mod recipe;
#[allow(dead_code)]
fn _silence_unused() {
let _ = mem::size_of::<()>();
}