use std::cell::RefCell;
pub struct SolverArena {
buf: RefCell<Vec<u8>>,
offset: RefCell<usize>,
}
impl SolverArena {
pub fn with_capacity(capacity: usize) -> Self {
Self {
buf: RefCell::new(vec![0u8; capacity]),
offset: RefCell::new(0),
}
}
pub fn alloc_slice<T: Copy + Default>(&self, len: usize) -> &mut [T] {
let size = std::mem::size_of::<T>();
let align = std::mem::align_of::<T>();
assert!(align <= 16, "SolverArena does not support alignment > 16");
let byte_len = size
.checked_mul(len)
.expect("SolverArena::alloc_slice: size * len overflowed usize");
let mut offset = self.offset.borrow_mut();
let mut buf = self.buf.borrow_mut();
let aligned = (*offset + align - 1) & !(align - 1);
let needed = aligned
.checked_add(byte_len)
.expect("SolverArena::alloc_slice: aligned + byte_len overflowed usize");
if needed > buf.len() {
let new_cap = (needed * 2).max(buf.len() * 2);
buf.resize(new_cap, 0);
}
buf[aligned..aligned + byte_len].fill(0);
*offset = aligned + byte_len;
let ptr = buf[aligned..].as_mut_ptr() as *mut T;
drop(offset);
drop(buf);
unsafe { std::slice::from_raw_parts_mut(ptr, len) }
}
pub fn reset(&self) {
*self.offset.borrow_mut() = 0;
}
pub fn bytes_used(&self) -> usize {
*self.offset.borrow()
}
pub fn capacity(&self) -> usize {
self.buf.borrow().len()
}
}
unsafe impl Send for SolverArena {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alloc_and_reset() {
let arena = SolverArena::with_capacity(4096);
let s1: &mut [f64] = arena.alloc_slice(100);
assert_eq!(s1.len(), 100);
assert!(arena.bytes_used() >= 800);
let s2: &mut [f32] = arena.alloc_slice(50);
assert_eq!(s2.len(), 50);
arena.reset();
assert_eq!(arena.bytes_used(), 0);
}
#[test]
fn grows_when_needed() {
let arena = SolverArena::with_capacity(16);
let s: &mut [f64] = arena.alloc_slice(100);
assert_eq!(s.len(), 100);
assert!(arena.capacity() >= 800);
}
}