use alloc::vec::Vec;
use crate::error::{Error, Result};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Index {
generation: u32,
slot: u32,
}
impl Index {
#[inline]
pub const fn slot(self) -> u32 {
self.slot
}
#[inline]
pub const fn generation(self) -> u32 {
self.generation
}
}
enum Occupant<T> {
Occupied(T),
Vacant { next_free: Option<u32> },
}
struct Slot<T> {
generation: u32,
occupant: Occupant<T>,
}
pub struct Arena<T> {
slots: Vec<Slot<T>>,
free_head: Option<u32>,
len: usize,
}
impl<T> Arena<T> {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
slots: Vec::new(),
free_head: None,
len: 0,
}
}
#[inline]
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
slots: Vec::with_capacity(capacity),
free_head: None,
len: 0,
}
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.slots.reserve(additional);
}
#[inline]
#[must_use]
pub const fn len(&self) -> usize {
self.len
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
#[must_use]
pub fn capacity(&self) -> usize {
self.slots.capacity()
}
#[inline]
#[must_use]
pub fn contains(&self, idx: Index) -> bool {
self.get(idx).is_some()
}
#[inline]
#[must_use]
pub fn get(&self, idx: Index) -> Option<&T> {
let slot = self.slots.get(idx.slot as usize)?;
if slot.generation != idx.generation {
return None;
}
match &slot.occupant {
Occupant::Occupied(value) => Some(value),
Occupant::Vacant { .. } => None,
}
}
#[inline]
#[must_use]
pub fn get_mut(&mut self, idx: Index) -> Option<&mut T> {
let slot = self.slots.get_mut(idx.slot as usize)?;
if slot.generation != idx.generation {
return None;
}
match &mut slot.occupant {
Occupant::Occupied(value) => Some(value),
Occupant::Vacant { .. } => None,
}
}
#[inline]
pub fn insert(&mut self, value: T) -> Index {
match self.try_insert(value) {
Ok(idx) => idx,
Err(_) => panic!("arena slot counter overflow (u32::MAX slots)"),
}
}
pub fn try_insert(&mut self, value: T) -> Result<Index> {
if let Some(slot_id) = self.free_head {
let slot = match self.slots.get_mut(slot_id as usize) {
Some(s) => s,
None => return Err(Error::CounterOverflow),
};
let next_free = match &slot.occupant {
Occupant::Vacant { next_free } => *next_free,
Occupant::Occupied(_) => return Err(Error::CounterOverflow),
};
self.free_head = next_free;
slot.occupant = Occupant::Occupied(value);
self.len += 1;
Ok(Index {
generation: slot.generation,
slot: slot_id,
})
} else {
let slot_idx = self.slots.len();
if slot_idx > u32::MAX as usize {
return Err(Error::CounterOverflow);
}
self.slots.push(Slot {
generation: 1,
occupant: Occupant::Occupied(value),
});
self.len += 1;
Ok(Index {
generation: 1,
slot: slot_idx as u32,
})
}
}
pub fn remove(&mut self, idx: Index) -> Option<T> {
let slot = self.slots.get_mut(idx.slot as usize)?;
if slot.generation != idx.generation {
return None;
}
if matches!(slot.occupant, Occupant::Vacant { .. }) {
return None;
}
let vacated = Occupant::Vacant {
next_free: self.free_head,
};
let prior = core::mem::replace(&mut slot.occupant, vacated);
slot.generation = slot.generation.wrapping_add(1);
self.free_head = Some(idx.slot);
self.len -= 1;
match prior {
Occupant::Occupied(value) => Some(value),
Occupant::Vacant { .. } => None,
}
}
pub fn clear(&mut self) {
let total = self.slots.len();
for (i, slot) in self.slots.iter_mut().enumerate() {
if matches!(slot.occupant, Occupant::Occupied(_)) {
slot.generation = slot.generation.wrapping_add(1);
}
slot.occupant = Occupant::Vacant {
next_free: if i + 1 < total {
Some((i + 1) as u32)
} else {
None
},
};
}
self.free_head = if total == 0 { None } else { Some(0) };
self.len = 0;
}
#[inline]
pub fn iter(&self) -> Iter<'_, T> {
Iter {
slots: self.slots.iter().enumerate(),
}
}
#[inline]
pub fn iter_mut(&mut self) -> IterMut<'_, T> {
IterMut {
slots: self.slots.iter_mut().enumerate(),
}
}
}
impl<T> Default for Arena<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T: core::fmt::Debug> core::fmt::Debug for Arena<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Arena")
.field("len", &self.len)
.field("capacity", &self.slots.capacity())
.finish()
}
}
pub struct Iter<'a, T> {
slots: core::iter::Enumerate<core::slice::Iter<'a, Slot<T>>>,
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = (Index, &'a T);
fn next(&mut self) -> Option<Self::Item> {
for (i, slot) in self.slots.by_ref() {
if let Occupant::Occupied(value) = &slot.occupant {
return Some((
Index {
generation: slot.generation,
slot: i as u32,
},
value,
));
}
}
None
}
}
pub struct IterMut<'a, T> {
slots: core::iter::Enumerate<core::slice::IterMut<'a, Slot<T>>>,
}
impl<'a, T> Iterator for IterMut<'a, T> {
type Item = (Index, &'a mut T);
fn next(&mut self) -> Option<Self::Item> {
for (i, slot) in self.slots.by_ref() {
let generation = slot.generation;
if let Occupant::Occupied(value) = &mut slot.occupant {
return Some((
Index {
generation,
slot: i as u32,
},
value,
));
}
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn insert_and_get() {
let mut a = Arena::new();
let i = a.insert(42);
assert_eq!(a.get(i), Some(&42));
assert_eq!(a.len(), 1);
assert!(!a.is_empty());
}
#[test]
fn remove_invalidates_handle() {
let mut a = Arena::new();
let i = a.insert("hello");
assert_eq!(a.remove(i), Some("hello"));
assert!(a.get(i).is_none());
assert!(a.remove(i).is_none());
}
#[test]
fn slot_reuse_bumps_generation() {
let mut a = Arena::new();
let i1 = a.insert(1);
let _ = a.remove(i1);
let i2 = a.insert(2);
assert_eq!(i1.slot(), i2.slot());
assert_ne!(i1.generation(), i2.generation());
assert!(a.get(i1).is_none());
assert_eq!(a.get(i2), Some(&2));
}
#[test]
fn iter_yields_only_live() {
let mut a = Arena::new();
let i1 = a.insert("a");
let i2 = a.insert("b");
let i3 = a.insert("c");
let _ = a.remove(i2);
let values: Vec<_> = a.iter().map(|(_, v)| *v).collect();
assert_eq!(values, vec!["a", "c"]);
let _ = (i1, i3);
}
#[test]
fn clear_resets_len_and_invalidates_handles() {
let mut a = Arena::new();
let i = a.insert(7);
a.clear();
assert_eq!(a.len(), 0);
assert!(a.get(i).is_none());
}
#[test]
fn get_mut_mutates() {
let mut a = Arena::new();
let i = a.insert(10);
if let Some(v) = a.get_mut(i) {
*v = 99;
}
assert_eq!(a.get(i), Some(&99));
}
#[test]
fn contains_reflects_liveness() {
let mut a = Arena::new();
let i = a.insert(0_u8);
assert!(a.contains(i));
let _ = a.remove(i);
assert!(!a.contains(i));
}
}