use crate::map::{MutableZeroVecLike, ZeroMapKV, ZeroVecLike};
use crate::ZeroVec;
use alloc::vec::Vec;
use core::borrow::Borrow;
use core::hash::Hash;
pub mod algorithms;
use algorithms::*;
#[cfg(feature = "serde")]
mod serde;
#[derive(Debug)]
pub struct ZeroHashMap<'a, K, V>
where
K: ZeroMapKV<'a> + ?Sized,
V: ZeroMapKV<'a> + ?Sized,
{
displacements: ZeroVec<'a, (u32, u32)>,
keys: K::Container,
values: V::Container,
}
impl<'a, K, V> ZeroHashMap<'a, K, V>
where
K: ZeroMapKV<'a> + ?Sized,
V: ZeroMapKV<'a> + ?Sized,
{
pub fn len(&self) -> usize {
self.values.zvl_len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<'a, K, V> ZeroHashMap<'a, K, V>
where
K: ZeroMapKV<'a> + ?Sized + Hash + Eq,
V: ZeroMapKV<'a> + ?Sized,
{
fn index<A>(&self, key: &A) -> Option<usize>
where
A: Borrow<K> + ?Sized,
{
let hash = compute_hash(key.borrow());
let (g, f0, f1) = split_hash64(hash, self.len());
#[expect(clippy::unwrap_used)] let (d0, d1) = self.displacements.get(g).unwrap();
let index = compute_index((f0, f1), (d0, d1), self.displacements.len() as u32)?;
#[expect(clippy::unwrap_used)] let found = self.keys.zvl_get(index).unwrap();
if K::Container::zvl_get_as_t(found, |found| found == key.borrow()) {
Some(index)
} else {
None
}
}
pub fn get<'b, A>(&'b self, key: &A) -> Option<&'b V::GetType>
where
A: Borrow<K> + ?Sized + 'b,
{
self.index(key).and_then(|i| self.values.zvl_get(i))
}
pub fn contains_key(&self, key: &K) -> bool {
self.index(key).is_some()
}
}
impl<'a, K, V> ZeroHashMap<'a, K, V>
where
K: ZeroMapKV<'a> + ?Sized,
V: ZeroMapKV<'a> + ?Sized,
{
pub fn iter<'b>(
&'b self,
) -> impl ExactSizeIterator<
Item = (
&'b <K as ZeroMapKV<'a>>::GetType,
&'b <V as ZeroMapKV<'a>>::GetType,
),
> {
(0..self.len()).map(|index| {
(
#[expect(clippy::unwrap_used)] self.keys.zvl_get(index).unwrap(),
#[expect(clippy::unwrap_used)] self.values.zvl_get(index).unwrap(),
)
})
}
pub fn iter_keys<'b>(
&'b self,
) -> impl ExactSizeIterator<Item = &'b <K as ZeroMapKV<'a>>::GetType> {
#[expect(clippy::unwrap_used)] (0..self.len()).map(|index| self.keys.zvl_get(index).unwrap())
}
pub fn iter_values<'b>(
&'b self,
) -> impl ExactSizeIterator<Item = &'b <V as ZeroMapKV<'a>>::GetType> {
#[expect(clippy::unwrap_used)] (0..self.len()).map(|index| self.values.zvl_get(index).unwrap())
}
}
impl<'a, K, V, A, B> FromIterator<(A, B)> for ZeroHashMap<'a, K, V>
where
K: ZeroMapKV<'a> + ?Sized + Hash + Eq,
V: ZeroMapKV<'a> + ?Sized,
B: Borrow<V>,
A: Borrow<K>,
{
fn from_iter<T: IntoIterator<Item = (A, B)>>(iter: T) -> Self {
let iter = iter.into_iter();
let size_hint = match iter.size_hint() {
(_, Some(upper)) => upper,
(lower, None) => lower,
};
let mut key_hashes = Vec::with_capacity(size_hint);
let mut keys = K::Container::zvl_with_capacity(size_hint);
let mut values = V::Container::zvl_with_capacity(size_hint);
for (k, v) in iter {
keys.zvl_push(k.borrow());
key_hashes.push(compute_hash(k.borrow()));
values.zvl_push(v.borrow());
}
let (displacements, mut reverse_mapping) = compute_displacements(key_hashes.into_iter());
keys.zvl_permute(&mut reverse_mapping.clone());
values.zvl_permute(&mut reverse_mapping);
Self {
displacements: ZeroVec::alloc_from_slice(&displacements),
values,
keys,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ule::AsULE;
use rand::{distr::StandardUniform, Rng, SeedableRng};
use rand_pcg::Lcg64Xsh32;
#[test]
fn test_zhms_u64k_u64v() {
const N: usize = 65530;
let seed = u64::from_le_bytes(*b"testseed");
let rng = Lcg64Xsh32::seed_from_u64(seed);
let kv: Vec<(u64, u64)> = rng.sample_iter(&StandardUniform).take(N).collect();
let hashmap: ZeroHashMap<u64, u64> =
ZeroHashMap::from_iter(kv.iter().map(|e| (&e.0, &e.1)));
for (k, v) in kv {
assert_eq!(
hashmap.get(&k).copied().map(<u64 as AsULE>::from_unaligned),
Some(v),
);
}
}
}