use core::cell::Cell;
pub const DEFAULT_ARENA_SIZE: usize = 64 * 1024;
#[derive(Debug)]
pub struct Arena {
buffer: Vec<u8>,
pos: Cell<usize>,
peak_usage: Cell<usize>,
}
impl Arena {
pub fn new(capacity: usize) -> Self {
Self {
buffer: vec![0; capacity],
pos: Cell::new(0),
peak_usage: Cell::new(0),
}
}
pub fn with_default_size() -> Self {
Self::new(DEFAULT_ARENA_SIZE)
}
#[inline]
pub fn reset(&self) {
let current = self.pos.get();
if current > self.peak_usage.get() {
self.peak_usage.set(current);
}
self.pos.set(0);
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub fn alloc_slice(&self, len: usize) -> Option<&mut [u8]> {
let pos = self.pos.get();
let new_pos = pos.checked_add(len)?;
if new_pos > self.buffer.len() {
return None;
}
self.pos.set(new_pos);
unsafe {
let ptr = self.buffer.as_ptr().add(pos) as *mut u8;
Some(core::slice::from_raw_parts_mut(ptr, len))
}
}
#[inline]
pub fn alloc_slice_zeroed(&self, len: usize) -> Option<&mut [u8]> {
let slice = self.alloc_slice(len)?;
slice.fill(0);
Some(slice)
}
pub fn alloc_vec(&self, initial_capacity: usize) -> Option<ArenaVec<'_>> {
let pos = self.pos.get();
let max_capacity = self.buffer.len().saturating_sub(pos);
if initial_capacity > max_capacity {
return None;
}
self.pos.set(pos + initial_capacity);
Some(ArenaVec {
arena: self,
start: pos,
len: 0,
capacity: initial_capacity,
})
}
#[inline]
pub fn usage(&self) -> usize {
self.pos.get()
}
#[inline]
pub fn capacity(&self) -> usize {
self.buffer.len()
}
#[inline]
pub fn remaining(&self) -> usize {
self.buffer.len().saturating_sub(self.pos.get())
}
#[inline]
pub fn peak_usage(&self) -> usize {
self.peak_usage.get().max(self.pos.get())
}
}
pub struct ArenaVec<'a> {
arena: &'a Arena,
start: usize,
len: usize,
capacity: usize,
}
impl<'a> ArenaVec<'a> {
#[inline]
pub fn push(&mut self, value: u8) -> bool {
if self.len >= self.capacity {
if !self.grow(1) {
return false;
}
}
unsafe {
let ptr = self.arena.buffer.as_ptr().add(self.start + self.len) as *mut u8;
*ptr = value;
}
self.len += 1;
true
}
pub fn extend_from_slice(&mut self, data: &[u8]) -> bool {
if self.len + data.len() > self.capacity {
if !self.grow(data.len()) {
return false;
}
}
unsafe {
let ptr = self.arena.buffer.as_ptr().add(self.start + self.len) as *mut u8;
core::ptr::copy_nonoverlapping(data.as_ptr(), ptr, data.len());
}
self.len += data.len();
true
}
fn grow(&mut self, additional: usize) -> bool {
let needed = self.len + additional;
if needed <= self.capacity {
return true;
}
let arena_pos = self.arena.pos.get();
let our_end = self.start + self.capacity;
if arena_pos == our_end {
let new_capacity = (needed * 2).min(self.arena.remaining() + self.capacity);
if new_capacity >= needed {
let growth = new_capacity - self.capacity;
self.arena.pos.set(arena_pos + growth);
self.capacity = new_capacity;
return true;
}
}
false
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
unsafe {
let ptr = self.arena.buffer.as_ptr().add(self.start);
core::slice::from_raw_parts(ptr, self.len)
}
}
pub fn to_vec(&self) -> Vec<u8> {
self.as_slice().to_vec()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arena_creation() {
let arena = Arena::new(1024);
assert_eq!(arena.capacity(), 1024);
assert_eq!(arena.usage(), 0);
assert_eq!(arena.remaining(), 1024);
}
#[test]
fn test_arena_alloc_slice() {
let arena = Arena::new(1024);
let slice1 = arena.alloc_slice(100).unwrap();
assert_eq!(slice1.len(), 100);
assert_eq!(arena.usage(), 100);
let slice2 = arena.alloc_slice(200).unwrap();
assert_eq!(slice2.len(), 200);
assert_eq!(arena.usage(), 300);
}
#[test]
fn test_arena_alloc_slice_zeroed() {
let arena = Arena::new(1024);
let slice = arena.alloc_slice(100).unwrap();
slice.fill(0xFF);
arena.reset();
let zeroed = arena.alloc_slice_zeroed(100).unwrap();
for &byte in zeroed.iter() {
assert_eq!(byte, 0);
}
}
#[test]
fn test_arena_reset() {
let arena = Arena::new(1024);
let _ = arena.alloc_slice(500).unwrap();
assert_eq!(arena.usage(), 500);
arena.reset();
assert_eq!(arena.usage(), 0);
assert_eq!(arena.peak_usage(), 500);
}
#[test]
fn test_arena_overflow() {
let arena = Arena::new(100);
assert!(arena.alloc_slice(50).is_some());
assert!(arena.alloc_slice(60).is_none()); assert_eq!(arena.usage(), 50); }
#[test]
fn test_arena_vec_basic() {
let arena = Arena::new(1024);
let mut vec = arena.alloc_vec(100).unwrap();
assert!(vec.is_empty());
vec.push(1);
vec.push(2);
vec.push(3);
assert_eq!(vec.len(), 3);
assert_eq!(vec.as_slice(), &[1, 2, 3]);
}
#[test]
fn test_arena_vec_extend() {
let arena = Arena::new(1024);
let mut vec = arena.alloc_vec(100).unwrap();
vec.extend_from_slice(b"Hello, ");
vec.extend_from_slice(b"World!");
assert_eq!(vec.as_slice(), b"Hello, World!");
}
#[test]
fn test_arena_vec_grow() {
let arena = Arena::new(1024);
let mut vec = arena.alloc_vec(10).unwrap();
for i in 0..50u8 {
assert!(vec.push(i));
}
assert_eq!(vec.len(), 50);
for (i, &b) in vec.as_slice().iter().enumerate() {
assert_eq!(b, i as u8);
}
}
#[test]
fn test_arena_vec_to_vec() {
let arena = Arena::new(1024);
let mut arena_vec = arena.alloc_vec(100).unwrap();
arena_vec.extend_from_slice(b"Test data");
let regular_vec = arena_vec.to_vec();
assert_eq!(regular_vec, b"Test data");
}
#[test]
fn test_arena_peak_usage() {
let arena = Arena::new(1024);
let _ = arena.alloc_slice(400);
arena.reset();
let _ = arena.alloc_slice(200);
assert_eq!(arena.peak_usage(), 400);
}
#[test]
fn test_default_arena_size() {
let arena = Arena::with_default_size();
assert_eq!(arena.capacity(), DEFAULT_ARENA_SIZE);
}
}