1use crate::map::{MutableZeroVecLike, ZeroMapKV, ZeroVecLike};
6use crate::ZeroVec;
7use alloc::vec::Vec;
8use core::borrow::Borrow;
9use core::hash::Hash;
10
11pub mod algorithms;
12use algorithms::*;
13
14#[cfg(feature = "serde")]
15mod serde;
16
17#[derive(Debug)]
30pub struct ZeroHashMap<'a, K, V>
31where
32 K: ZeroMapKV<'a> + ?Sized,
33 V: ZeroMapKV<'a> + ?Sized,
34{
35 displacements: ZeroVec<'a, (u32, u32)>,
41 keys: K::Container,
42 values: V::Container,
43}
44
45impl<'a, K, V> ZeroHashMap<'a, K, V>
46where
47 K: ZeroMapKV<'a> + ?Sized,
48 V: ZeroMapKV<'a> + ?Sized,
49{
50 pub fn len(&self) -> usize {
52 self.values.zvl_len()
53 }
54
55 pub fn is_empty(&self) -> bool {
57 self.len() == 0
58 }
59}
60
61impl<'a, K, V> ZeroHashMap<'a, K, V>
62where
63 K: ZeroMapKV<'a> + ?Sized + Hash + Eq,
64 V: ZeroMapKV<'a> + ?Sized,
65{
66 fn index<A>(&self, key: &A) -> Option<usize>
68 where
69 A: Borrow<K> + ?Sized,
70 {
71 let hash = compute_hash(key.borrow());
72 let (g, f0, f1) = split_hash64(hash, self.len());
73
74 #[expect(clippy::unwrap_used)] let (d0, d1) = self.displacements.get(g).unwrap();
76 let index = compute_index((f0, f1), (d0, d1), self.displacements.len() as u32)?;
77
78 #[expect(clippy::unwrap_used)] let found = self.keys.zvl_get(index).unwrap();
80 if K::Container::zvl_get_as_t(found, |found| found == key.borrow()) {
81 Some(index)
82 } else {
83 None
84 }
85 }
86
87 pub fn get<'b, A>(&'b self, key: &A) -> Option<&'b V::GetType>
101 where
102 A: Borrow<K> + ?Sized + 'b,
103 {
104 self.index(key).and_then(|i| self.values.zvl_get(i))
105 }
106
107 pub fn contains_key(&self, key: &K) -> bool {
119 self.index(key).is_some()
120 }
121}
122
123impl<'a, K, V> ZeroHashMap<'a, K, V>
124where
125 K: ZeroMapKV<'a> + ?Sized,
126 V: ZeroMapKV<'a> + ?Sized,
127{
128 pub fn iter<'b>(
130 &'b self,
131 ) -> impl ExactSizeIterator<
132 Item = (
133 &'b <K as ZeroMapKV<'a>>::GetType,
134 &'b <V as ZeroMapKV<'a>>::GetType,
135 ),
136 > {
137 (0..self.len()).map(|index| {
138 (
139 #[expect(clippy::unwrap_used)] self.keys.zvl_get(index).unwrap(),
141 #[expect(clippy::unwrap_used)] self.values.zvl_get(index).unwrap(),
143 )
144 })
145 }
146
147 pub fn iter_keys<'b>(
149 &'b self,
150 ) -> impl ExactSizeIterator<Item = &'b <K as ZeroMapKV<'a>>::GetType> {
151 #[expect(clippy::unwrap_used)] (0..self.len()).map(|index| self.keys.zvl_get(index).unwrap())
153 }
154
155 pub fn iter_values<'b>(
157 &'b self,
158 ) -> impl ExactSizeIterator<Item = &'b <V as ZeroMapKV<'a>>::GetType> {
159 #[expect(clippy::unwrap_used)] (0..self.len()).map(|index| self.values.zvl_get(index).unwrap())
161 }
162}
163
164impl<'a, K, V, A, B> FromIterator<(A, B)> for ZeroHashMap<'a, K, V>
165where
166 K: ZeroMapKV<'a> + ?Sized + Hash + Eq,
167 V: ZeroMapKV<'a> + ?Sized,
168 B: Borrow<V>,
169 A: Borrow<K>,
170{
171 fn from_iter<T: IntoIterator<Item = (A, B)>>(iter: T) -> Self {
189 let iter = iter.into_iter();
190 let size_hint = match iter.size_hint() {
191 (_, Some(upper)) => upper,
192 (lower, None) => lower,
193 };
194
195 let mut key_hashes = Vec::with_capacity(size_hint);
196 let mut keys = K::Container::zvl_with_capacity(size_hint);
197 let mut values = V::Container::zvl_with_capacity(size_hint);
198 for (k, v) in iter {
199 keys.zvl_push(k.borrow());
200 key_hashes.push(compute_hash(k.borrow()));
201 values.zvl_push(v.borrow());
202 }
203
204 let (displacements, mut reverse_mapping) = compute_displacements(key_hashes.into_iter());
205
206 keys.zvl_permute(&mut reverse_mapping.clone());
207 values.zvl_permute(&mut reverse_mapping);
208
209 Self {
210 displacements: ZeroVec::alloc_from_slice(&displacements),
211 values,
212 keys,
213 }
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220 use crate::ule::AsULE;
221 use rand::{distr::StandardUniform, Rng, SeedableRng};
222 use rand_pcg::Lcg64Xsh32;
223
224 #[test]
225 fn test_zhms_u64k_u64v() {
226 const N: usize = 65530;
227 let seed = u64::from_le_bytes(*b"testseed");
228 let rng = Lcg64Xsh32::seed_from_u64(seed);
229 let kv: Vec<(u64, u64)> = rng.sample_iter(&StandardUniform).take(N).collect();
230 let hashmap: ZeroHashMap<u64, u64> =
231 ZeroHashMap::from_iter(kv.iter().map(|e| (&e.0, &e.1)));
232 for (k, v) in kv {
233 assert_eq!(
234 hashmap.get(&k).copied().map(<u64 as AsULE>::from_unaligned),
235 Some(v),
236 );
237 }
238 }
239}