#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Key {
pub(crate) index: u32,
pub(crate) generation: u32,
}
struct Slot<T> {
value: Option<T>,
generation: u32,
}
pub struct GenArena<T> {
slots: Vec<Slot<T>>,
free: Vec<u32>,
live: usize,
}
impl<T> Default for GenArena<T> {
fn default() -> Self {
GenArena {
slots: Vec::new(),
free: Vec::new(),
live: 0,
}
}
}
impl<T> GenArena<T> {
pub fn new() -> Self {
Self::default()
}
pub fn insert(&mut self, value: T) -> Key {
self.live += 1;
if let Some(index) = self.free.pop() {
let slot = &mut self.slots[index as usize];
debug_assert!(slot.value.is_none());
slot.value = Some(value);
return Key {
index,
generation: slot.generation,
};
}
let index = self.slots.len() as u32;
self.slots.push(Slot {
value: Some(value),
generation: 1,
});
Key {
index,
generation: 1,
}
}
pub fn get(&self, key: Key) -> Option<&T> {
let slot = self.slots.get(key.index as usize)?;
if slot.generation != key.generation {
return None;
}
slot.value.as_ref()
}
pub fn get_mut(&mut self, key: Key) -> Option<&mut T> {
let slot = self.slots.get_mut(key.index as usize)?;
if slot.generation != key.generation {
return None;
}
slot.value.as_mut()
}
pub fn remove(&mut self, key: Key) -> Option<T> {
let slot = self.slots.get_mut(key.index as usize)?;
if slot.generation != key.generation || slot.value.is_none() {
return None;
}
let value = slot.value.take();
slot.generation = slot.generation.wrapping_add(1);
self.free.push(key.index);
self.live -= 1;
value
}
pub fn contains(&self, key: Key) -> bool {
self.get(key).is_some()
}
pub fn live(&self) -> usize {
self.live
}
pub fn capacity_slots(&self) -> usize {
self.slots.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stale_keys_do_not_resolve() {
let mut a = GenArena::new();
let k1 = a.insert("one");
assert_eq!(a.get(k1), Some(&"one"));
assert_eq!(a.remove(k1), Some("one"));
assert_eq!(a.get(k1), None);
let k2 = a.insert("two");
assert_eq!(k1.index, k2.index);
assert_ne!(k1.generation, k2.generation);
assert_eq!(a.get(k1), None);
assert_eq!(a.get(k2), Some(&"two"));
}
#[test]
fn churn_keeps_slot_count_bounded() {
let mut a = GenArena::new();
for i in 0..10_000 {
let k = a.insert(i);
assert_eq!(a.remove(k), Some(i));
}
assert_eq!(a.live(), 0);
assert!(a.capacity_slots() <= 1, "churn must reuse freed slots");
}
#[test]
fn dead_key_never_matches() {
let mut a: GenArena<i32> = GenArena::new();
a.insert(7);
assert_eq!(
a.get(Key {
index: u32::MAX,
generation: 0
}),
None
);
assert_eq!(
a.get(Key {
index: 0,
generation: 0
}),
None
);
}
}