use bumpalo::Bump;
pub const DEFAULT_INITIAL_CAPACITY: usize = 16 * 1024;
pub struct RequestArena {
bump: Bump,
}
impl RequestArena {
#[must_use]
pub fn new() -> Self {
Self::with_capacity(DEFAULT_INITIAL_CAPACITY)
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
bump: Bump::with_capacity(capacity),
}
}
#[must_use]
pub fn bump(&self) -> &Bump {
&self.bump
}
#[must_use]
pub fn bytes_allocated(&self) -> usize {
self.bump.allocated_bytes()
}
pub fn chunk_count(&mut self) -> usize {
self.bump.iter_allocated_chunks().count()
}
pub fn reset(&mut self) {
self.bump.reset();
}
pub fn alloc<T>(&self, val: T) -> &mut T {
self.bump.alloc(val)
}
pub fn alloc_slice_copy<T: Copy>(&self, src: &[T]) -> &mut [T] {
self.bump.alloc_slice_copy(src)
}
}
impl Default for RequestArena {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for RequestArena {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RequestArena")
.field("bytes_allocated", &self.bytes_allocated())
.finish()
}
}
pub type ArenaVec<'a, T> = bumpalo::collections::Vec<'a, T>;
pub type ArenaString<'a> = bumpalo::collections::String<'a>;
#[must_use]
pub fn vec_with_capacity_in<T>(capacity: usize, arena: &RequestArena) -> ArenaVec<'_, T> {
bumpalo::collections::Vec::with_capacity_in(capacity, arena.bump())
}
#[must_use]
pub fn string_with_capacity_in(capacity: usize, arena: &RequestArena) -> ArenaString<'_> {
bumpalo::collections::String::with_capacity_in(capacity, arena.bump())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn arena_allocates_and_reports_bytes() {
let arena = RequestArena::with_capacity(1024);
let initial = arena.bytes_allocated();
let mut buf = vec_with_capacity_in::<u64>(64, &arena);
for i in 0..64u64 {
buf.push(i);
}
assert_eq!(buf.len(), 64);
assert!(arena.bytes_allocated() >= initial);
}
#[test]
fn arena_alloc_returns_writable_reference() {
let arena = RequestArena::new();
let v = arena.alloc(42_u32);
*v += 1;
assert_eq!(*v, 43);
}
#[test]
fn arena_alloc_slice_copy() {
let arena = RequestArena::new();
let src = [1u32, 2, 3, 4, 5];
let dst = arena.alloc_slice_copy(&src);
assert_eq!(dst, &[1, 2, 3, 4, 5]);
dst[0] = 99;
assert_eq!(dst[0], 99);
}
#[test]
fn arena_reset_keeps_capacity() {
let mut arena = RequestArena::with_capacity(2048);
{
let mut buf = vec_with_capacity_in::<u8>(1000, &arena);
for _ in 0..1000 {
buf.push(0);
}
}
let pre_reset = arena.bytes_allocated();
arena.reset();
assert!(arena.bytes_allocated() > 0);
assert!(arena.bytes_allocated() <= pre_reset);
}
#[test]
fn arena_string_helper() {
let arena = RequestArena::new();
let mut s = string_with_capacity_in(32, &arena);
s.push_str("hello, ");
s.push_str("arena");
assert_eq!(s.as_str(), "hello, arena");
}
#[test]
fn arena_default_constructs() {
let a = RequestArena::default();
assert!(a.bytes_allocated() < DEFAULT_INITIAL_CAPACITY * 4);
}
#[test]
fn arena_debug_format_includes_bytes() {
let arena = RequestArena::new();
let s = format!("{arena:?}");
assert!(s.contains("RequestArena"));
assert!(s.contains("bytes_allocated"));
}
#[test]
fn arena_supports_many_small_allocations() {
let arena = RequestArena::with_capacity(64);
let mut refs = Vec::new();
for i in 0..1000u32 {
refs.push(arena.alloc(i));
}
for (i, r) in refs.iter().enumerate() {
assert_eq!(**r, i as u32);
}
}
}