use serde::{Deserialize, Serialize}; use std::{ cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}, fmt, hash, marker::PhantomData, }; /// Type safe index into Depot #[derive(Deserialize, Serialize)] pub struct Id { idx: u32, gen: u32, phantom: PhantomData, } impl Id { pub fn id(&self) -> u64 { self.idx as u64 | ((self.gen as u64) << 32) } } impl Copy for Id {} impl Clone for Id { fn clone(&self) -> Self { Self { idx: self.idx, gen: self.gen, phantom: PhantomData, } } } impl Eq for Id {} impl PartialEq for Id { fn eq(&self, other: &Self) -> bool { self.idx == other.idx && self.gen == other.gen } } impl Ord for Id { fn cmp(&self, other: &Self) -> Ordering { (self.idx, self.gen).cmp(&(other.idx, other.gen)) } } impl PartialOrd for Id { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl fmt::Debug for Id { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "Id<{}>({}, {})", std::any::type_name::(), self.idx, self.gen ) } } impl hash::Hash for Id { fn hash(&self, h: &mut H) { self.idx.hash(h); self.gen.hash(h); } } struct Entry { gen: u32, item: Option, } /// A general-purpose high performance allocator, basically Vec with type safe /// indices (Id) pub struct Depot { entries: Vec>, len: usize, } impl Default for Depot { fn default() -> Self { Self { entries: Vec::new(), len: 0, } } } impl Depot { pub fn is_empty(&self) -> bool { self.len == 0 } pub fn len(&self) -> usize { self.len } pub fn contains(&self, id: Id) -> bool { self.entries .get(id.idx as usize) .map(|e| e.gen == id.gen && e.item.is_some()) .unwrap_or(false) } pub fn get(&self, id: Id) -> Option<&T> { if let Some(entry) = self.entries.get(id.idx as usize) { if entry.gen == id.gen { entry.item.as_ref() } else { None } } else { None } } pub fn get_mut(&mut self, id: Id) -> Option<&mut T> { if let Some(entry) = self.entries.get_mut(id.idx as usize) { if entry.gen == id.gen { entry.item.as_mut() } else { None } } else { None } } pub fn ids(&self) -> impl Iterator> + '_ { self.iter().map(|(id, _)| id) } pub fn values(&self) -> impl Iterator + '_ { self.iter().map(|(_, item)| item) } pub fn values_mut(&mut self) -> impl Iterator + '_ { self.iter_mut().map(|(_, item)| item) } pub fn iter(&self) -> impl Iterator, &T)> + '_ { self.entries .iter() .enumerate() .filter_map(move |(idx, entry)| { Some(Id { idx: idx as u32, gen: entry.gen, phantom: PhantomData, }) .zip(entry.item.as_ref()) }) } pub fn iter_mut(&mut self) -> impl Iterator, &mut T)> + '_ { self.entries .iter_mut() .enumerate() .filter_map(move |(idx, entry)| { Some(Id { idx: idx as u32, gen: entry.gen, phantom: PhantomData, }) .zip(entry.item.as_mut()) }) } pub fn insert(&mut self, item: T) -> Id { if self.len < self.entries.len() { // TODO: Make this more efficient with a lookahead system let (idx, entry) = self .entries .iter_mut() .enumerate() .find(|(_, e)| e.item.is_none()) .unwrap(); entry.item = Some(item); assert!(entry.gen < u32::MAX); entry.gen += 1; Id { idx: idx as u32, gen: entry.gen, phantom: PhantomData, } } else { assert!(self.entries.len() < (u32::MAX - 1) as usize); let id = Id { idx: self.entries.len() as u32, gen: 0, phantom: PhantomData, }; self.entries.push(Entry { gen: 0, item: Some(item), }); self.len += 1; id } } pub fn remove(&mut self, id: Id) -> Option { if let Some(item) = self .entries .get_mut(id.idx as usize) .and_then(|e| if e.gen == id.gen { e.item.take() } else { None }) { self.len -= 1; Some(item) } else { None } } pub fn recreate_id(&self, i: u64) -> Option> { if i as usize >= self.entries.len() { None } else { Some(Id { idx: i as u32, gen: self .entries .get(i as usize) .map(|e| e.gen) .unwrap_or_default(), phantom: PhantomData, }) } } }