use std::ops::{Index, IndexMut}; use itertools::Itertools; enum Rec { Val(T), Next(usize), } /// A simple and very fast store that assigns small stable integer IDs to /// objects. It uses a free-list for O(1) insertion, deletion and retrieval. pub struct IdStore { first: usize, values: Vec>, } impl IdStore { pub fn new() -> Self { IdStore { first: 0, values: Vec::new() } } pub fn add(&mut self, value: T) -> usize { if self.first == 0 && self.values.is_empty() { self.first = 1; self.values.push(Rec::Val(value)); return 0; } if self.first == self.values.len() { let len = self.values.len(); self.values.extend((len..len * 2).map(|i| Rec::Next(i + 1))); } let Some(rec) = self.values.get_mut(self.first) else { panic!("Bounds check and growth above") }; let Rec::Next(next) = rec else { panic!("first should always point to an empty space or one past the length") }; let id = std::mem::replace(&mut self.first, *next); *rec = Rec::Val(value); id } pub fn add_with(&mut self, cb: impl FnOnce(usize) -> T) -> usize { self.add(cb(self.first)) } pub fn remove(&mut self, id: usize) -> T { let Some(rec) = self.values.get_mut(id) else { panic!("Index out of bounds") }; let Rec::Val(val) = std::mem::replace(rec, Rec::Next(self.first)) else { panic!("Index vacated") }; self.first = id; val } pub fn iter(&self) -> impl Iterator { (self.values.iter().enumerate()) .filter_map(|(i, rec)| if let Rec::Val(val) = rec { Some((i, val)) } else { None }) } pub fn iter_mut(&mut self) -> impl Iterator { (self.values.iter_mut().enumerate()) .filter_map(|(i, rec)| if let Rec::Val(val) = rec { Some((i, val)) } else { None }) } } #[allow(clippy::type_complexity, reason = "This is verbose enough as it is")] pub struct IntoIter( std::iter::FilterMap< std::iter::Enumerate>>, fn((usize, Rec)) -> Option<(usize, T)>, >, ); impl Iterator for IntoIter { type Item = (usize, T); fn next(&mut self) -> Option { self.0.next() } fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } } impl IntoIterator for IdStore { type Item = (usize, T); type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { IntoIter( (self.values.into_iter().enumerate()) .filter_map(|(i, rec)| if let Rec::Val(val) = rec { Some((i, val)) } else { None }), ) } } impl Index for IdStore { type Output = T; fn index(&self, index: usize) -> &Self::Output { match self.values.get(index) { Some(Rec::Val(val)) => val, _ => panic!("Invalid or stale index"), } } } impl IndexMut for IdStore { fn index_mut(&mut self, index: usize) -> &mut Self::Output { match self.values.get_mut(index) { Some(Rec::Val(val)) => val, _ => panic!("Invalid or stale index"), } } } impl Default for IdStore { fn default() -> Self { Self::new() } } impl FromIterator for IdStore { fn from_iter>(iter: T) -> Self { let values = iter.into_iter().map(|a| Rec::Val(a)).collect_vec(); Self { first: values.len(), values } } } #[cfg(test)] mod test { use super::*; #[test] fn add_and_retrieve() { let mut store = IdStore::new(); let key1 = store.add(14); let key2 = store.add(34); assert_eq!(store[key1], 14); assert_eq!(store[key2], 34); assert_eq!(store.remove(key1), 14); assert_eq!(store.iter().collect_vec(), vec![(key2, &34)]); } }