use std::alloc::{alloc, dealloc, Layout};
use std::cell::{Cell, RefCell};
use std::ptr::NonNull;
use std::sync::Arc;
use super::hints::{likely, unlikely};
pub struct PooledArena {
base: NonNull<u8>,
size: usize,
head: Cell<usize>,
pool: Option<Arc<ArenaPool>>,
}
impl PooledArena {
fn new(size: usize) -> Self {
let aligned_size = (size + 63) & !63;
let layout = Layout::from_size_align(aligned_size, 64).unwrap();
let base = unsafe {
let ptr = alloc(layout);
NonNull::new(ptr).unwrap()
};
Self {
base,
size: aligned_size,
head: Cell::new(0),
pool: None,
}
}
fn set_pool(&mut self, pool: Arc<ArenaPool>) {
self.pool = Some(pool);
}
#[inline(always)]
pub fn alloc(&self, layout: Layout) -> *mut u8 {
let align = layout.align();
let size = layout.size();
let current = self.head.get();
let align_mask = align - 1;
let aligned = (current + align_mask) & !align_mask;
let new_head = aligned + size;
if unlikely(new_head > self.size) {
return std::ptr::null_mut();
}
self.head.set(new_head);
unsafe { self.base.as_ptr().add(aligned) }
}
#[inline(always)]
pub fn reset(&self) {
self.head.set(0);
}
#[inline(always)]
pub fn allocated(&self) -> usize {
self.head.get()
}
#[inline(always)]
pub fn capacity(&self) -> usize {
self.size
}
}
impl Drop for PooledArena {
fn drop(&mut self) {
if let Some(_pool) = self.pool.take() {
self.reset();
} else {
unsafe {
let layout = Layout::from_size_align_unchecked(self.size, 64);
dealloc(self.base.as_ptr(), layout);
}
}
}
}
pub struct ArenaPool {
arenas: RefCell<Vec<PooledArena>>,
arena_size: usize,
max_pool_size: usize,
}
impl ArenaPool {
pub fn new(arena_size: usize, max_pool_size: usize) -> Self {
Self {
arenas: RefCell::new(Vec::with_capacity(max_pool_size)),
arena_size,
max_pool_size,
}
}
pub fn get_arena(&self) -> PooledArena {
let mut arenas = self.arenas.borrow_mut();
if let Some(arena) = arenas.pop() {
arena.reset();
arena
} else {
PooledArena::new(self.arena_size)
}
}
fn return_arena(&self, mut arena: PooledArena) {
let mut arenas = self.arenas.borrow_mut();
if arenas.len() < self.max_pool_size {
arena.set_pool(Arc::new(self.clone()));
arenas.push(arena);
}
}
pub fn clear(&self) {
self.arenas.borrow_mut().clear();
}
pub fn len(&self) -> usize {
self.arenas.borrow().len()
}
pub fn is_empty(&self) -> bool {
self.arenas.borrow().is_empty()
}
}
impl Clone for ArenaPool {
fn clone(&self) -> Self {
Self {
arenas: RefCell::new(Vec::with_capacity(self.max_pool_size)),
arena_size: self.arena_size,
max_pool_size: self.max_pool_size,
}
}
}
pub struct GlobalArenaPool {
pools: RefCell<Vec<Option<Arc<ArenaPool>>>>,
max_pool_size: usize,
}
impl GlobalArenaPool {
pub fn new(max_pool_size: usize) -> Self {
let pool_count = 7;
Self {
pools: RefCell::new(vec![None; pool_count]),
max_pool_size,
}
}
fn get_pool(&self, size: usize) -> Option<Arc<ArenaPool>> {
let mut pools = self.pools.borrow_mut();
let pool_idx = match size {
0..=1024 => 0,
1025..=4096 => 1,
4097..=16384 => 2,
16385..=65536 => 3,
65537..=262144 => 4,
262145..=1048576 => 5,
1048577..=4194304 => 6,
_ => return None,
};
if pools[pool_idx].is_none() {
let pool_size = match pool_idx {
0 => 1024,
1 => 4096,
2 => 16384,
3 => 65536,
4 => 262144,
5 => 1048576,
6 => 4194304,
_ => unreachable!(),
};
pools[pool_idx] = Some(Arc::new(ArenaPool::new(pool_size, self.max_pool_size)));
}
pools[pool_idx].clone()
}
pub fn get_arena(&self, size: usize) -> Option<PooledArena> {
self.get_pool(size).map(|pool| {
let mut arena = pool.get_arena();
arena.set_pool(pool);
arena
})
}
pub fn clear_all(&self) {
let pools = self.pools.borrow();
for pool in pools.iter().flatten() {
pool.clear();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arena_pool_basic() {
let pool = ArenaPool::new(4096, 5);
let arena1 = pool.get_arena();
assert_eq!(arena1.capacity(), 4096);
assert_eq!(arena1.allocated(), 0);
let layout = Layout::new::<u64>();
let ptr = arena1.alloc(layout);
assert!(!ptr.is_null());
assert_eq!(arena1.allocated(), 8);
drop(arena1);
assert_eq!(pool.len(), 0);
let arena2 = pool.get_arena();
assert_eq!(arena2.allocated(), 0); assert_eq!(pool.len(), 0); }
#[test]
fn test_global_arena_pool() {
let global = GlobalArenaPool::new(10);
let arena1 = global.get_arena(1024).unwrap();
let arena2 = global.get_arena(4096).unwrap();
let arena3 = global.get_arena(4096).unwrap();
assert_eq!(arena1.capacity(), 1024);
assert_eq!(arena2.capacity(), 4096);
assert_eq!(arena3.capacity(), 4096);
let arena4 = global.get_arena(8192 * 1024);
assert!(arena4.is_none());
}
}