use std::{
collections::hash_map::{Entry, HashMap},
hash::Hash,
mem,
num::NonZeroUsize,
};
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct Token(NonZeroUsize);
#[derive(Debug)]
pub struct KeySet<Key: Eq + Hash> {
keys: HashMap<Key, Token>,
tokens: HashMap<Token, usize>,
}
impl<Key: Eq + Hash> KeySet<Key> {
#[inline]
pub fn is_empty(&self) -> bool {
self.tokens.is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.tokens.len()
}
pub fn keys(&self) -> impl Iterator<Item = &Key> + Clone {
let tokens = &self.tokens;
self.keys
.iter()
.filter(move |(_key, token)| tokens.contains_key(token))
.map(|(key, _token)| key)
}
pub fn into_values<Value>(self, mut get_value: impl FnMut(&Key) -> Value) -> ValueSet<Value> {
#[derive(Debug)]
enum Never {}
self.try_into_values(move |key| -> Result<Value, Never> { Ok(get_value(key)) })
.unwrap()
}
pub fn try_into_values<Value, Error>(
self,
mut get_value: impl FnMut(&Key) -> Result<Value, Error>,
) -> Result<ValueSet<Value>, Error> {
let KeySet { keys, tokens } = self;
let result: Result<HashMap<Token, ValueSetEntry<Value>>, Error> = keys
.into_iter()
.filter_map(move |(key, token)| {
let count = tokens.get(&token)?;
Some((key, token, *count))
})
.map(move |(key, token, count)| {
let value = get_value(&key)?;
Ok((token, ValueSetEntry { value, count }))
})
.collect();
result.map(move |values| ValueSet { values })
}
pub(crate) fn new() -> Self {
Self {
keys: HashMap::new(),
tokens: HashMap::new(),
}
}
pub(crate) fn add_key(&mut self, key: Key) -> Token {
let new_token = Token(NonZeroUsize::new(self.keys.len() + 1).unwrap());
let token = *self.keys.entry(key).or_insert(new_token);
self.tokens
.entry(token)
.and_modify(|count| *count += 1)
.or_insert(0);
token
}
pub(crate) fn discard_token(&mut self, token: Token) {
match self.tokens.entry(token) {
Entry::Occupied(entry) if *entry.get() == 0 => {
entry.remove();
}
Entry::Occupied(mut entry) => {
*entry.get_mut() -= 1;
}
Entry::Vacant(_) => panic!("Attempted to remove nonexistent token from KeySet"),
}
}
pub(crate) fn take(&mut self) -> Self {
Self {
keys: mem::take(&mut self.keys),
tokens: mem::take(&mut self.tokens),
}
}
}
#[derive(Debug)]
struct ValueSetEntry<Value> {
count: usize,
value: Value,
}
#[derive(Debug)]
enum MaybeRef<'a, T> {
Owned(T),
Ref(&'a T),
}
impl<'a, T: Clone> MaybeRef<'a, T> {
fn into_owned(self) -> T {
match self {
MaybeRef::Owned(value) => value,
MaybeRef::Ref(value) => value.clone(),
}
}
}
#[derive(Debug)]
pub struct ValueSet<Value> {
values: HashMap<Token, ValueSetEntry<Value>>,
}
impl<Value> ValueSet<Value> {
#[inline]
fn extract(&mut self, token: Token) -> Option<MaybeRef<Value>> {
match self.values.entry(token) {
Entry::Vacant(..) => None,
Entry::Occupied(entry) if entry.get().count == 0 => {
Some(MaybeRef::Owned(entry.remove().value))
}
Entry::Occupied(entry) => {
let entry = entry.into_mut();
entry.count -= 1;
Some(MaybeRef::Ref(&entry.value))
}
}
}
pub(crate) fn discard(&mut self, token: Token) {
let _value = self.extract(token);
}
}
impl<Value: Clone> ValueSet<Value> {
pub(crate) fn take(&mut self, token: Token) -> Option<Value> {
self.extract(token).map(|value| value.into_owned())
}
}