use std::sync::atomic::{AtomicU64, Ordering};
use ahash::{HashMap, HashMapExt};
use maybe_sync::{MaybeSend, MaybeSync};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct FeatureId(u64);
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
impl FeatureId {
pub fn next() -> Self {
Self(NEXT_ID.fetch_add(1, Ordering::Relaxed))
}
}
pub trait FeatureStore<F>: MaybeSend + MaybeSync {
fn iter(&self) -> Box<dyn Iterator<Item = (FeatureId, &F)> + '_>;
fn iter_mut(&mut self) -> Box<dyn Iterator<Item = (FeatureId, &mut F)> + '_>;
fn get(&self, id: FeatureId) -> Option<&F>;
fn get_mut(&mut self, id: FeatureId) -> Option<&mut F>;
fn add(&mut self, feature: F) -> FeatureId;
fn remove(&mut self, id: FeatureId) -> Option<F>;
}
pub(super) struct VecFeatureStore<F> {
features: Vec<(FeatureId, F)>,
ids: HashMap<FeatureId, usize>,
}
impl<F> VecFeatureStore<F> {
pub(super) fn new(feature_iter: impl IntoIterator<Item = F>) -> Self {
let mut features = vec![];
let mut ids = HashMap::new();
for (index, feature) in feature_iter.into_iter().enumerate() {
let id = FeatureId::next();
features.push((id, feature));
ids.insert(id, index);
}
Self { features, ids }
}
}
impl<F> FeatureStore<F> for VecFeatureStore<F>
where
F: MaybeSync + MaybeSend,
{
fn iter(&self) -> Box<dyn Iterator<Item = (FeatureId, &F)> + '_> {
Box::new(self.features.iter().map(|(id, f)| (*id, f)))
}
fn iter_mut(&mut self) -> Box<dyn Iterator<Item = (FeatureId, &mut F)> + '_> {
Box::new(self.features.iter_mut().map(|(id, f)| (*id, f)))
}
fn get(&self, id: FeatureId) -> Option<&F> {
self.ids
.get(&id)
.and_then(|index| self.features.get(*index))
.map(|(_, f)| f)
}
fn get_mut(&mut self, id: FeatureId) -> Option<&mut F> {
self.ids
.get(&id)
.and_then(|index| self.features.get_mut(*index))
.map(|(_, f)| f)
}
fn add(&mut self, feature: F) -> FeatureId {
let id = FeatureId::next();
let index = self.features.len();
self.features.push((id, feature));
self.ids.insert(id, index);
id
}
fn remove(&mut self, id: FeatureId) -> Option<F> {
let index = self.ids.remove(&id)?;
let (_, feature) = self.features.remove(index);
for (to_adjust_id, _) in self.features.iter().skip(index) {
if let Some(index) = self.ids.get_mut(to_adjust_id) {
*index -= 1;
}
}
Some(feature)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vec_store_add_remove_features_preserves_indices() {
let mut store = VecFeatureStore::new([]);
let ids: Vec<_> = (0..5).map(|i| store.add(i)).collect();
store.remove(ids[2]);
assert_eq!(store.get(ids[2]), None);
assert_eq!(store.get(ids[0]), Some(&0));
assert_eq!(store.get(ids[1]), Some(&1));
assert_eq!(store.get(ids[3]), Some(&3));
assert_eq!(store.get(ids[4]), Some(&4));
}
}