use std::{borrow::Borrow, fmt::Debug, hash::Hash, sync::Arc};
use scc::HashIndex;
use super::IndexUpdateCollection;
use crate::Record;
pub type HashIndexExtractor<Item, Id, HashValue> = fn(&Item, &Id) -> Option<HashValue>;
#[derive(Clone)]
pub struct CompositionIndex<Key, Item: Record> {
map: Arc<HashIndex<Key, Item::SelfId>>,
getter: HashIndexExtractor<Item, Item::SelfId, Key>,
}
impl<Key, Item> CompositionIndex<Key, Item>
where
Item: Record,
Item::SelfId: Copy,
Key: Hash + Eq + Clone + 'static,
{
pub fn new(capacity: usize, getter: HashIndexExtractor<Item, Item::SelfId, Key>) -> Self {
let map = HashIndex::with_capacity(capacity.max(1024));
let map = Arc::new(map);
Self { map, getter }
}
}
impl<Key, Item> CompositionIndex<Key, Item>
where
Item: Record,
Item::SelfId: Copy,
Key: Hash + Eq + Debug + Clone + 'static,
{
pub fn get<Q>(&self, field: &Q) -> Option<Item::SelfId>
where
Key: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.map.peek_with(field, |_, v| *v)
}
pub fn exist<Q>(&self, field: &Q) -> bool
where
Key: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.map.contains(field)
}
fn remove(&self, field: Key) {
if !self.map.remove_sync(&field) {
error!(?field, "index not exists");
}
}
}
impl<Key, Item> IndexUpdateCollection<Item> for CompositionIndex<Key, Item>
where
Key: Sync + Send + Eq + Hash + Debug + Clone + 'static,
Item: Record,
Item::SelfId: Sync + Send + Copy,
{
fn update(&self, id: &Item::SelfId, old: Option<&Item>, new: Option<&Item>) {
let getter = self.getter;
let old_key = old.and_then(|item| getter(item, id));
let new_key = new.and_then(|item| getter(item, id));
if old_key == new_key {
return;
}
match new_key {
Some(new_key) => {
if let Some(old_key) = old_key {
self.remove(old_key);
}
if let Err((field, _)) = self.map.insert_sync(new_key, *id) {
error!(?field, ?id, "index exists");
}
}
None => {
if let Some(old_key) = old_key {
self.remove(old_key);
}
}
}
}
fn batch_update(&self, items: &[crate::Entry<Item>]) {
let reserved = self
.map
.reserve(items.len())
.expect("items too large for indexing");
for item in items {
let (key, val) = item.split_ref();
let new_key = (self.getter)(val, key);
if let Some(new_key) = new_key {
let res = reserved.insert_sync(new_key, *key);
if let Err((field, _)) = res {
error!(?field, ?key, "index exists");
}
}
}
}
}