use std::sync::Arc; use crate::id::{Id, Marker}; use crate::resource::ResourceType; use crate::{Epoch, Index}; /// An entry in a `Storage::map` table. #[derive(Debug)] pub(crate) enum Element where T: StorageItem, { /// There are no live ids with this index. Vacant, /// There is one live id with this index, allocated at the given /// epoch. Occupied(T, Epoch), } pub(crate) trait StorageItem: ResourceType { type Marker: Marker; } impl ResourceType for Arc { const TYPE: &'static str = T::TYPE; } impl StorageItem for Arc { type Marker = T::Marker; } #[macro_export] macro_rules! impl_storage_item { ($ty:ident) => { impl $crate::storage::StorageItem for $ty { type Marker = $crate::id::markers::$ty; } }; } /// A table of `T` values indexed by the id type `I`. /// /// `Storage` implements [`std::ops::Index`], accepting `Id` values as /// indices. /// /// The table is represented as a vector indexed by the ids' index /// values, so you should use an id allocator like `IdentityManager` /// that keeps the index values dense and close to zero. #[derive(Debug)] pub(crate) struct Storage where T: StorageItem, { pub(crate) map: Vec>, kind: &'static str, } impl Storage where T: StorageItem, { pub(crate) fn new() -> Self { Self { map: Vec::new(), kind: T::TYPE, } } } impl Storage where T: StorageItem, { pub(crate) fn insert(&mut self, id: Id, value: T) { let (index, epoch) = id.unzip(); let index = index as usize; if index >= self.map.len() { self.map.resize_with(index + 1, || Element::Vacant); } match std::mem::replace(&mut self.map[index], Element::Occupied(value, epoch)) { Element::Vacant => {} Element::Occupied(_, storage_epoch) => { assert_ne!( epoch, storage_epoch, "Index {index:?} of {} is already occupied", T::TYPE ); } } } pub(crate) fn remove(&mut self, id: Id) -> T { let (index, epoch) = id.unzip(); match std::mem::replace(&mut self.map[index as usize], Element::Vacant) { Element::Occupied(value, storage_epoch) => { assert_eq!(epoch, storage_epoch); value } Element::Vacant => panic!("Cannot remove a vacant resource"), } } pub(crate) fn iter(&self) -> impl Iterator, &T)> { self.map .iter() .enumerate() .filter_map(move |(index, x)| match *x { Element::Occupied(ref value, storage_epoch) => { Some((Id::zip(index as Index, storage_epoch), value)) } _ => None, }) } } impl Storage where T: StorageItem + Clone, { /// Get an owned reference to an item. /// Panics if there is an epoch mismatch, the entry is empty or in error. pub(crate) fn get(&self, id: Id) -> T { let (index, epoch) = id.unzip(); let (result, storage_epoch) = match self.map.get(index as usize) { Some(&Element::Occupied(ref v, epoch)) => (v.clone(), epoch), None | Some(&Element::Vacant) => panic!("{}[{:?}] does not exist", self.kind, id), }; assert_eq!( epoch, storage_epoch, "{}[{:?}] is no longer alive", self.kind, id ); result } }