use std::num::NonZeroUsize;
pub enum Slot<T> {
Free { next_free: Option<Index> },
Used { value: T },
}
pub struct HandleTable<T> {
data: Vec<Slot<T>>,
free_list: Option<Index>, requests: usize,
}
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub struct Index(NonZeroUsize);
impl Index {
pub fn get(&self) -> usize {
self.0.get()
}
}
impl From<NonZeroUsize> for Index {
fn from(value: NonZeroUsize) -> Self {
Index(value)
}
}
impl<T> HandleTable<T> {
pub fn new() -> Self {
Self {
data: Vec::new(),
free_list: None,
requests: 0,
}
}
pub fn clear(&mut self) {
self.data.clear();
self.free_list = None;
self.requests = 0;
}
pub fn len(&self) -> usize {
self.requests
}
pub fn insert(&mut self, value: T) -> Index {
self.insert_fn(|_| value)
}
pub fn insert_fn(&mut self, value_fn: impl FnOnce(Index) -> T) -> Index {
self.requests += 1;
if let Some(index) = self.free_list {
let offset = index.0.get() - 1;
let free = std::mem::replace(
&mut self.data[offset],
Slot::Used {
value: value_fn(index),
},
);
match free {
Slot::Free { next_free } => {
self.free_list = next_free;
}
_ => unreachable!(),
}
index
} else {
let offset = self.data.len();
let index = Index(NonZeroUsize::new(offset + 1).unwrap());
self.data.push(Slot::Used {
value: value_fn(index),
});
index
}
}
pub fn get(&self, index: Index) -> Option<&T> {
let offset = index.0.get() - 1;
match self.data.get(offset) {
Some(Slot::Used { value }) => Some(value),
_ => None,
}
}
pub fn remove(&mut self, index: Index) -> Option<T> {
let offset = index.0.get() - 1;
if offset >= self.data.len() {
return None;
}
let slot = &mut self.data[offset];
if matches!(slot, Slot::Free { .. }) {
return None;
}
self.requests -= 1;
let next_free = self.free_list;
self.free_list = Some(index);
let value = std::mem::replace(&mut self.data[offset], Slot::Free { next_free });
match value {
Slot::Used { value } => Some(value),
_ => unreachable!(),
}
}
pub fn is_empty(&self) -> bool {
self.requests == 0
}
pub fn drain(&mut self) -> impl Iterator<Item = (Index, T)> {
self.free_list = None;
self.requests = 0;
self.data
.drain(..)
.enumerate()
.filter_map(|(offset, slot)| match slot {
Slot::Used { value } => {
Some((Index(NonZeroUsize::new(offset + 1).unwrap()), value))
}
Slot::Free { .. } => None,
})
}
pub fn indexes(&self) -> impl Iterator<Item = Index> + use<'_, T> {
self.data
.iter()
.enumerate()
.filter_map(|(offset, slot)| match slot {
Slot::Used { .. } => Some(Index(NonZeroUsize::new(offset + 1).unwrap())),
_ => None,
})
}
pub fn iter(&self) -> impl Iterator<Item = (Index, &T)> {
self.data
.iter()
.enumerate()
.filter_map(|(offset, slot)| match slot {
Slot::Used { value } => {
Some((Index(NonZeroUsize::new(offset + 1).unwrap()), value))
}
_ => None,
})
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (Index, &mut T)> {
self.data
.iter_mut()
.enumerate()
.filter_map(|(offset, slot)| match slot {
Slot::Used { value } => {
Some((Index(NonZeroUsize::new(offset + 1).unwrap()), value))
}
_ => None,
})
}
}
impl<T> Default for HandleTable<T> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod test {
use super::*;
use std::{cell::Cell, rc::Rc};
#[test]
fn test_insert_and_iter() {
let mut handle_table = HandleTable::new();
let values = vec![10, 20, 30, 40, 50];
let indices = values
.into_iter()
.map(|v| handle_table.insert(v))
.collect::<Vec<_>>();
for (i, index) in indices.into_iter().enumerate() {
let stored = handle_table.remove(index).unwrap();
assert_eq!(stored, [10, 20, 30, 40, 50][i]);
}
}
#[test]
fn test_remove() {
let mut handle_table = HandleTable::new();
let idx1 = handle_table.insert(100);
let idx2 = handle_table.insert(200);
assert_eq!(handle_table.remove(idx1), Some(100));
assert_eq!(handle_table.remove(idx2), Some(200));
assert_eq!(handle_table.remove(idx1), None);
}
#[test]
fn test_reuse_slot() {
let mut handle_table = HandleTable::new();
let idx1 = handle_table.insert(1);
let idx2 = handle_table.insert(2);
let idx3 = handle_table.insert(3);
assert_eq!(handle_table.remove(idx2), Some(2));
assert_eq!(handle_table.remove(idx2), None);
let idx_new = handle_table.insert(4);
assert_eq!(idx_new, idx2);
assert_eq!(handle_table.remove(idx_new), Some(4));
assert_eq!(handle_table.remove(idx3), Some(3));
assert_eq!(handle_table.remove(idx2), None);
assert_eq!(handle_table.remove(idx1), Some(1));
}
#[test]
fn test_drop() {
let drop_count = Rc::new(Cell::new(0usize));
struct Dropper {
drop_count: Rc<Cell<usize>>,
}
impl Drop for Dropper {
fn drop(&mut self) {
self.drop_count.set(self.drop_count.get() + 1);
}
}
{
let mut handle_table = HandleTable::new();
for _ in 0..100 {
let drop_count = drop_count.clone();
handle_table.insert(Dropper { drop_count });
}
for i in (0..100).step_by(10) {
handle_table
.remove(Index(NonZeroUsize::new(i + 1).unwrap()))
.unwrap();
}
handle_table.insert(Dropper {
drop_count: drop_count.clone(),
});
}
assert_eq!(101, drop_count.get());
}
#[test]
fn test_new_and_default() {
let handle_table1 = HandleTable::<i32>::new();
let handle_table2 = HandleTable::<i32>::default();
assert_eq!(handle_table1.len(), 0);
assert_eq!(handle_table2.len(), 0);
assert!(handle_table1.is_empty());
assert!(handle_table2.is_empty());
}
#[test]
fn test_clear() {
let mut handle_table = HandleTable::new();
let idx1 = handle_table.insert("value1");
let idx2 = handle_table.insert("value2");
let idx3 = handle_table.insert("value3");
assert_eq!(handle_table.len(), 3);
assert!(!handle_table.is_empty());
handle_table.clear();
assert_eq!(handle_table.len(), 0);
assert!(handle_table.is_empty());
assert_eq!(handle_table.get(idx1), None);
assert_eq!(handle_table.get(idx2), None);
assert_eq!(handle_table.get(idx3), None);
let new_idx = handle_table.insert("new_value");
assert_eq!(handle_table.len(), 1);
assert_eq!(handle_table.get(new_idx), Some(&"new_value"));
}
#[test]
fn test_len_and_is_empty() {
let mut handle_table = HandleTable::new();
assert_eq!(handle_table.len(), 0);
assert!(handle_table.is_empty());
let idx1 = handle_table.insert(10);
assert_eq!(handle_table.len(), 1);
assert!(!handle_table.is_empty());
let idx2 = handle_table.insert(20);
assert_eq!(handle_table.len(), 2);
assert!(!handle_table.is_empty());
let idx3 = handle_table.insert(30);
assert_eq!(handle_table.len(), 3);
assert!(!handle_table.is_empty());
handle_table.remove(idx2);
assert_eq!(handle_table.len(), 2);
assert!(!handle_table.is_empty());
handle_table.remove(idx1);
assert_eq!(handle_table.len(), 1);
assert!(!handle_table.is_empty());
handle_table.remove(idx3);
assert_eq!(handle_table.len(), 0);
assert!(handle_table.is_empty());
}
#[test]
fn test_get() {
let mut handle_table = HandleTable::new();
let invalid_idx = Index(NonZeroUsize::new(1).unwrap());
assert_eq!(handle_table.get(invalid_idx), None);
let idx1 = handle_table.insert("hello");
let idx2 = handle_table.insert("world");
assert_eq!(handle_table.get(idx1), Some(&"hello"));
assert_eq!(handle_table.get(idx2), Some(&"world"));
handle_table.remove(idx1);
assert_eq!(handle_table.get(idx1), None);
assert_eq!(handle_table.get(idx2), Some(&"world"));
let out_of_bounds = Index(NonZeroUsize::new(100).unwrap());
assert_eq!(handle_table.get(out_of_bounds), None);
}
#[test]
fn test_insert_fn() {
let mut handle_table = HandleTable::new();
let idx1 = handle_table.insert_fn(|index| format!("value_at_{}", index.get()));
assert_eq!(handle_table.get(idx1), Some(&"value_at_1".to_string()));
let idx2 = handle_table.insert_fn(|index| format!("value_at_{}", index.get()));
assert_eq!(handle_table.get(idx2), Some(&"value_at_2".to_string()));
handle_table.remove(idx1);
let idx3 = handle_table.insert_fn(|index| format!("reused_slot_{}", index.get()));
assert_eq!(idx3, idx1);
assert_eq!(handle_table.get(idx3), Some(&"reused_slot_1".to_string()));
}
#[test]
fn test_indexes() {
let mut handle_table = HandleTable::new();
let indexes: Vec<_> = handle_table.indexes().collect();
assert!(indexes.is_empty());
let idx1 = handle_table.insert("a");
let idx2 = handle_table.insert("b");
let idx3 = handle_table.insert("c");
let mut indexes: Vec<_> = handle_table.indexes().collect();
indexes.sort();
assert_eq!(indexes, vec![idx1, idx2, idx3]);
handle_table.remove(idx2);
let mut indexes: Vec<_> = handle_table.indexes().collect();
indexes.sort();
assert_eq!(indexes, vec![idx1, idx3]);
let idx4 = handle_table.insert("d");
assert_eq!(idx4, idx2);
let mut indexes: Vec<_> = handle_table.indexes().collect();
indexes.sort();
assert_eq!(indexes, vec![idx1, idx2, idx3]); }
#[test]
fn test_iter() {
let mut handle_table = HandleTable::new();
let items: Vec<_> = handle_table.iter().collect();
assert!(items.is_empty());
let idx1 = handle_table.insert(10);
let idx2 = handle_table.insert(20);
let idx3 = handle_table.insert(30);
let mut items: Vec<_> = handle_table.iter().collect();
items.sort_by_key(|&(idx, _)| idx);
assert_eq!(items, vec![(idx1, &10), (idx2, &20), (idx3, &30)]);
handle_table.remove(idx2);
let mut items: Vec<_> = handle_table.iter().collect();
items.sort_by_key(|&(idx, _)| idx);
assert_eq!(items, vec![(idx1, &10), (idx3, &30)]);
}
#[test]
fn test_iter_mut() {
let mut handle_table = HandleTable::new();
let idx1 = handle_table.insert(10);
let idx2 = handle_table.insert(20);
let idx3 = handle_table.insert(30);
for (_, value) in handle_table.iter_mut() {
*value += 1;
}
assert_eq!(handle_table.get(idx1), Some(&11));
assert_eq!(handle_table.get(idx2), Some(&21));
assert_eq!(handle_table.get(idx3), Some(&31));
handle_table.remove(idx2);
for (_, value) in handle_table.iter_mut() {
*value *= 2;
}
assert_eq!(handle_table.get(idx1), Some(&22));
assert_eq!(handle_table.get(idx3), Some(&62));
}
#[test]
fn test_index_methods() {
let idx = Index(NonZeroUsize::new(42).unwrap());
assert_eq!(idx.get(), 42);
let nz = NonZeroUsize::new(123).unwrap();
let idx_from: Index = Index::from(nz);
assert_eq!(idx_from.get(), 123);
let idx1 = Index(NonZeroUsize::new(1).unwrap());
let idx2 = Index(NonZeroUsize::new(2).unwrap());
let idx1_copy = idx1;
let idx1_clone = idx1;
assert_eq!(idx1, idx1_copy);
assert_eq!(idx1, idx1_clone);
assert_ne!(idx1, idx2);
assert!(idx1 < idx2);
assert!(idx2 > idx1);
assert_eq!(format!("{idx1:?}"), "Index(1)"); use std::collections::HashMap;
let mut map = HashMap::new();
map.insert(idx1, "value1");
map.insert(idx2, "value2");
assert_eq!(map.get(&idx1), Some(&"value1"));
}
#[test]
fn test_drain() {
let mut handle_table = HandleTable::new();
let items: Vec<_> = handle_table.drain().collect();
assert!(items.is_empty());
assert!(handle_table.is_empty());
let idx1 = handle_table.insert(10);
let idx2 = handle_table.insert(20);
let idx3 = handle_table.insert(30);
handle_table.remove(idx2);
let mut items: Vec<_> = handle_table.drain().collect();
items.sort_by_key(|(idx, _)| *idx);
assert_eq!(items, vec![(idx1, 10), (idx3, 30)]);
assert!(handle_table.is_empty());
assert_eq!(handle_table.len(), 0);
let new_idx = handle_table.insert(42);
assert_eq!(handle_table.get(new_idx), Some(&42));
assert_eq!(handle_table.len(), 1);
}
#[test]
fn test_drain_drops_values() {
let drop_count = Rc::new(Cell::new(0usize));
struct Dropper {
drop_count: Rc<Cell<usize>>,
}
impl Drop for Dropper {
fn drop(&mut self) {
self.drop_count.set(self.drop_count.get() + 1);
}
}
let mut handle_table = HandleTable::new();
for _ in 0..5 {
handle_table.insert(Dropper {
drop_count: drop_count.clone(),
});
}
assert_eq!(drop_count.get(), 0);
drop(handle_table.drain());
assert_eq!(drop_count.get(), 5);
assert!(handle_table.is_empty());
}
#[test]
fn test_edge_cases() {
let mut handle_table = HandleTable::new();
let fake_idx = Index(NonZeroUsize::new(999).unwrap());
assert_eq!(handle_table.remove(fake_idx), None);
let idx = handle_table.insert("value");
assert_eq!(handle_table.remove(idx), Some("value"));
assert_eq!(handle_table.remove(idx), None);
assert_eq!(handle_table.remove(idx), None);
assert_eq!(handle_table.get(idx), None);
let idx1 = handle_table.insert("1");
let idx2 = handle_table.insert("2");
let idx3 = handle_table.insert("3");
handle_table.remove(idx1);
handle_table.remove(idx3);
let idx_new1 = handle_table.insert("4");
assert_eq!(idx_new1, idx3);
let idx_new2 = handle_table.insert("5");
assert_eq!(idx_new2, idx1);
assert_eq!(handle_table.get(idx2), Some(&"2"));
assert_eq!(handle_table.get(idx_new1), Some(&"4"));
assert_eq!(handle_table.get(idx_new2), Some(&"5"));
}
}