1use commonware_utils::GOLDEN_RATIO;
4use core::hash::{BuildHasher, Hash, Hasher};
5
6pub trait Translator: Clone + BuildHasher + Send + Sync + 'static {
13 type Key: Ord + Hash + Copy + Send + Sync;
20
21 fn transform(&self, key: &[u8]) -> Self::Key;
23}
24
25pub trait OrderedTranslator: Translator {}
30
31#[derive(Default, Clone)]
46pub struct UintIdentity {
47 value: u64,
48}
49
50impl Hasher for UintIdentity {
51 #[inline]
52 fn write(&mut self, bytes: &[u8]) {
53 assert!(bytes.len() <= 8, "UintIdenty hasher cannot handle >8 bytes");
54 self.value = u64::from_le_bytes(cap::<8>(bytes));
57 }
58
59 #[inline]
60 fn write_u8(&mut self, i: u8) {
61 self.value = i as u64;
62 }
63
64 #[inline]
65 fn write_u16(&mut self, i: u16) {
66 self.value = i as u64;
67 }
68
69 #[inline]
70 fn write_u32(&mut self, i: u32) {
71 self.value = i as u64;
72 }
73
74 #[inline]
75 fn write_u64(&mut self, i: u64) {
76 self.value = i;
77 }
78
79 #[inline]
80 fn finish(&self) -> u64 {
81 self.value.wrapping_mul(GOLDEN_RATIO)
85 }
86}
87
88fn cap<const N: usize>(key: &[u8]) -> [u8; N] {
96 let mut capped = [0; N];
97 let len = key.len().min(N);
98 capped[..len].copy_from_slice(&key[..len]);
99 capped
100}
101
102macro_rules! define_cap_translator {
103 ($name:ident, $size:expr, $int:ty) => {
104 #[doc = concat!("Translator that caps the key to ", stringify!($size), " byte(s) and returns it packed in a ", stringify!($int), ".")]
105 #[derive(Clone, Default)]
109 pub struct $name;
110
111 impl Translator for $name {
112 type Key = $int;
114
115 #[inline]
116 fn transform(&self, key: &[u8]) -> Self::Key {
117 let capped = cap::<$size>(key);
118 <$int>::from_be_bytes(capped)
119 }
120 }
121
122 impl OrderedTranslator for $name {}
123
124 impl BuildHasher for $name {
126 type Hasher = UintIdentity;
127
128 #[inline]
129 fn build_hasher(&self) -> Self::Hasher {
130 UintIdentity::default()
131 }
132 }
133 };
134}
135
136define_cap_translator!(OneCap, 1, u8);
137define_cap_translator!(TwoCap, 2, u16);
138define_cap_translator!(FourCap, 4, u32);
139define_cap_translator!(EightCap, 8, u64);
140
141#[derive(Clone, Copy, PartialEq, Eq, Debug)]
145pub struct UnhashedArray<const N: usize> {
146 pub inner: [u8; N],
147}
148
149impl<const N: usize> UnhashedArray<N> {
150 #[inline]
156 fn word(&self) -> u64 {
157 let mut word = [0u8; 8];
158 word[..N].copy_from_slice(&self.inner);
159 u64::from_be_bytes(word)
160 }
161}
162
163impl<const N: usize> Ord for UnhashedArray<N> {
164 #[inline]
165 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
166 if N <= 8 {
169 return self.word().cmp(&other.word());
170 }
171 self.inner.cmp(&other.inner)
172 }
173}
174
175impl<const N: usize> PartialOrd for UnhashedArray<N> {
176 #[inline]
177 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
178 Some(self.cmp(other))
179 }
180}
181
182impl<const N: usize> Hash for UnhashedArray<N> {
183 #[inline]
184 fn hash<H: Hasher>(&self, state: &mut H) {
185 state.write(&self.inner);
186 }
187}
188
189impl<const N: usize> PartialEq<[u8; N]> for UnhashedArray<N> {
190 fn eq(&self, other: &[u8; N]) -> bool {
191 &self.inner == other
192 }
193}
194
195#[derive(Clone, Copy)]
199pub struct Cap<const N: usize>;
200
201impl<const N: usize> Cap<N> {
202 pub const fn new() -> Self {
203 const {
204 assert!(N <= 8 && N > 0, "Cap must be between 1 and 8");
205 };
206 Self
207 }
208}
209
210impl<const N: usize> Default for Cap<N> {
212 fn default() -> Self {
213 Self::new()
214 }
215}
216
217impl<const N: usize> Translator for Cap<N> {
218 type Key = UnhashedArray<N>;
219
220 #[inline]
221 fn transform(&self, key: &[u8]) -> Self::Key {
222 const {
223 assert!(N <= 8 && N > 0, "Cap must be between 1 and 8");
224 };
225 UnhashedArray {
226 inner: cap::<N>(key),
227 }
228 }
229}
230
231impl<const N: usize> OrderedTranslator for Cap<N> {}
232
233impl<const N: usize> BuildHasher for Cap<N> {
234 type Hasher = UintIdentity;
235
236 #[inline]
237 fn build_hasher(&self) -> Self::Hasher {
238 UintIdentity::default()
239 }
240}
241
242#[derive(Clone)]
300pub struct Hashed<T: Translator> {
301 random_state: ahash::RandomState,
302 inner: T,
303}
304
305#[cfg(feature = "std")]
306impl<T: Translator + Default> Default for Hashed<T> {
307 fn default() -> Self {
308 Self::new(T::default())
309 }
310}
311
312impl<T: Translator> Hashed<T> {
313 #[cfg(feature = "std")]
315 pub fn new(inner: T) -> Self {
316 Self {
317 random_state: ahash::RandomState::new(),
318 inner,
319 }
320 }
321
322 pub const fn from_seed(seed: u64, inner: T) -> Self {
324 Self {
325 random_state: ahash::RandomState::with_seeds(seed, 0, 0, 0),
326 inner,
327 }
328 }
329}
330
331impl<T: Translator> Translator for Hashed<T> {
332 type Key = T::Key;
333
334 #[inline]
335 fn transform(&self, key: &[u8]) -> T::Key {
336 let hash_val = self.random_state.hash_one(key);
337 self.inner.transform(&hash_val.to_le_bytes())
338 }
339}
340
341impl<T: Translator> BuildHasher for Hashed<T> {
342 type Hasher = <T as BuildHasher>::Hasher;
343
344 #[inline]
345 fn build_hasher(&self) -> Self::Hasher {
346 self.inner.build_hasher()
347 }
348}
349
350#[cfg(test)]
351mod tests {
352 use super::*;
353 use commonware_utils::test_rng;
354 use core::hash::Hasher;
355 use rand::RngExt as _;
356
357 #[test]
358 fn test_one_cap() {
359 let t = OneCap;
360 assert_eq!(t.transform(b""), 0);
361 assert_eq!(t.transform(b"a"), b'a');
362 assert_eq!(t.transform(b"ab"), b'a');
363 assert_eq!(t.transform(b"abc"), b'a');
364 }
365
366 #[test]
367 fn test_two_cap() {
368 let t = TwoCap;
369 assert_eq!(t.transform(b""), 0);
370 assert_eq!(t.transform(b"abc"), t.transform(b"ab"));
371 assert!(t.transform(b"") < t.transform(b"a"));
372 assert!(t.transform(b"a") < t.transform(b"b"));
373 assert!(t.transform(b"ab") < t.transform(b"ac"));
374 assert!(t.transform(b"z") < t.transform(b"zz"));
375 assert_eq!(t.transform(b"zz"), t.transform(b"zzabc"));
376 }
377
378 #[test]
379 fn test_four_cap() {
380 let t = FourCap;
381 let t1 = t.transform(b"");
382 let t2 = t.transform(b"a");
383 let t3 = t.transform(b"abcd");
384 let t4 = t.transform(b"abcdef");
385 let t5 = t.transform(b"b");
386
387 assert_eq!(t1, 0);
388 assert!(t1 < t2);
389 assert!(t2 < t3);
390 assert_eq!(t3, t4);
391 assert!(t3 < t5);
392 assert!(t4 < t5);
393 }
394
395 #[test]
396 fn test_unhashed_array_orders_like_its_bytes() {
397 fn check<const N: usize>(rng: &mut impl rand::Rng) {
398 let mut arrays: Vec<UnhashedArray<N>> = (0..200)
400 .map(|_| {
401 let mut inner = [0u8; N];
402 rng.fill_bytes(&mut inner);
403 UnhashedArray { inner }
404 })
405 .collect();
406
407 let mut last = [0xab; N];
409 arrays.push(UnhashedArray { inner: last });
410 last[N - 1] = 0xac;
411 arrays.push(UnhashedArray { inner: last });
412 arrays.push(UnhashedArray { inner: [0xff; N] });
413 arrays.push(UnhashedArray { inner: [0; N] });
414
415 for a in &arrays {
417 for b in &arrays {
418 assert_eq!(a.cmp(b), a.inner.cmp(&b.inner), "{a:?} vs {b:?}");
419 assert_eq!(a.partial_cmp(b), Some(a.inner.cmp(&b.inner)));
420 }
421 }
422 }
423
424 let mut rng = test_rng();
426 check::<1>(&mut rng);
427 check::<2>(&mut rng);
428 check::<3>(&mut rng);
429 check::<4>(&mut rng);
430 check::<5>(&mut rng);
431 check::<6>(&mut rng);
432 check::<7>(&mut rng);
433 check::<8>(&mut rng);
434 check::<9>(&mut rng);
435 }
436
437 #[test]
438 fn test_cap_3() {
439 let t = Cap::<3>::new();
440 assert_eq!(t.transform(b""), [0; 3]);
441 assert_eq!(t.transform(b"abc"), *b"abc");
442 assert_eq!(t.transform(b"abcdef"), *b"abc");
443 assert_eq!(t.transform(b"ab"), [b'a', b'b', 0]);
444 }
445
446 #[test]
447 fn test_cap_6() {
448 let t = Cap::<6>::new();
449 assert_eq!(t.transform(b""), [0; 6]);
450 assert_eq!(t.transform(b"abcdef"), *b"abcdef");
451 assert_eq!(t.transform(b"abcdefghi"), *b"abcdef");
452 assert_eq!(t.transform(b"abc"), [b'a', b'b', b'c', 0, 0, 0]);
453 }
454
455 #[test]
456 fn test_eight_cap() {
457 let t = EightCap;
458 let t1 = t.transform(b"");
459 let t2 = t.transform(b"a");
460 let t3 = t.transform(b"abcdefghaaaaaaa");
461 let t4 = t.transform(b"abcdefghijkzzzzzzzzzzzzzzzzzz");
462 let t5 = t.transform(b"b");
463
464 assert_eq!(t1, 0);
465 assert!(t1 < t2);
466 assert!(t2 < t3);
467 assert_eq!(t3, t4);
468 assert!(t3 < t5);
469 assert!(t4 < t5);
470 }
471
472 fn assert_order_preserving<T: OrderedTranslator>(t: T) {
474 const ALPHABET: [u8; 6] = [0x00, 0x01, 0x7F, 0x80, 0xFE, 0xFF];
476 let mut rng = test_rng();
477 let mut keys: Vec<Vec<u8>> = (0..10_000)
478 .map(|_| {
479 let len = rng.random_range(0..=10);
480 (0..len)
481 .map(|_| ALPHABET[rng.random_range(0..ALPHABET.len())])
482 .collect()
483 })
484 .collect();
485 keys.sort();
486 for pair in keys.windows(2) {
487 assert!(t.transform(&pair[0]) <= t.transform(&pair[1]), "{pair:?}");
488 }
489 }
490
491 #[test]
492 fn test_cap_translators_preserve_order() {
493 assert_order_preserving(OneCap);
494 assert_order_preserving(TwoCap);
495 assert_order_preserving(FourCap);
496 assert_order_preserving(EightCap);
497 assert_order_preserving(Cap::<1>::new());
498 assert_order_preserving(Cap::<3>::new());
499 assert_order_preserving(Cap::<5>::new());
500 assert_order_preserving(Cap::<8>::new());
501 }
502
503 #[test]
504 fn identity_hasher_small_slices_differ() {
505 let hash = |bytes: &[u8]| {
506 let mut h = UintIdentity::default();
507 h.write(bytes);
508 h.finish()
509 };
510 assert_ne!(hash(b"abc"), hash(b"abd"));
511 assert_ne!(hash(b"a"), hash(b"b"));
512 assert_ne!(hash(b""), hash(b"a"));
513 }
514
515 #[test]
516 fn identity_hasher_sets_high_bits() {
517 for i in [1u64, 7, 17, 255] {
520 let mut h = UintIdentity::default();
521 h.write_u64(i);
522 assert_ne!(h.finish() >> 57, 0, "high bits all zero for input {i}");
523 }
524 }
525
526 #[test]
527 fn identity_hasher_integer_writes_differ() {
528 let hash_u8 = |v: u8| {
529 let mut h = UintIdentity::default();
530 h.write_u8(v);
531 h.finish()
532 };
533 let hash_u16 = |v: u16| {
534 let mut h = UintIdentity::default();
535 h.write_u16(v);
536 h.finish()
537 };
538 let hash_u32 = |v: u32| {
539 let mut h = UintIdentity::default();
540 h.write_u32(v);
541 h.finish()
542 };
543 let hash_u64 = |v: u64| {
544 let mut h = UintIdentity::default();
545 h.write_u64(v);
546 h.finish()
547 };
548 assert_ne!(hash_u8(0), hash_u8(1));
549 assert_ne!(hash_u16(0), hash_u16(1));
550 assert_ne!(hash_u32(0), hash_u32(1));
551 assert_ne!(hash_u64(0), hash_u64(1));
552
553 assert_eq!(hash_u8(7), hash_u16(7));
555 assert_eq!(hash_u16(7), hash_u32(7));
556 assert_eq!(hash_u32(7), hash_u64(7));
557 }
558
559 #[test]
560 #[should_panic]
561 fn identity_hasher_panics_on_large_write_slice() {
562 let mut h = UintIdentity::default();
563 h.write(b"too big for an int");
564 }
565
566 #[test]
567 fn test_hashed_consistency() {
568 let t = Hashed::from_seed(42, TwoCap);
569 assert_eq!(t.transform(b"hello"), t.transform(b"hello"));
570 assert_eq!(t.transform(b""), t.transform(b""));
571 assert_eq!(t.transform(b"abcdef"), t.transform(b"abcdef"));
572 }
573
574 #[test]
575 fn test_hashed_seed_determinism() {
576 let t1 = Hashed::from_seed(42, TwoCap);
577 let t2 = Hashed::from_seed(42, TwoCap);
578 assert_eq!(t1.transform(b"hello"), t2.transform(b"hello"));
579 assert_eq!(t1.transform(b"world"), t2.transform(b"world"));
580 }
581
582 #[test]
583 fn test_hashed_seed_independence() {
584 let t1 = Hashed::from_seed(1, EightCap);
585 let t2 = Hashed::from_seed(2, EightCap);
586 assert_ne!(t1.transform(b"hello"), t2.transform(b"hello"));
588 }
589
590 #[test]
591 fn test_hashed_prefix_collisions_avoided() {
592 let t = Hashed::from_seed(99, TwoCap);
594 let k1 = t.transform(b"abXXX");
595 let k2 = t.transform(b"abYYY");
596 assert_ne!(k1, k2);
598 }
599
600 #[test]
601 fn test_hashed_all_cap_sizes() {
602 let t1 = Hashed::from_seed(7, OneCap);
603 assert_eq!(t1.transform(b"test"), t1.transform(b"test"));
604
605 let t4 = Hashed::from_seed(7, FourCap);
606 assert_eq!(t4.transform(b"test"), t4.transform(b"test"));
607
608 let t8 = Hashed::from_seed(7, EightCap);
609 assert_eq!(t8.transform(b"test"), t8.transform(b"test"));
610
611 let tc = Hashed::from_seed(7, Cap::<3>::new());
612 assert_eq!(tc.transform(b"test"), tc.transform(b"test"));
613 }
614
615 #[cfg(feature = "std")]
616 #[test]
617 fn test_hashed_random_seed() {
618 let t1 = Hashed::new(EightCap);
621 let t2 = Hashed::new(EightCap);
622 assert_ne!(t1.transform(b"hello"), t2.transform(b"hello"));
623 }
624}