use rustc_hash::FxHashMap;
use std::hash::Hash;
#[derive(Debug, Clone)]
pub struct SubsetMap<K, V> {
map: FxHashMap<K, Vec<SsmKeyValue<K, V>>>,
}
#[derive(Debug, Clone)]
struct SsmKeyValue<K, V> {
key: Box<[K]>,
value: V,
}
impl<K, V> SsmKeyValue<K, V>
where
K: Clone,
{
fn ssmkv_new(key: impl AsRef<[K]>, value: V) -> Self {
Self {
key: key.as_ref().to_vec().into_boxed_slice(),
value,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum GetOrIsSubsetOfKnownKey<T> {
HasValue(T),
IsSubset,
Neither,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum SsmKeyExistedBeforeInsert {
Existed,
NotThere,
}
use GetOrIsSubsetOfKnownKey::*;
impl<K, V> Default for SubsetMap<K, V>
where
K: Clone + PartialEq + Ord + Hash,
V: Clone,
{
fn default() -> Self {
Self::ssm_new()
}
}
impl<K, V> SubsetMap<K, V>
where
K: Clone + PartialEq + Ord + Hash,
V: Clone,
{
pub fn ssm_new() -> Self {
Self {
map: FxHashMap::default(),
}
}
pub fn ssm_insert(&mut self, mut key: impl AsMut<[K]>, val: V) -> SsmKeyExistedBeforeInsert {
key.as_mut().sort();
self.ssm_insert_ksorted(key.as_mut(), val)
}
pub fn ssm_insert_ksorted(
&mut self,
key: impl AsRef<[K]>,
val: V,
) -> SsmKeyExistedBeforeInsert {
let mut key_existed = SsmKeyExistedBeforeInsert::NotThere;
for k in key.as_ref().iter().cloned() {
let keyvals_for_key_item = self.map.entry(k).or_default();
match keyvals_for_key_item
.binary_search_by(|probe| probe.key.as_ref().cmp(key.as_ref()))
{
Ok(pos) => {
key_existed = SsmKeyExistedBeforeInsert::Existed;
keyvals_for_key_item[pos] = SsmKeyValue::ssmkv_new(key.as_ref(), val.clone());
}
Err(pos) => {
keyvals_for_key_item
.insert(pos, SsmKeyValue::ssmkv_new(key.as_ref(), val.clone()));
}
}
}
key_existed
}
pub fn ssm_get_or_is_subset(&self, mut key: impl AsMut<[K]>) -> GetOrIsSubsetOfKnownKey<V> {
key.as_mut().sort();
self.ssm_get_or_is_subset_ksorted(key.as_mut())
}
pub fn ssm_get_or_is_subset_ksorted(
&self,
get_key: impl AsRef<[K]>,
) -> GetOrIsSubsetOfKnownKey<V> {
let get_key = get_key.as_ref();
if get_key.is_empty() {
return match self.is_empty() {
true => Neither,
false => IsSubset,
};
}
match self.map.get(&get_key[0]) {
None => Neither,
Some(keyvals_for_key_item) => {
match keyvals_for_key_item
.binary_search_by(|probe| probe.key.as_ref().cmp(get_key.as_ref()))
{
Ok(pos) => HasValue(keyvals_for_key_item[pos].value.clone()),
Err(_) => {
for kv in keyvals_for_key_item.iter() {
if get_key.iter().all(|kitem| kv.key.contains(kitem)) {
return IsSubset;
}
}
Neither
}
}
}
}
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
}