#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Handle(pub u64);
impl Handle {
#[inline]
pub fn new(slot: u32, generation: u32) -> Self {
let packed = ((generation as u64) << 32) | (slot as u64 & 0xFFFFFFFF);
Self(packed)
}
#[inline]
pub fn generation(&self) -> u32 {
(self.0 >> 32) as u32
}
#[inline]
pub fn slot(&self) -> u32 {
(self.0 & 0xFFFFFFFF) as u32
}
}
#[derive(Debug, Clone)]
pub struct SlotManager {
dense_to_sparse: Vec<usize>,
sparse_to_index: Vec<Option<usize>>,
generations: Vec<u32>,
free_stack: Vec<u32>,
size: usize,
}
impl Default for SlotManager {
fn default() -> Self {
Self::new(0)
}
}
impl SlotManager {
pub fn new(capacity: usize) -> Self {
Self {
dense_to_sparse: vec![0; capacity],
sparse_to_index: vec![None; capacity],
generations: vec![0; capacity],
free_stack: (0..capacity as u32).rev().collect(),
size: 0,
}
}
pub fn add<F>(&mut self, on_add: F) -> Handle
where
F: FnOnce(usize),
{
if self.free_stack.is_empty() {
self.grow();
}
let slot = self.free_stack.pop().unwrap() as usize;
self.generations[slot] += 1;
let g = self.generations[slot];
self.sparse_to_index[slot] = Some(self.size);
self.dense_to_sparse[self.size] = slot;
on_add(self.size);
self.size += 1;
Handle::new(slot as u32, g)
}
#[inline]
pub fn get(&self, handle: Handle) -> Option<usize> {
let slot = handle.slot() as usize;
if slot >= self.generations.len() || self.generations[slot] != handle.generation() {
return None;
}
self.sparse_to_index[slot]
}
pub fn remove<F>(&mut self, handle: Handle, on_remove: F) -> bool
where
F: FnOnce(usize, usize),
{
let slot = handle.slot() as usize;
if slot >= self.generations.len() || self.generations[slot] != handle.generation() {
return false;
}
if let Some(i_to_remove) = self.sparse_to_index[slot] {
let last_index = self.size - 1;
on_remove(last_index, i_to_remove);
let last_slot = self.dense_to_sparse[last_index];
self.sparse_to_index[last_slot] = Some(i_to_remove);
self.dense_to_sparse[i_to_remove] = last_slot;
self.generations[slot] += 1;
self.sparse_to_index[slot] = None;
self.free_stack.push(slot as u32);
self.size -= 1;
return true;
}
false
}
#[inline]
pub fn exists(&self, handle: Handle) -> bool {
let slot = handle.slot() as usize;
slot < self.generations.len() && self.generations[slot] == handle.generation()
}
pub fn iter(&self) -> impl Iterator<Item = Handle> + '_ {
self.dense_to_sparse[0..self.size]
.iter()
.map(|&slot| Handle::new(slot as u32, self.generations[slot]))
}
#[inline]
pub fn len(&self) -> usize {
self.size
}
#[inline]
pub fn is_empty(&self) -> bool {
self.size == 0
}
fn grow(&mut self) {
let old_cap = self.generations.len();
let new_cap = if old_cap == 0 { 1 } else { old_cap * 2 };
self.dense_to_sparse.resize(new_cap, 0);
self.sparse_to_index.resize(new_cap, None);
self.generations.resize(new_cap, 0);
for i in (old_cap..new_cap).rev() {
self.free_stack.push(i as u32);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_workflow() {
let mut manager = SlotManager::new(10);
let mut data = Vec::new();
let h1 = manager.add(|_| data.push("A"));
let h2 = manager.add(|_| data.push("B"));
assert_eq!(manager.len(), 2);
assert_eq!(data[manager.get(h1).unwrap()], "A");
manager.remove(h1, |last, to_rem| {
data.swap(to_rem, last);
data.pop();
});
assert!(manager.get(h1).is_none());
assert_eq!(data[manager.get(h2).unwrap()], "B");
}
#[test]
fn aba_protection() {
let mut manager = SlotManager::new(1);
let h1 = manager.add(|_| ());
manager.remove(h1, |_, _| ());
let h2 = manager.add(|_| ());
assert_eq!(h1.slot(), h2.slot());
assert_ne!(h1.generation(), h2.generation());
assert!(!manager.exists(h1));
assert!(manager.exists(h2));
}
}