stable_hash/fast/
hasher.rs

1use std::convert::TryInto;
2
3use super::fld::FldMix;
4use crate::prelude::*;
5
6#[derive(PartialEq, Eq, Hash, Clone, Debug)]
7pub struct FastStableHasher {
8    mixer: FldMix,
9    count: u64,
10}
11
12#[cfg(test)]
13impl FastStableHasher {
14    pub(crate) fn rand() -> Self {
15        use rand::thread_rng as rng;
16        use rand::Rng as _;
17        Self {
18            mixer: FldMix::rand(),
19            count: rng().gen(),
20        }
21    }
22}
23
24impl StableHasher for FastStableHasher {
25    type Out = u128;
26    type Addr = u128;
27    type Bytes = [u8; 32];
28
29    fn new() -> Self {
30        Self {
31            mixer: FldMix::new(),
32            count: 0,
33        }
34    }
35
36    fn mixin(&mut self, other: &Self) {
37        self.mixer.mixin(&other.mixer);
38        self.count = self.count.wrapping_add(other.count);
39    }
40
41    fn unmix(&mut self, other: &Self) {
42        self.mixer.unmix(&other.mixer);
43        self.count = self.count.wrapping_sub(other.count);
44    }
45
46    fn to_bytes(&self) -> Self::Bytes {
47        let mixer = self.mixer.to_bytes();
48        let count = self.count.to_le_bytes();
49
50        let mut bytes = [0; 32];
51        bytes[0..24].copy_from_slice(&mixer);
52        bytes[24..32].copy_from_slice(&count);
53
54        bytes
55    }
56
57    fn from_bytes(bytes: Self::Bytes) -> Self {
58        Self {
59            mixer: FldMix::from_bytes(bytes[0..24].try_into().unwrap()),
60            count: u64::from_le_bytes(bytes[24..32].try_into().unwrap()),
61        }
62    }
63
64    fn write(&mut self, field_address: Self::Addr, bytes: &[u8]) {
65        profile_method!(write);
66
67        // xxh3 128 has no weaknesses listed on SMHasher.
68        // It also is built for checksumming, meaning all bytes are accounted for.
69        // And it is the fastest, making it a clear choice.
70        // Also considered: t1ha3, MetroHash, SipHasher24
71        // For more information about XXH3, see this:
72        // https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html
73        let hash = xxhash_rust::xxh3::xxh3_128_with_seed(bytes, field_address as u64);
74        self.mixer.mix(hash, (field_address >> 64) as u64);
75        self.count += 1;
76    }
77
78    fn finish(&self) -> u128 {
79        profile_method!(finish);
80        xxhash_rust::xxh3::xxh3_128_with_seed(&self.mixer.to_bytes(), self.count)
81    }
82}