Skip to main content

sofie_core/
fnv.rs

1/// FNV-1a 64: the same in every process and release, which stored migration checksums
2/// and flag rollout buckets rely on; not collision-resistant.
3#[derive(Clone, Copy)]
4pub struct Fnv1a64(u64);
5
6impl Fnv1a64 {
7    const PRIME: u64 = 0x0000_0100_0000_01b3;
8
9    #[must_use]
10    pub const fn new() -> Self {
11        Self(0xcbf2_9ce4_8422_2325)
12    }
13
14    #[must_use]
15    pub const fn write(self, bytes: &[u8]) -> Self {
16        let mut hash = self.0;
17        let mut index = 0;
18        while index < bytes.len() {
19            hash ^= bytes[index] as u64;
20            hash = hash.wrapping_mul(Self::PRIME);
21            index += 1;
22        }
23        Self(hash)
24    }
25
26    #[must_use]
27    pub const fn finish(self) -> u64 {
28        self.0
29    }
30}
31
32impl Default for Fnv1a64 {
33    fn default() -> Self {
34        Self::new()
35    }
36}
37
38#[cfg(test)]
39mod tests {
40    use super::Fnv1a64;
41
42    fn hash(bytes: &[u8]) -> u64 {
43        Fnv1a64::new().write(bytes).finish()
44    }
45
46    #[test]
47    fn hashes_like_the_reference_fnv1a() {
48        assert_eq!(hash(b""), 0xcbf2_9ce4_8422_2325);
49        assert_eq!(hash(b"a"), 0xaf63_dc4c_8601_ec8c);
50        assert_eq!(hash(b"foobar"), 0x8594_4171_f739_67e8);
51    }
52
53    #[test]
54    fn writing_in_pieces_hashes_like_writing_at_once() {
55        let pieces = Fnv1a64::new().write(b"foo").write(b"").write(b"bar");
56        assert_eq!(pieces.finish(), hash(b"foobar"));
57    }
58}