use alloc::alloc::{Layout, alloc, dealloc};
use alloc::vec;
use alloc::vec::Vec;
use core::cell::{Cell, RefCell};
use core::marker::PhantomData;
use core::ptr::NonNull;
pub struct Arena {
chunks: RefCell<Vec<Chunk>>,
current: Cell<usize>,
ptr: Cell<*mut u8>,
end: Cell<*mut u8>,
}
struct Chunk {
start: NonNull<u8>,
layout: Layout,
}
impl Chunk {
fn new(size: usize) -> Option<Self> {
let layout = Layout::from_size_align(size, 16).ok()?;
let ptr = unsafe { alloc(layout) };
NonNull::new(ptr).map(|start| Chunk { start, layout })
}
fn end(&self) -> *mut u8 {
unsafe { self.start.as_ptr().add(self.layout.size()) }
}
}
impl Drop for Chunk {
fn drop(&mut self) {
unsafe {
dealloc(self.start.as_ptr(), self.layout);
}
}
}
pub const DEFAULT_CHUNK_SIZE: usize = 64 * 1024;
pub const MIN_CHUNK_SIZE: usize = 4 * 1024;
impl Arena {
pub fn new() -> Self {
Self::with_capacity(DEFAULT_CHUNK_SIZE)
}
pub fn with_capacity(capacity: usize) -> Self {
let chunk_size = capacity.max(MIN_CHUNK_SIZE);
let chunk = Chunk::new(chunk_size).expect("Failed to allocate arena chunk");
let ptr = chunk.start.as_ptr();
let end = chunk.end();
Arena {
chunks: RefCell::new(vec![chunk]),
current: Cell::new(0),
ptr: Cell::new(ptr),
end: Cell::new(end),
}
}
pub fn alloc<T>(&self, value: T) -> ArenaRef<'_, T> {
let layout = Layout::new::<T>();
let ptr = self.alloc_layout(layout);
unsafe {
let typed_ptr = ptr.as_ptr().cast::<T>();
typed_ptr.write(value);
ArenaRef::from_mut(&mut *typed_ptr)
}
}
pub fn alloc_slice<T: Copy>(&self, slice: &[T]) -> ArenaRef<'_, [T]> {
let layout = Layout::array::<T>(slice.len()).expect("Invalid slice layout");
let ptr = self.alloc_layout(layout);
unsafe {
let typed_ptr = ptr.as_ptr().cast::<T>();
core::ptr::copy_nonoverlapping(slice.as_ptr(), typed_ptr, slice.len());
ArenaRef::from_mut(core::slice::from_raw_parts_mut(typed_ptr, slice.len()))
}
}
pub fn alloc_str(&self, s: &str) -> ArenaRef<'_, str> {
let bytes = self.alloc_slice(s.as_bytes()).into_mut();
ArenaRef::from_mut(unsafe { core::str::from_utf8_unchecked_mut(bytes) })
}
fn alloc_layout(&self, layout: Layout) -> NonNull<u8> {
let layout = layout.pad_to_align();
loop {
let ptr = self.ptr.get();
let end = self.end.get();
let aligned = align_up(ptr as usize, layout.align());
let new_ptr = aligned + layout.size();
if new_ptr <= end as usize {
self.ptr.set(new_ptr as *mut u8);
return unsafe { NonNull::new_unchecked(aligned as *mut u8) };
}
self.grow(layout.size());
}
}
fn grow(&self, min_size: usize) {
let mut chunks = self.chunks.borrow_mut();
let last_size = chunks
.last()
.map_or(DEFAULT_CHUNK_SIZE, |c| c.layout.size());
let new_size = (last_size * 2).max(min_size + 256);
let chunk = Chunk::new(new_size).expect("Failed to allocate arena chunk");
let ptr = chunk.start.as_ptr();
let end = chunk.end();
chunks.push(chunk);
self.current.set(chunks.len() - 1);
self.ptr.set(ptr);
self.end.set(end);
}
pub unsafe fn reset(&self) {
let mut chunks = self.chunks.borrow_mut();
if let Some(first) = chunks.first() {
self.current.set(0);
self.ptr.set(first.start.as_ptr());
self.end.set(first.end());
}
chunks.truncate(1);
}
pub fn capacity(&self) -> usize {
self.chunks.borrow().iter().map(|c| c.layout.size()).sum()
}
pub fn used(&self) -> usize {
let chunks = self.chunks.borrow();
let current = self.current.get();
let mut used = 0;
for (i, chunk) in chunks.iter().enumerate() {
if i < current {
used += chunk.layout.size();
} else if i == current {
used += self.ptr.get() as usize - chunk.start.as_ptr() as usize;
}
}
used
}
}
impl Default for Arena {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "std")]
pub struct SyncArena {
inner: std::sync::Mutex<SyncArenaInner>,
}
#[cfg(feature = "std")]
struct SyncArenaInner {
chunks: Vec<Chunk>,
ptr: *mut u8,
end: *mut u8,
}
#[cfg(feature = "std")]
unsafe impl Send for SyncArena {}
#[cfg(feature = "std")]
unsafe impl Sync for SyncArena {}
#[cfg(feature = "std")]
impl SyncArena {
pub fn new() -> Self {
Self::with_capacity(DEFAULT_CHUNK_SIZE)
}
pub fn with_capacity(capacity: usize) -> Self {
let chunk_size = capacity.max(MIN_CHUNK_SIZE);
let chunk = Chunk::new(chunk_size).expect("Failed to allocate SyncArena chunk");
let ptr = chunk.start.as_ptr();
let end = chunk.end();
SyncArena {
inner: std::sync::Mutex::new(SyncArenaInner {
chunks: vec![chunk],
ptr,
end,
}),
}
}
pub fn alloc<T>(&self, value: T) -> ArenaRef<'_, T> {
let layout = Layout::new::<T>();
let ptr = self.alloc_layout(layout);
unsafe {
let typed_ptr = ptr.as_ptr().cast::<T>();
typed_ptr.write(value);
ArenaRef::from_mut(&mut *typed_ptr)
}
}
fn alloc_layout(&self, layout: Layout) -> NonNull<u8> {
let layout = layout.pad_to_align();
let mut inner = self.inner.lock().expect("SyncArena mutex poisoned");
loop {
let aligned = align_up(inner.ptr as usize, layout.align());
let new_ptr = aligned + layout.size();
if new_ptr <= inner.end as usize {
inner.ptr = new_ptr as *mut u8;
return unsafe { NonNull::new_unchecked(aligned as *mut u8) };
}
let last_size = inner
.chunks
.last()
.map_or(DEFAULT_CHUNK_SIZE, |c| c.layout.size());
let new_size = (last_size * 2).max(layout.size() + 256);
let chunk = Chunk::new(new_size).expect("Failed to allocate SyncArena chunk");
inner.ptr = chunk.start.as_ptr();
inner.end = chunk.end();
inner.chunks.push(chunk);
}
}
pub fn capacity(&self) -> usize {
self.inner
.lock()
.expect("SyncArena mutex poisoned")
.chunks
.iter()
.map(|c| c.layout.size())
.sum()
}
}
#[cfg(feature = "std")]
impl Default for SyncArena {
fn default() -> Self {
Self::new()
}
}
fn align_up(ptr: usize, align: usize) -> usize {
(ptr + align - 1) & !(align - 1)
}
pub fn with_arena<F, R>(f: F) -> R
where
F: FnOnce(&Arena) -> R,
{
let arena = Arena::new();
f(&arena)
}
pub fn with_arena_capacity<F, R>(capacity: usize, f: F) -> R
where
F: FnOnce(&Arena) -> R,
{
let arena = Arena::with_capacity(capacity);
f(&arena)
}
pub struct ArenaRef<'a, T: ?Sized> {
ptr: &'a mut T,
_marker: PhantomData<&'a ()>,
}
impl<'a, T: ?Sized> ArenaRef<'a, T> {
#[inline]
fn from_mut(ptr: &'a mut T) -> Self {
ArenaRef {
ptr,
_marker: PhantomData,
}
}
#[inline]
pub fn into_mut(self) -> &'a mut T {
self.ptr
}
pub fn get(&self) -> &T {
self.ptr
}
pub fn get_mut(&mut self) -> &mut T {
self.ptr
}
}
impl<'a, T> ArenaRef<'a, T> {
pub fn new(arena: &'a Arena, value: T) -> Self {
arena.alloc(value)
}
}
impl<T: ?Sized> core::ops::Deref for ArenaRef<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.ptr
}
}
impl<T: ?Sized> core::ops::DerefMut for ArenaRef<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.ptr
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arena_alloc() {
let arena = Arena::new();
let x = arena.alloc(42);
let y = arena.alloc(100);
assert_eq!(*x, 42);
assert_eq!(*y, 100);
}
#[test]
fn test_arena_alloc_slice() {
let arena = Arena::new();
let slice = arena.alloc_slice(&[1, 2, 3, 4, 5]);
assert_eq!(&*slice, &[1, 2, 3, 4, 5]);
}
#[test]
fn test_arena_alloc_str() {
let arena = Arena::new();
let s = arena.alloc_str("hello world");
assert_eq!(&*s, "hello world");
}
#[test]
fn test_arena_multiple_types() {
let arena = Arena::new();
let x: &mut i32 = arena.alloc(42).into_mut();
let y: &mut f64 = arena.alloc(2.5).into_mut();
let z: &mut bool = arena.alloc(true).into_mut();
assert_eq!(*x, 42);
assert_eq!(*y, 2.5);
assert!(*z);
}
#[test]
fn test_arena_capacity() {
let arena = Arena::with_capacity(1024);
assert!(arena.capacity() >= 1024);
for i in 0..1000 {
let _ = arena.alloc(i);
}
assert!(arena.capacity() > 1024);
}
#[test]
fn test_with_arena() {
let result = with_arena(|arena| {
let x = arena.alloc(10);
let y = arena.alloc(20);
*x + *y
});
assert_eq!(result, 30);
}
#[test]
fn test_arena_ref() {
let arena = Arena::new();
let mut r = ArenaRef::new(&arena, 42);
assert_eq!(*r, 42);
*r.get_mut() = 100;
assert_eq!(*r, 100);
}
}