use std::{fmt, ptr::NonNull};
use super::{Bucket, Pointer, Root, Slot, Trace, TARGET};
pub struct Arena<'a, T: fmt::Debug, const N: usize> {
buckets: Vec<Box<Bucket<'a, T, N>>>,
}
impl<'a, T: fmt::Debug, const N: usize> Arena<'a, T, N> {
#[must_use]
pub fn new() -> Self {
log::debug!(target: TARGET,
"Arena: new arena of {} with bucket size {}",
std::any::type_name::<T>(), N);
Self { buckets: vec![] }
}
#[must_use]
pub fn len(&self) -> usize {
self.buckets.iter().fold(0, |x, acc| x + acc.len())
}
#[must_use]
#[inline]
pub fn capacity(&self) -> usize {
N * self.buckets.len()
}
#[must_use]
pub fn available(&self) -> usize {
self.buckets.iter().fold(0, |x, acc| x + acc.available())
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.buckets.is_empty() || self.buckets.iter().all(|bucket| bucket.is_empty())
}
#[must_use]
pub fn is_valid_pointer(&self, ptr: NonNull<Slot<T>>) -> bool {
self.buckets
.iter()
.any(|bucket| bucket.is_valid_pointer(ptr))
}
#[must_use]
pub fn alloc(&mut self, value: T) -> Pointer<'a, T> {
for (idx, bucket) in self.buckets.iter_mut().enumerate().rev() {
if !bucket.is_full() {
let value = bucket.alloc(value).expect("bucket reports it is not full");
log::debug!(target: TARGET,
"Arena: allocating value in arena at {:p} in bucket {}",
value.as_ptr(), idx);
return value;
}
}
let mut bucket = Bucket::new();
let value = bucket
.alloc(value)
.expect("new bucket must be able to allocate");
self.buckets.push(bucket);
log::debug!(target: TARGET,
"Arena: allocating value in arena at {:p} in new bucket {}",
value.as_ptr(), self.buckets.len() - 1);
value
}
pub(super) fn drop_all(&mut self) {
for bucket in &mut self.buckets {
bucket.drop_all();
}
}
}
impl<'a, T: Trace<'a> + fmt::Debug, const N: usize> Arena<'a, T, N> {
pub fn mark(&mut self, pending: &mut Vec<Root<'a>>) {
log::debug!(target: TARGET,
"Arena: marking {} buckets of {}",
self.buckets.len(), std::any::type_name::<T>());
for bucket in &mut self.buckets {
bucket.mark(pending);
}
log::debug!(target: TARGET, "Arena: {} pending values", pending.len());
}
#[inline]
pub fn sweep(&mut self) -> usize {
self.sweep_and(|_| {})
}
pub fn sweep_and(&mut self, mut f: impl FnMut(&mut Slot<T>)) -> usize {
log::debug!(target: TARGET,
"Arena: sweeping {} buckets of {}",
self.buckets.len(), std::any::type_name::<T>());
let mut collected = 0;
for bucket in &mut self.buckets.iter_mut() {
collected += bucket.sweep_and(&mut f);
}
log::debug!(target: TARGET,
"Arena: sweeped {} buckets, collected {} values",
self.buckets.len(), collected);
if self.buckets.len() > 4 && collected > self.capacity() / 4 {
self.compact();
}
collected
}
pub fn compact(&mut self) {
let len = self.buckets.len();
log::debug!(target: TARGET,
"Arena: compacting {} buckets of {}",
self.buckets.len(), std::any::type_name::<T>());
self.buckets.retain(|bucket| !bucket.is_empty());
log::debug!(target: TARGET,
"Arena: compacted {} buckets, removed {} buckets",
len, len - self.buckets.len());
}
}
impl<'a, T: fmt::Debug, const N: usize> Default for Arena<'a, T, N> {
fn default() -> Self {
Self::new()
}
}
impl<'a, T: fmt::Debug, const N: usize> fmt::Debug for Arena<'a, T, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Arena")
.field("buckets", &self.buckets)
.field("len", &self.len())
.field("capacity", &self.capacity())
.finish()
}
}