use bumpalo::Bump; use std::cell::RefCell; use std::mem::MaybeUninit; use std::ptr::NonNull; use std::sync::Arc; use parking_lot::Mutex; use string_interner::{DefaultBackend, Symbol}; use string_interner::symbol::SymbolU32; const ARENA_SIZE: usize = 64 * 1024 * 1024; // 64MB arenas const POOL_SIZE: usize = 1024; #[repr(align(64))] pub struct ArenaAllocator { current: RefCell, arenas: RefCell>, string_interner: Arc>>, } impl ArenaAllocator { pub fn new() -> Self { Self { current: RefCell::new(Bump::with_capacity(ARENA_SIZE)), arenas: RefCell::new(Vec::with_capacity(16)), string_interner: Arc::new(Mutex::new(string_interner::StringInterner::new())), } } #[inline(always)] pub fn alloc(&self, val: T) -> &T { unsafe { let ptr = self.current.borrow().alloc(val) as *const T; &*ptr } } #[inline(always)] pub fn alloc_slice(&self, slice: &[T]) -> &[T] { unsafe { let ptr = self.current.borrow().alloc_slice_copy(slice) as *const [T]; &*ptr } } #[inline(always)] pub fn alloc_str(&self, s: &str) -> &str { unsafe { let ptr = self.current.borrow().alloc_str(s) as *const str; &*ptr } } #[inline(always)] pub fn intern_string(&self, s: &str) -> u32 { let mut interner = self.string_interner.lock(); interner.get_or_intern(s).to_usize() as u32 } #[inline(always)] pub fn get_interned(&self, id: u32) -> Option { let interner = self.string_interner.lock(); let symbol = SymbolU32::try_from_usize(id as usize)?; interner.resolve(symbol) .map(|s| s.to_string()) } pub fn reset(&self) { let mut current = self.current.borrow_mut(); current.reset(); let mut arenas = self.arenas.borrow_mut(); for arena in arenas.iter_mut() { arena.reset(); } } pub fn new_arena(&self) { let mut arenas = self.arenas.borrow_mut(); let old = std::mem::replace(&mut *self.current.borrow_mut(), Bump::with_capacity(ARENA_SIZE)); arenas.push(old); } } pub struct ObjectPool { pool: Vec>, factory: fn() -> T, } impl ObjectPool { pub fn new(capacity: usize, factory: fn() -> T) -> Self { let mut pool = Vec::with_capacity(capacity); for _ in 0..capacity { pool.push(Box::new(factory())); } Self { pool, factory } } #[inline(always)] pub fn acquire(&mut self) -> Box { self.pool.pop().unwrap_or_else(|| Box::new((self.factory)())) } #[inline(always)] pub fn release(&mut self, obj: Box) { if self.pool.len() < POOL_SIZE { self.pool.push(obj); } } } #[repr(C, align(64))] pub struct StackBuffer { data: [MaybeUninit; N], len: usize, } impl StackBuffer { #[inline(always)] pub const fn new() -> Self { Self { data: unsafe { MaybeUninit::uninit().assume_init() }, len: 0, } } #[inline(always)] pub fn push(&mut self, byte: u8) -> bool { if self.len < N { self.data[self.len] = MaybeUninit::new(byte); self.len += 1; true } else { false } } #[inline(always)] pub fn as_slice(&self) -> &[u8] { unsafe { std::slice::from_raw_parts( self.data.as_ptr() as *const u8, self.len ) } } #[inline(always)] pub fn clear(&mut self) { self.len = 0; } } #[cfg(test)] mod tests { use super::*; #[test] fn test_arena_allocator() { let arena = ArenaAllocator::new(); let s1 = arena.alloc_str("hello"); let s2 = arena.alloc_str("world"); assert_eq!(s1, "hello"); assert_eq!(s2, "world"); } #[test] fn test_string_interning() { let arena = ArenaAllocator::new(); let id1 = arena.intern_string("test"); let id2 = arena.intern_string("test"); assert_eq!(id1, id2); let s = arena.get_interned(id1).unwrap(); assert_eq!(s, "test"); } }