Skip to main content

molframe_core/contract/policy/
fingerprint.rs

1//! Telling one policy or input from another.
2//!
3//! The standard hasher is seeded afresh each process, which is right for hash
4//! tables and wrong here: a fingerprint that changes between runs identifies the
5//! run rather than the thing. This one does not.
6
7use std::fmt;
8use std::hash::Hasher;
9
10/// FNV-1a's specified 64-bit initial state.
11const FNV1A64_OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325;
12/// FNV-1a's specified 64-bit prime multiplier.
13const FNV1A64_PRIME: u64 = 0x0000_0100_0000_01b3;
14
15/// A stable content fingerprint.
16#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
17pub struct Fingerprint(u64);
18
19impl Fingerprint {
20    /// Wraps a finished hash.
21    pub(super) const fn from_hash(hash: u64) -> Self {
22        Self(hash)
23    }
24
25    /// Fingerprints a block of bytes.
26    #[must_use]
27    pub fn of(bytes: &[u8]) -> Self {
28        let mut hasher = Fnv1a::new();
29        hasher.write(bytes);
30        Self(hasher.finish())
31    }
32
33    /// The raw value.
34    #[must_use]
35    pub const fn get(self) -> u64 {
36        self.0
37    }
38}
39
40impl fmt::Display for Fingerprint {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        write!(f, "fnv1a64:{:016x}", self.0)
43    }
44}
45
46impl fmt::Debug for Fingerprint {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        write!(f, "{self}")
49    }
50}
51
52/// A hasher whose output does not depend on how the program was started.
53///
54/// The standard hasher is randomly seeded per process, which is right for
55/// hash tables and wrong here: a fingerprint that changes between runs cannot
56/// identify anything.
57pub(super) struct Fnv1a(u64);
58
59impl Fnv1a {
60    pub(super) const fn new() -> Self {
61        Self(FNV1A64_OFFSET_BASIS)
62    }
63}
64
65impl Hasher for Fnv1a {
66    fn finish(&self) -> u64 {
67        self.0
68    }
69
70    fn write(&mut self, bytes: &[u8]) {
71        for byte in bytes {
72            self.0 ^= u64::from(*byte);
73            // FNV-1a is defined modulo 2^64, so wrapping is part of the
74            // algorithm rather than an overflow fallback.
75            self.0 = self.0.wrapping_mul(FNV1A64_PRIME);
76        }
77    }
78}