use std::any::Any;
use std::cell::RefCell;
use std::rc::Rc;
const PAGE_SIZE: usize = 4096;
pub struct FrameArena {
pages: Vec<Vec<u8>>,
current_page: usize,
cursor: usize,
pub total_allocated: usize,
pub reset_count: usize,
}
impl FrameArena {
pub fn new() -> Self {
FrameArena {
pages: vec![vec![0u8; PAGE_SIZE]],
current_page: 0,
cursor: 0,
total_allocated: 0,
reset_count: 0,
}
}
pub fn with_page_size(page_size: usize) -> Self {
let size = if page_size == 0 { PAGE_SIZE } else { page_size };
FrameArena {
pages: vec![vec![0u8; size]],
current_page: 0,
cursor: 0,
total_allocated: 0,
reset_count: 0,
}
}
pub fn alloc_bytes(&mut self, size: usize) -> (usize, usize) {
let aligned = (size + 7) & !7;
let page_size = self.page_size();
if aligned > page_size {
let page_idx = self.pages.len();
self.pages.push(vec![0u8; aligned]);
self.total_allocated += aligned;
return (page_idx, 0);
}
if self.cursor + aligned > page_size {
self.current_page += 1;
self.cursor = 0;
if self.current_page >= self.pages.len() {
self.pages.push(vec![0u8; page_size]);
}
}
let offset = self.cursor;
self.cursor += aligned;
self.total_allocated += aligned;
(self.current_page, offset)
}
pub fn get_bytes(&self, page: usize, offset: usize, len: usize) -> Option<&[u8]> {
self.pages
.get(page)
.and_then(|p| p.get(offset..offset + len))
}
pub fn get_bytes_mut(&mut self, page: usize, offset: usize, len: usize) -> Option<&mut [u8]> {
self.pages
.get_mut(page)
.and_then(|p| p.get_mut(offset..offset + len))
}
pub fn reset(&mut self) {
self.current_page = 0;
self.cursor = 0;
self.reset_count += 1;
}
pub fn page_count(&self) -> usize {
self.pages.len()
}
pub fn capacity(&self) -> usize {
self.pages.iter().map(|p| p.len()).sum()
}
pub fn used_bytes(&self) -> usize {
if self.pages.is_empty() {
return 0;
}
let page_size = self.page_size();
self.current_page * page_size + self.cursor
}
fn page_size(&self) -> usize {
self.pages.first().map(|p| p.len()).unwrap_or(PAGE_SIZE)
}
}
impl Default for FrameArena {
fn default() -> Self {
Self::new()
}
}
pub struct ArenaEntry {
pub value: Rc<RefCell<Box<dyn Any>>>,
}
pub struct ArenaStore {
pub arena: FrameArena,
entries: Vec<Option<ArenaEntry>>,
free_slots: Vec<usize>,
}
impl ArenaStore {
pub fn new() -> Self {
ArenaStore {
arena: FrameArena::new(),
entries: Vec::new(),
free_slots: Vec::new(),
}
}
pub fn alloc<T: Any + 'static>(&mut self, value: T) -> usize {
let entry = ArenaEntry {
value: Rc::new(RefCell::new(Box::new(value))),
};
if let Some(idx) = self.free_slots.pop() {
self.entries[idx] = Some(entry);
idx
} else {
let idx = self.entries.len();
self.entries.push(Some(entry));
idx
}
}
pub fn get<T: Any + 'static>(&self, index: usize) -> Option<&T> {
self.entries.get(index).and_then(|slot| {
slot.as_ref().and_then(|entry| {
let borrowed = entry.value.as_ref();
let ptr = borrowed.as_ptr();
unsafe { (*ptr).downcast_ref::<T>() }
})
})
}
pub fn live_count(&self) -> usize {
self.entries.iter().filter(|e| e.is_some()).count()
}
pub fn reset(&mut self) {
self.free_slots.clear();
for i in 0..self.entries.len() {
if self.entries[i].is_some() {
self.entries[i] = None;
self.free_slots.push(i);
}
}
self.free_slots.reverse();
self.arena.reset();
}
}
impl Default for ArenaStore {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arena_basic_alloc() {
let mut arena = FrameArena::new();
let (p1, o1) = arena.alloc_bytes(16);
let (p2, o2) = arena.alloc_bytes(16);
assert_eq!(p1, 0);
assert_eq!(o1, 0);
assert_eq!(p2, 0);
assert_eq!(o2, 16); }
#[test]
fn test_arena_page_overflow() {
let mut arena = FrameArena::with_page_size(32);
let (p1, _) = arena.alloc_bytes(16);
let (p2, _) = arena.alloc_bytes(16);
assert_eq!(p1, 0);
assert_eq!(p2, 0);
let (p3, o3) = arena.alloc_bytes(8);
assert_eq!(p3, 1);
assert_eq!(o3, 0);
assert_eq!(arena.page_count(), 2);
}
#[test]
fn test_arena_reset_reuses_pages() {
let mut arena = FrameArena::with_page_size(32);
arena.alloc_bytes(16);
arena.alloc_bytes(16);
arena.alloc_bytes(16); assert_eq!(arena.page_count(), 2);
arena.reset();
assert_eq!(arena.page_count(), 2); assert_eq!(arena.used_bytes(), 0);
assert_eq!(arena.reset_count, 1);
let (p, o) = arena.alloc_bytes(16);
assert_eq!(p, 0);
assert_eq!(o, 0);
}
#[test]
fn test_arena_alignment() {
let mut arena = FrameArena::new();
let (_, o1) = arena.alloc_bytes(1); let (_, o2) = arena.alloc_bytes(1); assert_eq!(o1, 0);
assert_eq!(o2, 8);
}
#[test]
fn test_arena_oversized_alloc() {
let mut arena = FrameArena::with_page_size(32);
let (p, o) = arena.alloc_bytes(64);
assert_eq!(o, 0);
assert!(arena.pages[p].len() >= 64);
}
#[test]
fn test_arena_get_bytes() {
let mut arena = FrameArena::new();
let (p, o) = arena.alloc_bytes(8);
let bytes = arena.get_bytes_mut(p, o, 8).unwrap();
bytes.copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
let read = arena.get_bytes(p, o, 8).unwrap();
assert_eq!(read, &[1, 2, 3, 4, 5, 6, 7, 8]);
}
#[test]
fn test_arena_deterministic_layout() {
let mut a1 = FrameArena::with_page_size(64);
let mut a2 = FrameArena::with_page_size(64);
let r1: Vec<_> = (0..10).map(|_| a1.alloc_bytes(8)).collect();
let r2: Vec<_> = (0..10).map(|_| a2.alloc_bytes(8)).collect();
assert_eq!(r1, r2, "deterministic layout");
}
#[test]
fn test_store_alloc_and_read() {
let mut store = ArenaStore::new();
let idx = store.alloc(42i64);
assert_eq!(*store.get::<i64>(idx).unwrap(), 42);
}
#[test]
fn test_store_reset_frees_entries() {
let mut store = ArenaStore::new();
store.alloc(1i64);
store.alloc(2i64);
assert_eq!(store.live_count(), 2);
store.reset();
assert_eq!(store.live_count(), 0);
}
#[test]
fn test_store_reset_reuses_slots() {
let mut store = ArenaStore::new();
let _a = store.alloc(1i64);
let _b = store.alloc(2i64);
store.reset();
let c = store.alloc(99i64);
assert!(c < 2, "should reuse a freed slot, got {c}");
assert_eq!(*store.get::<i64>(c).unwrap(), 99);
}
#[test]
fn test_store_type_mismatch() {
let mut store = ArenaStore::new();
let idx = store.alloc(42i64);
assert!(store.get::<String>(idx).is_none());
}
#[test]
fn test_arena_capacity_grows_not_shrinks() {
let mut arena = FrameArena::with_page_size(32);
for _ in 0..10 {
arena.alloc_bytes(16);
}
let cap = arena.capacity();
arena.reset();
assert_eq!(arena.capacity(), cap, "capacity must not shrink after reset");
}
}