use std::collections::HashSet;
pub(crate) struct ReclaimArena<E> {
slots: Vec<Option<E>>,
generations: Vec<u32>,
free: Vec<u32>,
}
impl<E> Default for ReclaimArena<E> {
fn default() -> Self {
ReclaimArena {
slots: Vec::new(),
generations: Vec::new(),
free: Vec::new(),
}
}
}
impl<E> core::fmt::Debug for ReclaimArena<E> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ReclaimArena")
.field("slots", &self.slots.len())
.field("free", &self.free.len())
.finish_non_exhaustive()
}
}
impl<E> ReclaimArena<E> {
pub(crate) fn alloc(&mut self, entity: E) -> u32 {
if let Some(index) = self.free.pop() {
self.slots[index as usize] = Some(entity);
return index;
}
let index = self.slots.len() as u32;
self.slots.push(Some(entity));
self.generations.push(0);
index
}
pub(crate) fn get(&self, index: u32) -> Option<&E> {
self.slots.get(index as usize)?.as_ref()
}
pub(crate) fn get_mut(&mut self, index: u32) -> Option<&mut E> {
self.slots.get_mut(index as usize)?.as_mut()
}
pub(crate) fn generation(&self, index: u32) -> Option<u32> {
self.generations.get(index as usize).copied()
}
pub(crate) fn sweep(&mut self, live: &HashSet<u32>, byte_of: impl Fn(&E) -> usize) -> usize {
let mut freed_bytes = 0;
for (i, slot) in self.slots.iter_mut().enumerate() {
let Some(entry) = slot.as_ref() else {
continue;
};
if !live.contains(&(i as u32)) {
freed_bytes += byte_of(entry);
*slot = None;
let next = self.generations[i].wrapping_add(1);
self.generations[i] = if next == u32::MAX { 0 } else { next };
self.free.push(i as u32);
}
}
freed_bytes
}
}