Skip to main content

alpha2_geometry/
hash.rs

1//! A stable 128-bit hash: the same input bits give the same key on every
2//! target and in every run, unlike `std`'s randomly seeded hashers.
3
4use kurbo::{BezPath, PathEl, Point};
5
6/// Two FNV-style lanes over the bytes written, as the render graph keys its
7/// results.
8#[derive(Clone, Debug)]
9pub struct StableHash([u64; 2]);
10
11impl Default for StableHash {
12    fn default() -> Self {
13        Self::new()
14    }
15}
16
17impl StableHash {
18    /// A hash of nothing yet.
19    #[must_use]
20    pub const fn new() -> Self {
21        Self([0xcbf2_9ce4_8422_2325, 0x6c62_272e_07bb_0142])
22    }
23
24    /// Adds `bytes`, length first, so adjacent writes cannot run together.
25    pub fn bytes(&mut self, bytes: &[u8]) {
26        self.u64(bytes.len() as u64);
27        for byte in bytes {
28            self.mix(*byte);
29        }
30    }
31
32    /// Adds a 32-bit value.
33    pub fn u32(&mut self, value: u32) {
34        for byte in value.to_le_bytes() {
35            self.mix(byte);
36        }
37    }
38
39    /// Adds a 64-bit value.
40    pub fn u64(&mut self, value: u64) {
41        for byte in value.to_le_bytes() {
42            self.mix(byte);
43        }
44    }
45
46    /// Adds a float's bits.
47    pub fn f32(&mut self, value: f32) {
48        self.u32(value.to_bits());
49    }
50
51    /// Adds a float's bits.
52    pub fn f64(&mut self, value: f64) {
53        self.u64(value.to_bits());
54    }
55
56    /// Adds a point's bits.
57    pub fn point(&mut self, point: Point) {
58        self.f64(point.x);
59        self.f64(point.y);
60    }
61
62    /// Adds every element of a path, with its kind.
63    pub fn path(&mut self, path: &BezPath) {
64        self.u64(path.elements().len() as u64);
65        for element in path.elements() {
66            match *element {
67                PathEl::MoveTo(p) => {
68                    self.mix(b'M');
69                    self.point(p);
70                }
71                PathEl::LineTo(p) => {
72                    self.mix(b'L');
73                    self.point(p);
74                }
75                PathEl::QuadTo(a, b) => {
76                    self.mix(b'Q');
77                    self.point(a);
78                    self.point(b);
79                }
80                PathEl::CurveTo(a, b, c) => {
81                    self.mix(b'C');
82                    self.point(a);
83                    self.point(b);
84                    self.point(c);
85                }
86                PathEl::ClosePath => self.mix(b'Z'),
87            }
88        }
89    }
90
91    fn mix(&mut self, byte: u8) {
92        self.0[0] ^= u64::from(byte);
93        self.0[0] = self.0[0].wrapping_mul(0x0000_0100_0000_01b3);
94        self.0[1] ^= u64::from(byte).wrapping_add(0x9d);
95        self.0[1] = self.0[1].wrapping_mul(0x9e37_79b1_85eb_ca87);
96    }
97
98    /// The key of everything added.
99    #[must_use]
100    pub fn finish(&self) -> u128 {
101        (u128::from(self.0[1]) << 64) | u128::from(self.0[0])
102    }
103}