#![warn(clippy::pedantic, rust_2018_idioms, missing_docs)]
use std::{
collections::hash_map::RandomState,
fmt::Debug,
hash::{BuildHasher, Hash, Hasher as _},
marker::PhantomData,
mem::{replace, ManuallyDrop},
};
use hashbrown::{hash_table::Entry as RawEntry, HashTable};
use mut_guard::MutGuard;
#[doc(hidden)]
pub mod doc_examples;
pub mod entry;
#[doc(hidden)]
pub mod iter;
mod mut_guard;
#[cfg(feature = "serde")]
mod serde;
#[cfg(feature = "typesize")]
mod typesize;
#[cfg(feature = "serde")]
pub use serde::serialize_as_map;
fn hash_one<S: BuildHasher, H: Hash>(build_hasher: &S, val: H) -> u64 {
let mut hasher = build_hasher.build_hasher();
val.hash(&mut hasher);
hasher.finish()
}
pub trait ExtractKey<K: Hash + Eq> {
fn extract_key(&self) -> &K;
}
pub struct ExtractMap<K, V, S = RandomState> {
table: hashbrown::HashTable<V>,
phantom: PhantomData<K>,
build_hasher: S,
}
impl<K, V, S: Default> Default for ExtractMap<K, V, S> {
fn default() -> Self {
Self::with_hasher(S::default())
}
}
impl<K, V> ExtractMap<K, V, RandomState> {
#[must_use]
pub fn new() -> Self {
Self::with_hasher(RandomState::new())
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self::with_capacity_and_hasher(capacity, RandomState::new())
}
}
impl<K, V, S> ExtractMap<K, V, S> {
#[must_use]
pub fn with_hasher(hash_builder: S) -> Self {
Self {
table: HashTable::new(),
phantom: PhantomData,
build_hasher: hash_builder,
}
}
#[must_use]
pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> Self {
Self {
table: HashTable::with_capacity(capacity),
phantom: PhantomData,
build_hasher: hash_builder,
}
}
}
impl<K, V, S> ExtractMap<K, V, S>
where
K: Hash + Eq,
V: ExtractKey<K>,
S: BuildHasher,
{
fn raw_entry(&mut self, key: &K) -> RawEntry<'_, V> {
self.table.entry(
hash_one(&self.build_hasher, key),
|v| key == v.extract_key(),
|v| hash_one(&self.build_hasher, v.extract_key()),
)
}
pub fn insert(&mut self, value: V) -> Option<V> {
match self.raw_entry(value.extract_key()) {
RawEntry::Occupied(entry) => Some(replace(entry.into_mut(), value)),
RawEntry::Vacant(entry) => {
entry.insert(value);
None
}
}
}
pub fn remove(&mut self, key: &K) -> Option<V> {
let hash = hash_one(&self.build_hasher, key);
let entry = self.table.find_entry(hash, |v| key == v.extract_key());
match entry {
Ok(entry) => Some(entry.remove().0),
Err(_) => None,
}
}
#[must_use]
pub fn contains_key(&self, key: &K) -> bool {
self.get(key).is_some()
}
#[must_use]
pub fn get(&self, key: &K) -> Option<&V> {
let hash = hash_one(&self.build_hasher, key);
self.table.find(hash, |v| key == v.extract_key())
}
#[must_use]
pub fn get_mut<'a>(&'a mut self, key: &K) -> Option<MutGuard<'a, K, V, S>> {
let value = self.remove(key)?;
Some(MutGuard {
value: ManuallyDrop::new(value),
map: self,
})
}
}
impl<K, V, S> ExtractMap<K, V, S> {
#[must_use]
pub fn capacity(&self) -> usize {
self.table.capacity()
}
#[must_use]
pub fn len(&self) -> usize {
self.table.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.table.is_empty()
}
pub fn allocation_size(&self) -> usize {
self.table.allocation_size()
}
pub fn iter(&self) -> iter::Iter<'_, V> {
self.into_iter()
}
pub fn iter_mut(&mut self) -> iter::IterMut<'_, V> {
self.into_iter()
}
}
impl<K, V: Clone, S: Clone> Clone for ExtractMap<K, V, S> {
fn clone(&self) -> Self {
Self {
build_hasher: self.build_hasher.clone(),
table: self.table.clone(),
phantom: PhantomData,
}
}
fn clone_from(&mut self, source: &Self) {
self.table.clone_from(&source.table);
self.build_hasher.clone_from(&source.build_hasher);
}
}
impl<K, V, S> Debug for ExtractMap<K, V, S>
where
K: Debug + Hash + Eq,
V: Debug + ExtractKey<K>,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_map()
.entries(self.iter().map(|v| (v.extract_key(), v)))
.finish()
}
}
impl<K, V, S> PartialEq for ExtractMap<K, V, S>
where
K: Hash + Eq,
V: ExtractKey<K> + PartialEq,
S: BuildHasher,
{
fn eq(&self, other: &Self) -> bool {
if self.len() != other.len() {
return false;
}
self.iter().all(|v| {
let k = v.extract_key();
other.get(k).is_some_and(|other_v| {
let other_k = other_v.extract_key();
k == other_k && v == other_v
})
})
}
}
impl<K, V, S> FromIterator<V> for ExtractMap<K, V, S>
where
K: Hash + Eq,
V: ExtractKey<K>,
S: BuildHasher + Default,
{
fn from_iter<T: IntoIterator<Item = V>>(iter: T) -> Self {
let iter = iter.into_iter();
let mut this = Self::with_capacity_and_hasher(iter.size_hint().0, S::default());
for value in iter {
this.insert(value);
}
this
}
}
impl<K, V, S> Extend<V> for ExtractMap<K, V, S>
where
K: Hash + Eq,
V: ExtractKey<K>,
S: BuildHasher,
{
fn extend<T: IntoIterator<Item = V>>(&mut self, iter: T) {
for item in iter {
self.insert(item);
}
}
}