use std::cell::RefCell;
use std::marker::PhantomData;
use std::mem;
pub struct Arena {
data: RefCell<Vec<u8>>,
offset: RefCell<usize>,
capacity: usize,
}
pub struct ArenaSlice<'a, T> {
ptr: *mut T,
len: usize,
_marker: PhantomData<&'a mut T>,
}
impl Arena {
pub fn new(n_elements: usize) -> Self {
Self::with_byte_capacity(n_elements * mem::size_of::<f64>())
}
pub fn with_byte_capacity(bytes: usize) -> Self {
let data = vec![0u8; bytes];
Self {
data: RefCell::new(data),
offset: RefCell::new(0),
capacity: bytes,
}
}
pub fn alloc<T: Default + Copy>(&self, count: usize) -> Option<ArenaSlice<'_, T>> {
let size = mem::size_of::<T>();
let align = mem::align_of::<T>();
let total_bytes = size * count;
let mut offset = self.offset.borrow_mut();
let aligned = (*offset + align - 1) & !(align - 1);
if aligned + total_bytes > self.capacity {
return None;
}
let data = self.data.borrow_mut();
let base_ptr = data.as_ptr();
let ptr = unsafe { base_ptr.add(aligned) as *mut T };
unsafe {
std::ptr::write_bytes(ptr, 0, count);
}
*offset = aligned + total_bytes;
Some(ArenaSlice {
ptr,
len: count,
_marker: PhantomData,
})
}
pub fn alloc_copy<T: Copy>(&self, src: &[T]) -> Option<ArenaSlice<'_, T>> {
let _size = mem::size_of::<T>();
let align = mem::align_of::<T>();
let total_bytes = std::mem::size_of_val(src);
let mut offset = self.offset.borrow_mut();
let aligned = (*offset + align - 1) & !(align - 1);
if aligned + total_bytes > self.capacity {
return None;
}
let data = self.data.borrow_mut();
let base_ptr = data.as_ptr();
let ptr = unsafe { base_ptr.add(aligned) as *mut T };
unsafe {
std::ptr::copy_nonoverlapping(src.as_ptr(), ptr, src.len());
}
*offset = aligned + total_bytes;
Some(ArenaSlice {
ptr,
len: src.len(),
_marker: PhantomData,
})
}
pub fn reset(&self) {
*self.offset.borrow_mut() = 0;
}
pub fn used_bytes(&self) -> usize {
*self.offset.borrow()
}
pub fn capacity_bytes(&self) -> usize {
self.capacity
}
pub fn remaining_bytes(&self) -> usize {
self.capacity - *self.offset.borrow()
}
}
impl<T> ArenaSlice<'_, T> {
pub fn as_slice(&self) -> &[T] {
unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) }
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
impl<T> std::ops::Deref for ArenaSlice<'_, T> {
type Target = [T];
fn deref(&self) -> &[T] {
self.as_slice()
}
}
impl<T> std::ops::DerefMut for ArenaSlice<'_, T> {
fn deref_mut(&mut self) -> &mut [T] {
self.as_mut_slice()
}
}
const DEFAULT_SIZE_CLASSES: &[usize] = &[64, 256, 1024, 4096, 16384, 65536];
pub struct SlabPool<T: Copy> {
slabs: RefCell<Vec<Vec<Vec<T>>>>,
size_classes: Vec<usize>,
}
impl<T: Copy + Default> SlabPool<T> {
pub fn new() -> Self {
Self::with_sizes(DEFAULT_SIZE_CLASSES)
}
pub fn with_sizes(sizes: &[usize]) -> Self {
let mut size_classes: Vec<usize> = sizes.to_vec();
size_classes.sort_unstable();
let slabs = RefCell::new(size_classes.iter().map(|_| Vec::new()).collect());
Self {
slabs,
size_classes,
}
}
pub fn acquire(&self, min_size: usize) -> Vec<T> {
let class_idx = self.size_class_index(min_size);
let mut slabs = self.slabs.borrow_mut();
if let Some(idx) = class_idx {
if let Some(mut buf) = slabs[idx].pop() {
buf.clear();
buf.resize(self.size_classes[idx], T::default());
return buf;
}
let cap = self.size_classes[idx];
let mut buf = Vec::with_capacity(cap);
buf.resize(cap, T::default());
buf
} else {
let mut buf = Vec::with_capacity(min_size);
buf.resize(min_size, T::default());
buf
}
}
pub fn release(&self, buf: Vec<T>) {
let cap = buf.capacity();
let class_idx = self.size_classes.iter().position(|&s| s == cap);
if let Some(idx) = class_idx {
self.slabs.borrow_mut()[idx].push(buf);
}
}
pub fn available_count(&self) -> usize {
self.slabs.borrow().iter().map(std::vec::Vec::len).sum()
}
fn size_class_index(&self, min_size: usize) -> Option<usize> {
self.size_classes.iter().position(|&s| s >= min_size)
}
}
impl<T: Copy + Default> Default for SlabPool<T> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arena_basic() {
let arena = Arena::new(256);
let mut buf = arena.alloc::<f64>(10).expect("allocation should succeed");
assert_eq!(buf.len(), 10);
for i in 0..10 {
buf[i] = i as f64;
}
for i in 0..10 {
assert!((buf[i] - i as f64).abs() < 1e-15);
}
}
#[test]
fn test_arena_multiple_allocs() {
let arena = Arena::new(512);
let a = arena.alloc::<f64>(100).expect("first alloc");
let b = arena.alloc::<f64>(100).expect("second alloc");
let c = arena.alloc::<f64>(100).expect("third alloc");
assert_eq!(a.len(), 100);
assert_eq!(b.len(), 100);
assert_eq!(c.len(), 100);
for &v in a.as_slice() {
assert!((v - 0.0_f64).abs() < f64::EPSILON);
}
}
#[test]
fn test_arena_overflow() {
let arena = Arena::new(10); let ok = arena.alloc::<f64>(10);
assert!(ok.is_some());
let fail = arena.alloc::<f64>(1);
assert!(fail.is_none());
}
#[test]
fn test_arena_reset() {
let arena = Arena::new(64);
let _ = arena.alloc::<f64>(60).expect("alloc before reset");
assert!(arena.alloc::<f64>(10).is_none(), "should be full");
arena.reset();
assert_eq!(arena.used_bytes(), 0);
let buf = arena.alloc::<f64>(60).expect("alloc after reset");
assert_eq!(buf.len(), 60);
}
#[test]
fn test_slab_pool_acquire_release() {
let pool: SlabPool<f64> = SlabPool::new();
let mut buf = pool.acquire(100);
assert!(buf.capacity() >= 100);
buf[0] = 99.0;
let cap = buf.capacity();
pool.release(buf);
assert_eq!(pool.available_count(), 1);
let buf2 = pool.acquire(100);
assert_eq!(buf2.capacity(), cap);
assert!((buf2[0] - 0.0_f64).abs() < f64::EPSILON);
}
#[test]
fn test_slab_pool_size_class_selection() {
let pool: SlabPool<f64> = SlabPool::new();
let buf = pool.acquire(100);
assert_eq!(buf.len(), 256);
assert!(buf.capacity() >= 256);
let buf2 = pool.acquire(64);
assert_eq!(buf2.len(), 64);
let buf3 = pool.acquire(100_000);
assert_eq!(buf3.len(), 100_000);
}
#[test]
fn test_arena_alloc_copy() {
let arena = Arena::new(256);
let src = [1.0_f64, 2.0, 3.0, 4.0, 5.0];
let buf = arena.alloc_copy(&src).expect("alloc_copy");
assert_eq!(buf.len(), 5);
assert_eq!(buf.as_slice(), &src);
}
#[test]
fn test_arena_alignment() {
let arena = Arena::with_byte_capacity(256);
let _ = arena.alloc::<u8>(1).unwrap();
let f = arena.alloc::<f64>(1).unwrap();
let ptr = f.as_slice().as_ptr() as usize;
assert_eq!(
ptr % mem::align_of::<f64>(),
0,
"f64 must be 8-byte aligned"
);
}
}