use alloc::vec::Vec;
use core::fmt;
use crate::{ArenaError, Id};
pub struct Arena<T> {
items: Vec<T>,
}
impl<T> Arena<T> {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self { items: Vec::new() }
}
#[inline]
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
items: Vec::with_capacity(capacity),
}
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.items.reserve(additional);
}
#[inline]
pub fn alloc(&mut self, value: T) -> Id<T> {
match self.try_alloc(value) {
Ok(id) => id,
Err(_) => panic!("arena is full: cannot allocate beyond u32::MAX values"),
}
}
#[inline]
pub fn try_alloc(&mut self, value: T) -> Result<Id<T>, ArenaError> {
let raw = u32::try_from(self.items.len()).map_err(|_| ArenaError::CapacityExhausted)?;
self.items.push(value);
Ok(Id::new(raw))
}
#[inline]
#[must_use]
pub fn get(&self, id: Id<T>) -> Option<&T> {
self.items.get(id.raw() as usize)
}
#[inline]
pub fn get_mut(&mut self, id: Id<T>) -> Option<&mut T> {
self.items.get_mut(id.raw() as usize)
}
#[inline]
#[must_use]
pub fn contains(&self, id: Id<T>) -> bool {
(id.raw() as usize) < self.items.len()
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.items.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
#[inline]
#[must_use]
pub fn capacity(&self) -> usize {
self.items.capacity()
}
pub fn iter(&self) -> impl Iterator<Item = (Id<T>, &T)> + '_ {
self.items
.iter()
.enumerate()
.map(|(i, value)| (Id::new(i as u32), value))
}
}
impl<T> Default for Arena<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T> fmt::Debug for Arena<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Arena")
.field("len", &self.items.len())
.field("capacity", &self.items.capacity())
.finish()
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use alloc::vec::Vec;
use super::*;
#[test]
fn test_alloc_then_get_round_trips() {
let mut arena = Arena::new();
let id = arena.alloc("payload");
assert_eq!(arena.get(id), Some(&"payload"));
assert!(arena.contains(id));
assert_eq!(arena.len(), 1);
}
#[test]
fn test_handles_stay_valid_as_the_arena_grows() {
let mut arena = Arena::new();
let first = arena.alloc(1);
for n in 2..=1000 {
let _ = arena.alloc(n);
}
assert_eq!(arena.get(first), Some(&1));
assert_eq!(arena.len(), 1000);
}
#[test]
fn test_get_mut_back_patches_in_place() {
let mut arena = Arena::new();
let id = arena.alloc(0_u32);
*arena.get_mut(id).expect("live handle") = 7;
assert_eq!(arena.get(id), Some(&7));
}
#[test]
fn test_out_of_range_handle_resolves_to_none() {
let mut big = Arena::new();
let mut last = big.alloc(0);
for n in 1..50 {
last = big.alloc(n);
}
let small: Arena<i32> = Arena::new();
assert_eq!(small.get(last), None);
assert!(!small.contains(last));
}
#[test]
fn test_iter_visits_every_value_once() {
let mut arena = Arena::new();
let mut expected = Vec::new();
for n in 0..32 {
let _ = arena.alloc(n);
expected.push(n);
}
let mut seen: Vec<_> = arena.iter().map(|(_, v)| *v).collect();
seen.sort_unstable();
assert_eq!(seen, expected);
}
#[test]
fn test_try_alloc_succeeds_on_a_fresh_arena() {
let mut arena = Arena::new();
let id = arena.try_alloc(123).expect("first slot is free");
assert_eq!(arena.get(id), Some(&123));
}
#[test]
fn test_default_is_empty() {
let arena: Arena<u8> = Arena::default();
assert!(arena.is_empty());
assert!(arena.capacity() >= arena.len());
}
}