use std::marker::PhantomData;
pub trait ArenaKey: Copy {
fn from_raw(raw: u32) -> Self;
fn to_raw(self) -> u32;
}
#[derive(Clone, Debug)]
pub struct Arena<K: ArenaKey, T> {
slots: Vec<Option<T>>,
free: Vec<u32>,
len: usize,
_marker: PhantomData<K>,
}
impl<K: ArenaKey, T> Default for Arena<K, T> {
fn default() -> Self {
Self {
slots: Vec::new(),
free: Vec::new(),
len: 0,
_marker: PhantomData,
}
}
}
impl<K: ArenaKey, T> Arena<K, T> {
pub fn new() -> Self {
Self::default()
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn capacity(&self) -> usize {
self.slots.len()
}
pub fn insert(&mut self, value: T) -> K {
self.insert_with(|_| value)
}
pub fn insert_with(&mut self, make: impl FnOnce(K) -> T) -> K {
let key = match self.free.pop() {
Some(idx) => K::from_raw(idx),
None => {
let idx = self.slots.len() as u32;
self.slots.push(None);
K::from_raw(idx)
}
};
self.slots[key.to_raw() as usize] = Some(make(key));
self.len += 1;
key
}
pub fn contains(&self, key: K) -> bool {
self.get(key).is_some()
}
pub fn get(&self, key: K) -> Option<&T> {
self.slots.get(key.to_raw() as usize).and_then(Option::as_ref)
}
pub fn get_mut(&mut self, key: K) -> Option<&mut T> {
self.slots
.get_mut(key.to_raw() as usize)
.and_then(Option::as_mut)
}
pub fn remove(&mut self, key: K) -> Option<T> {
let slot = self.slots.get_mut(key.to_raw() as usize)?;
let taken = slot.take();
if taken.is_some() {
self.free.push(key.to_raw());
self.len -= 1;
}
taken
}
pub fn iter(&self) -> impl Iterator<Item = (K, &T)> {
self.slots
.iter()
.enumerate()
.filter_map(|(i, slot)| slot.as_ref().map(|v| (K::from_raw(i as u32), v)))
}
pub fn keys(&self) -> impl Iterator<Item = K> + '_ {
self.slots
.iter()
.enumerate()
.filter_map(|(i, slot)| slot.as_ref().map(|_| K::from_raw(i as u32)))
}
pub fn values(&self) -> impl Iterator<Item = &T> {
self.slots.iter().filter_map(Option::as_ref)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
struct K(u32);
impl ArenaKey for K {
fn from_raw(raw: u32) -> Self {
K(raw)
}
fn to_raw(self) -> u32 {
self.0
}
}
#[test]
fn insert_get_remove() {
let mut a: Arena<K, &str> = Arena::new();
assert!(a.is_empty());
let x = a.insert("x");
let y = a.insert("y");
assert_eq!(a.len(), 2);
assert_eq!(a.get(x), Some(&"x"));
assert_eq!(a.get(y), Some(&"y"));
assert_eq!(a.remove(x), Some("x"));
assert!(!a.contains(x));
assert_eq!(a.len(), 1);
}
#[test]
fn slots_are_recycled() {
let mut a: Arena<K, u32> = Arena::new();
let x = a.insert(1);
a.remove(x);
let z = a.insert(2);
assert_eq!(x.to_raw(), z.to_raw());
assert_eq!(a.get(z), Some(&2));
}
#[test]
fn insert_with_sees_own_key() {
let mut a: Arena<K, K> = Arena::new();
let k = a.insert_with(|self_key| self_key);
assert_eq!(a.get(k), Some(&k));
}
#[test]
fn iter_skips_tombstones() {
let mut a: Arena<K, u32> = Arena::new();
let x = a.insert(10);
let _y = a.insert(20);
a.remove(x);
let collected: Vec<u32> = a.values().copied().collect();
assert_eq!(collected, vec![20]);
}
}