use std::cell::Cell;
use std::ptr::NonNull;
pub struct BumpAllocator {
inner: bumpalo::Bump,
allocation_count: Cell<usize>,
}
impl BumpAllocator {
#[must_use]
pub fn new() -> Self {
Self {
inner: bumpalo::Bump::new(),
allocation_count: Cell::new(0),
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
inner: bumpalo::Bump::with_capacity(capacity),
allocation_count: Cell::new(0),
}
}
#[inline]
pub fn alloc<T>(&self, value: T) -> &mut T {
self.allocation_count.set(self.allocation_count.get() + 1);
self.inner.alloc(value)
}
#[inline]
pub fn alloc_slice_copy<T: Copy>(&self, values: &[T]) -> &mut [T] {
self.allocation_count.set(self.allocation_count.get() + 1);
self.inner.alloc_slice_copy(values)
}
#[inline]
pub fn alloc_slice_clone<T: Clone>(&self, values: &[T]) -> &mut [T] {
self.allocation_count.set(self.allocation_count.get() + 1);
self.inner.alloc_slice_clone(values)
}
#[inline]
pub fn alloc_str(&self, s: &str) -> &mut str {
self.allocation_count.set(self.allocation_count.get() + 1);
self.inner.alloc_str(s)
}
#[inline]
pub fn alloc_layout(&self, layout: std::alloc::Layout) -> NonNull<u8> {
self.allocation_count.set(self.allocation_count.get() + 1);
self.inner.alloc_layout(layout)
}
#[inline]
pub fn reset(&mut self) {
self.inner.reset();
self.allocation_count.set(0);
}
#[must_use]
#[inline]
pub fn allocated_bytes(&self) -> usize {
self.inner.allocated_bytes()
}
#[must_use]
#[inline]
pub fn allocation_count(&self) -> usize {
self.allocation_count.get()
}
#[must_use]
#[inline]
pub fn chunk_count(&mut self) -> usize {
self.inner.iter_allocated_chunks().count()
}
}
impl Default for BumpAllocator {
fn default() -> Self {
Self::new()
}
}
pub struct ScopedBump<'a> {
bump: &'a mut BumpAllocator,
start_bytes: usize,
}
impl<'a> ScopedBump<'a> {
pub fn new(bump: &'a mut BumpAllocator) -> Self {
let start_bytes = bump.allocated_bytes();
Self { bump, start_bytes }
}
#[inline]
pub fn alloc<T>(&self, value: T) -> &mut T {
self.bump.alloc(value)
}
#[must_use]
pub fn scope_allocated_bytes(&self) -> usize {
self.bump.allocated_bytes() - self.start_bytes
}
}
impl Drop for ScopedBump<'_> {
fn drop(&mut self) {
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bump_basic_allocation() {
let bump = BumpAllocator::new();
let a = bump.alloc(42u64);
assert_eq!(*a, 42);
let b = bump.alloc_str("hello");
assert_eq!(b, "hello");
}
#[test]
fn test_bump_slice_allocation() {
let bump = BumpAllocator::new();
let slice = bump.alloc_slice_copy(&[1, 2, 3, 4, 5]);
assert_eq!(slice, &[1, 2, 3, 4, 5]);
slice[0] = 10;
assert_eq!(slice[0], 10);
}
#[test]
fn test_bump_string_allocation() {
let bump = BumpAllocator::new();
let s = bump.alloc_str("hello world");
assert_eq!(s, "hello world");
}
#[test]
fn test_bump_reset() {
let mut bump = BumpAllocator::new();
for _ in 0..100 {
bump.alloc(42u64);
}
let bytes_before = bump.allocated_bytes();
assert!(bytes_before > 0);
assert_eq!(bump.allocation_count(), 100);
bump.reset();
assert_eq!(bump.allocation_count(), 0);
}
#[test]
fn test_bump_with_capacity() {
let bump = BumpAllocator::with_capacity(1024);
assert_eq!(bump.allocation_count(), 0);
for _ in 0..10 {
bump.alloc(42u64);
}
assert_eq!(bump.allocation_count(), 10);
}
#[test]
fn test_scoped_bump() {
let mut bump = BumpAllocator::new();
bump.alloc(1u64);
let outer_allocs = bump.allocation_count();
{
let scope = ScopedBump::new(&mut bump);
scope.alloc(2u64);
scope.alloc(3u64);
}
assert_eq!(bump.allocation_count(), outer_allocs + 2);
}
#[test]
fn test_bump_many_small_allocations() {
let bump = BumpAllocator::new();
for i in 0u64..10_000u64 {
let v = bump.alloc(i);
assert_eq!(*v, i);
}
assert_eq!(bump.allocation_count(), 10_000);
}
}