use core::cell::RefCell;
pub struct Arena<T> {
chunks: RefCell<Vec<Vec<T>>>,
chunk_capacity: usize,
}
#[allow(dead_code)]
impl<T> Arena<T> {
const INITIAL_CHUNK_CAPACITY: usize = 1024;
#[must_use]
pub const fn new() -> Self {
Self {
chunks: RefCell::new(Vec::new()),
chunk_capacity: Self::INITIAL_CHUNK_CAPACITY,
}
}
#[must_use]
pub const fn with_capacity(initial_capacity: usize) -> Self {
Self {
chunks: RefCell::new(Vec::new()),
chunk_capacity: if initial_capacity == 0 { 1
} else {
initial_capacity
},
}
}
#[allow(clippy::mut_from_ref)]
pub fn alloc(&self, value: T) -> &mut T {
let mut chunks = self.chunks.borrow_mut();
if chunks.is_empty() {
chunks.push(Vec::with_capacity(self.chunk_capacity));
} else if let Some(last) = chunks.last() {
if last.len() == last.capacity() {
let new_capacity = last.len() * 2;
chunks.push(Vec::with_capacity(new_capacity));
}
};
let current = chunks.last_mut().expect("chunks should not be empty");
let last = current.len();
current.push(value);
unsafe { &mut *current.as_mut_ptr().add(last) }
}
pub fn len(&self) -> usize {
let mut len = 0;
for chunk in self.chunks.borrow().iter() {
len += chunk.len();
}
len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.chunks.borrow().is_empty() || self.chunks.borrow()[0].is_empty()
}
pub fn into_vec(self) -> Vec<T> {
self.chunks.into_inner().into_iter().flatten().collect()
}
fn chunks(&self) -> usize {
self.chunks.borrow().len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq)]
struct TestStruct {
a: i32,
b: i32,
}
#[test]
pub fn test() {
let arena = Arena::<TestStruct>::with_capacity(2);
assert_eq!(arena.len(), 0);
assert_eq!(arena.chunks(), 0);
let a = arena.alloc(TestStruct { a: 1, b: 2 });
assert_eq!(arena.len(), 1);
assert_eq!(arena.chunks(), 1);
let b = arena.alloc(TestStruct { a: 3, b: 4 });
assert_eq!(arena.len(), 2);
assert_eq!(arena.chunks(), 1);
let c = arena.alloc(TestStruct { a: 5, b: 6 });
assert_eq!(arena.len(), 3);
assert_eq!(arena.chunks(), 2);
let d = arena.alloc(TestStruct { a: 7, b: 8 });
assert_eq!(arena.len(), 4);
assert_eq!(arena.chunks(), 2);
c.a = 10;
c.b = 11;
assert_eq!(a, &TestStruct { a: 1, b: 2 });
assert_eq!(b, &TestStruct { a: 3, b: 4 });
assert_eq!(c, &TestStruct { a: 10, b: 11 });
assert_eq!(d, &TestStruct { a: 7, b: 8 });
let vec = arena.into_vec();
assert_eq!(
vec,
vec![
TestStruct { a: 1, b: 2 },
TestStruct { a: 3, b: 4 },
TestStruct { a: 10, b: 11 },
TestStruct { a: 7, b: 8 },
]
);
}
#[test]
fn vec_order() {
let arena = Arena::with_capacity(1);
for &s in &["h", "e", "l", "l", "o"] {
arena.alloc(String::from(s));
}
assert_eq!(arena.into_vec(), vec!["h", "e", "l", "l", "o"]);
}
#[test]
fn test_zero_capacity() {
let arena = Arena::with_capacity(0);
assert_eq!(arena.chunk_capacity, 1);
let a = arena.alloc(1);
let b = arena.alloc(2);
assert_eq!(arena.len(), 2);
assert_eq!(arena.chunks(), 2);
assert_eq!(*a, 1);
assert_eq!(*b, 2);
}
}