1use std::borrow::Borrow;
11use std::collections::HashMap;
12use std::hash::{Hash, Hasher};
13use std::mem::size_of_val;
14
15use num::{PrimInt, Unsigned};
16use wyhash::WyHash;
17
18use crate::mphf::{Mphf, MphfError, DEFAULT_GAMMA};
19
20#[derive(Default)]
22#[cfg_attr(feature = "rkyv_derive", derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize))]
23#[cfg_attr(feature = "rkyv_derive", archive_attr(derive(rkyv::CheckBytes)))]
24#[cfg_attr(feature = "serde", derive(serde::Serialize))]
25#[cfg_attr(
26 feature = "serde",
27 serde(bound(serialize = "K: serde::Serialize, V: serde::Serialize, ST: serde::Serialize"))
28)]
29pub struct MapWithDict<K, V, const B: usize = 32, const S: usize = 8, ST = u8, H = WyHash>
30where
31 ST: PrimInt + Unsigned,
32 H: Hasher + Default,
33{
34 mphf: Mphf<B, S, ST, H>,
36 keys: Box<[K]>,
38 values_index: Box<[usize]>,
40 values_dict: Box<[V]>,
42}
43
44#[cfg(feature = "serde")]
45#[derive(serde::Deserialize)]
46#[serde(bound(deserialize = "K: serde::Deserialize<'de>, V: serde::Deserialize<'de>, ST: serde::Deserialize<'de>"))]
47struct MapWithDictUnchecked<K, V, const B: usize = 32, const S: usize = 8, ST = u8, H = WyHash>
48where
49 ST: PrimInt + Unsigned,
50 H: Hasher + Default,
51{
52 mphf: Mphf<B, S, ST, H>,
53 keys: Box<[K]>,
54 values_index: Box<[usize]>,
55 values_dict: Box<[V]>,
56}
57
58#[cfg(feature = "serde")]
59impl<'de, K, V, const B: usize, const S: usize, ST, H> serde::Deserialize<'de> for MapWithDict<K, V, B, S, ST, H>
60where
61 K: serde::Deserialize<'de> + Hash,
62 V: serde::Deserialize<'de>,
63 ST: serde::Deserialize<'de> + PrimInt + Unsigned,
64 H: Hasher + Default,
65{
66 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
67 where
68 D: serde::Deserializer<'de>,
69 {
70 use crate::{ValidateKeyResult, ValidateValueResult};
71 use serde::de::Error;
72
73 let this = MapWithDictUnchecked::<K, V, B, S, ST, H>::deserialize(deserializer)?;
74
75 this.mphf.validate_keys(&this.keys).map_err(|e| match e {
76 ValidateKeyResult::InvalidKeyCount => {
77 Error::custom("key count should equal the number of set bits in the MPHF")
78 }
79 ValidateKeyResult::IncorrectKeyOrder => Error::custom("keys should correspond to MPHF index"),
80 })?;
81
82 this.mphf
83 .validate_values(&this.keys, &this.values_index, &this.values_dict)
84 .map_err(|e| match e {
85 ValidateValueResult::KeyValueLenMismatch => Error::custom("key count should equal value count"),
86 ValidateValueResult::InvalidValueIndex => Error::custom("value index is out of bounds of value dict"),
87 })?;
88
89 Ok(Self {
90 mphf: this.mphf,
91 keys: this.keys,
92 values_index: this.values_index,
93 values_dict: this.values_dict,
94 })
95 }
96}
97
98impl<K, V, const B: usize, const S: usize, ST, H> MapWithDict<K, V, B, S, ST, H>
99where
100 K: Eq + Hash + Clone,
101 V: Eq + Clone + Hash,
102 ST: PrimInt + Unsigned,
103 H: Hasher + Default,
104{
105 pub fn from_iter_with_params<I>(iter: I, gamma: f32) -> Result<Self, MphfError>
107 where
108 I: IntoIterator<Item = (K, V)>,
109 {
110 let mut keys = vec![];
111 let mut values_index = vec![];
112 let mut values_dict = vec![];
113 let mut offsets_cache = HashMap::new();
114
115 for (k, v) in iter {
116 keys.push(k.clone());
117
118 if let Some(&offset) = offsets_cache.get(&v) {
119 values_index.push(offset);
121 } else {
122 let offset = values_dict.len();
124 offsets_cache.insert(v.clone(), offset);
125 values_index.push(offset);
126 values_dict.push(v.clone());
127 }
128 }
129
130 let mphf = Mphf::from_slice(&keys, gamma)?;
131
132 for i in 0..keys.len() {
134 loop {
135 let idx = mphf.get(&keys[i]).unwrap();
136 if idx == i {
137 break;
138 }
139 keys.swap(i, idx);
140 values_index.swap(i, idx);
141 }
142 }
143
144 Ok(MapWithDict {
145 mphf,
146 keys: keys.into_boxed_slice(),
147 values_index: values_index.into_boxed_slice(),
148 values_dict: values_dict.into_boxed_slice(),
149 })
150 }
151
152 #[inline]
164 pub fn get<Q>(&self, key: &Q) -> Option<&V>
165 where
166 K: Borrow<Q> + PartialEq<Q>,
167 Q: Hash + Eq + ?Sized,
168 {
169 let idx = self.mphf.get(key)?;
170
171 unsafe {
173 if self.keys.get_unchecked(idx) == key {
174 let value_idx = *self.values_index.get_unchecked(idx);
176 Some(self.values_dict.get_unchecked(value_idx))
177 } else {
178 None
179 }
180 }
181 }
182
183 #[inline]
193 pub fn len(&self) -> usize {
194 self.keys.len()
195 }
196
197 #[inline]
209 pub fn is_empty(&self) -> bool {
210 self.keys.is_empty()
211 }
212
213 #[inline]
224 pub fn contains_key<Q>(&self, key: &Q) -> bool
225 where
226 K: Borrow<Q> + PartialEq<Q>,
227 Q: Hash + Eq + ?Sized,
228 {
229 if let Some(idx) = self.mphf.get(key) {
230 unsafe { self.keys.get_unchecked(idx) == key }
232 } else {
233 false
234 }
235 }
236
237 #[inline]
249 pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
250 self.keys
251 .iter()
252 .zip(self.values_index.iter())
253 .map(move |(key, &value_idx)| {
254 let value = unsafe { self.values_dict.get_unchecked(value_idx) };
256 (key, value)
257 })
258 }
259
260 #[inline]
272 pub fn keys(&self) -> impl Iterator<Item = &K> {
273 self.keys.iter()
274 }
275
276 #[inline]
288 pub fn values(&self) -> impl Iterator<Item = &V> {
289 self.values_index.iter().map(move |&value_idx| {
290 unsafe { self.values_dict.get_unchecked(value_idx) }
292 })
293 }
294
295 #[inline]
305 pub fn size(&self) -> usize {
306 size_of_val(self)
307 + self.mphf.size()
308 + size_of_val(self.keys.as_ref())
309 + size_of_val(self.values_index.as_ref())
310 + size_of_val(self.values_dict.as_ref())
311 }
312}
313
314impl<K, V> TryFrom<HashMap<K, V>> for MapWithDict<K, V>
316where
317 K: Eq + Hash + Clone,
318 V: Eq + Clone + Hash,
319{
320 type Error = MphfError;
321
322 #[inline]
323 fn try_from(value: HashMap<K, V>) -> Result<Self, Self::Error> {
324 MapWithDict::<K, V>::from_iter_with_params(value, DEFAULT_GAMMA)
325 }
326}
327
328#[cfg(feature = "rkyv_derive")]
330impl<K, V, const B: usize, const S: usize, ST, H> ArchivedMapWithDict<K, V, B, S, ST, H>
331where
332 K: PartialEq + Hash + rkyv::Archive,
333 K::Archived: PartialEq<K>,
334 V: rkyv::Archive,
335 ST: PrimInt + Unsigned + rkyv::Archive<Archived = ST>,
336 H: Hasher + Default,
337{
338 #[inline]
352 pub fn contains_key<Q>(&self, key: &Q) -> bool
353 where
354 K: Borrow<Q>,
355 <K as rkyv::Archive>::Archived: PartialEq<Q>,
356 Q: ?Sized + Hash + Eq,
357 {
358 if let Some(idx) = self.mphf.get(key) {
359 unsafe { self.keys.get_unchecked(idx) == key }
361 } else {
362 false
363 }
364 }
365
366 #[inline]
381 pub fn get<Q>(&self, key: &Q) -> Option<&V::Archived>
382 where
383 K: Borrow<Q>,
384 <K as rkyv::Archive>::Archived: PartialEq<Q>,
385 Q: ?Sized + Hash + Eq,
386 {
387 let idx = self.mphf.get(key)?;
388
389 unsafe {
391 if self.keys.get_unchecked(idx) == key {
392 let value_idx = *self.values_index.get_unchecked(idx) as usize;
394 Some(self.values_dict.get_unchecked(value_idx))
395 } else {
396 None
397 }
398 }
399 }
400
401 #[inline]
403 pub fn iter(&self) -> impl Iterator<Item = (&K::Archived, &V::Archived)> {
404 self.keys
405 .iter()
406 .zip(self.values_index.iter())
407 .map(move |(key, &value_idx)| {
408 let value = unsafe { self.values_dict.get_unchecked(value_idx as usize) };
410 (key, value)
411 })
412 }
413}
414
415#[cfg(test)]
416mod tests {
417 use super::*;
418 use paste::paste;
419 use proptest::prelude::*;
420 use rand::{Rng, SeedableRng};
421 use rand_chacha::ChaCha8Rng;
422 use std::collections::{hash_map::RandomState, HashSet};
423
424 fn gen_map(items_num: usize) -> HashMap<u64, u32> {
425 let mut rng = ChaCha8Rng::seed_from_u64(123);
426
427 (0..items_num)
428 .map(|_| {
429 let key = rng.gen::<u64>();
430 let value = rng.gen_range(1..=10);
431 (key, value)
432 })
433 .collect()
434 }
435
436 #[test]
437 fn test_map_with_dict() {
438 let original_map = gen_map(1000);
440
441 let map = MapWithDict::try_from(original_map.clone()).unwrap();
443
444 assert_eq!(map.len(), original_map.len());
446
447 assert_eq!(map.is_empty(), original_map.is_empty());
449
450 for (key, value) in &original_map {
452 assert_eq!(map.get(key), Some(value));
453 assert!(map.contains_key(key));
454 }
455
456 for (&k, &v) in map.iter() {
458 assert_eq!(original_map.get(&k), Some(&v));
459 }
460
461 for k in map.keys() {
463 assert!(original_map.contains_key(k));
464 }
465
466 for &v in map.values() {
468 assert!(original_map.values().any(|&val| val == v));
469 }
470
471 assert_eq!(map.size(), 16626);
473 }
474
475 #[test]
477 fn test_get_borrow() {
478 let original_map = HashMap::from_iter([("a".to_string(), ()), ("b".to_string(), ())]);
479 let map = MapWithDict::try_from(original_map).unwrap();
480
481 assert_eq!(map.get("a"), Some(&()));
482 assert!(map.contains_key("a"));
483 assert_eq!(map.get("b"), Some(&()));
484 assert!(map.contains_key("b"));
485 assert_eq!(map.get("c"), None);
486 assert!(!map.contains_key("c"));
487 }
488
489 #[cfg(feature = "rkyv_derive")]
490 #[test]
491 fn test_rkyv() {
492 let original_map = gen_map(1000);
494 let map = MapWithDict::try_from(original_map.clone()).unwrap();
495 let rkyv_bytes = rkyv::to_bytes::<_, 1024>(&map).unwrap();
496
497 let rkyv_map = rkyv::check_archived_root::<MapWithDict<u64, u32>>(&rkyv_bytes).unwrap();
498
499 for (k, v) in original_map.iter() {
501 assert_eq!(v, rkyv_map.get(k).unwrap());
502 }
503
504 for (&k, &v) in rkyv_map.iter() {
506 assert_eq!(original_map.get(&k), Some(&v));
507 }
508 }
509
510 #[cfg(feature = "rkyv_derive")]
511 #[test]
512 fn test_rkyv_get_borrow() {
513 let original_map = HashMap::from_iter([("a".to_string(), ()), ("b".to_string(), ())]);
514 let map = MapWithDict::try_from(original_map).unwrap();
515 let rkyv_bytes = rkyv::to_bytes::<_, 1024>(&map).unwrap();
516 let rkyv_map = rkyv::check_archived_root::<MapWithDict<String, ()>>(&rkyv_bytes).unwrap();
517
518 assert_eq!(map.get("a"), Some(&()));
519 assert!(rkyv_map.contains_key("a"));
520 assert_eq!(map.get("b"), Some(&()));
521 assert!(rkyv_map.contains_key("b"));
522 assert_eq!(map.get("c"), None);
523 assert!(!rkyv_map.contains_key("c"));
524 }
525
526 #[cfg(feature = "serde")]
527 #[test]
528 fn test_serde() {
529 let original_map = gen_map(1000);
531 let map = MapWithDict::try_from(original_map.clone()).unwrap();
532
533 let bytes = rmp_serde::to_vec(&map).unwrap();
534 let de: MapWithDict<u64, u32> = rmp_serde::from_slice(&bytes).unwrap();
535
536 assert_eq!(de.len(), original_map.len());
537
538 for (k, v) in original_map.iter() {
540 assert_eq!(de.get(k), Some(v));
541 }
542
543 for (&k, &v) in de.iter() {
545 assert_eq!(original_map.get(&k), Some(&v));
546 }
547 }
548
549 #[cfg(feature = "serde")]
550 #[test]
551 fn test_serde_get_borrow() {
552 let original_map = HashMap::from_iter([("a".to_string(), ()), ("b".to_string(), ())]);
553 let map = MapWithDict::try_from(original_map).unwrap();
554 let bytes = rmp_serde::to_vec(&map).unwrap();
555 let de: MapWithDict<String, ()> = rmp_serde::from_slice(&bytes).unwrap();
556
557 assert_eq!(de.get("a"), Some(&()));
558 assert!(de.contains_key("a"));
559 assert_eq!(de.get("b"), Some(&()));
560 assert!(de.contains_key("b"));
561 assert_eq!(de.get("c"), None);
562 assert!(!de.contains_key("c"));
563 }
564
565 macro_rules! proptest_map_with_dict_model {
566 ($(($b:expr, $s:expr, $gamma:expr)),* $(,)?) => {
567 $(
568 paste! {
569 proptest! {
570 #[test]
571 fn [<proptest_map_with_dict_model_ $b _ $s _ $gamma>](model: HashMap<u64, u64>, arbitrary: HashSet<u64>) {
572 let entropy_map: MapWithDict<u64, u64, $b, $s> = MapWithDict::from_iter_with_params(
573 model.clone(),
574 $gamma as f32 / 100.0
575 ).unwrap();
576
577 assert_eq!(entropy_map.len(), model.len());
579 assert_eq!(entropy_map.is_empty(), model.is_empty());
580
581 assert_eq!(
583 HashSet::<_, RandomState>::from_iter(entropy_map.keys()),
584 HashSet::from_iter(model.keys())
585 );
586 assert_eq!(
587 HashSet::<_, RandomState>::from_iter(entropy_map.values()),
588 HashSet::from_iter(model.values())
589 );
590
591 for (k, v) in &model {
593 assert!(entropy_map.contains_key(&k));
594 assert_eq!(entropy_map.get(&k), Some(v));
595 }
596
597 for k in arbitrary {
599 assert_eq!(
600 model.contains_key(&k),
601 entropy_map.contains_key(&k),
602 );
603 assert_eq!(entropy_map.get(&k), model.get(&k));
604 }
605 }
606 }
607 }
608 )*
609 };
610 }
611
612 proptest_map_with_dict_model!(
613 (2, 8, 100),
615 (4, 8, 100),
616 (7, 8, 100),
617 (8, 8, 100),
618 (15, 8, 100),
619 (16, 8, 100),
620 (23, 8, 100),
621 (24, 8, 100),
622 (31, 8, 100),
623 (32, 8, 100),
624 (33, 8, 100),
625 (48, 8, 100),
626 (53, 8, 100),
627 (61, 8, 100),
628 (63, 8, 100),
629 (64, 8, 100),
630 (32, 7, 100),
631 (32, 5, 100),
632 (32, 4, 100),
633 (32, 3, 100),
634 (32, 1, 100),
635 (32, 0, 100),
636 (32, 8, 200),
637 (32, 6, 200),
638 );
639}