1use kurbo::{BezPath, PathEl, Point};
4
5#[derive(Clone, Debug)]
7pub struct StableHash([u64; 2]);
8
9impl Default for StableHash {
10 fn default() -> Self {
11 Self::new()
12 }
13}
14
15impl StableHash {
16 #[must_use]
18 pub const fn new() -> Self {
19 Self([0xcbf2_9ce4_8422_2325, 0x6c62_272e_07bb_0142])
20 }
21
22 pub fn bytes(&mut self, bytes: &[u8]) {
24 self.u64(bytes.len() as u64);
25 for byte in bytes {
26 self.mix(*byte);
27 }
28 }
29
30 pub fn u32(&mut self, value: u32) {
32 for byte in value.to_le_bytes() {
33 self.mix(byte);
34 }
35 }
36
37 pub fn u64(&mut self, value: u64) {
39 for byte in value.to_le_bytes() {
40 self.mix(byte);
41 }
42 }
43
44 pub fn f32(&mut self, value: f32) {
46 self.u32(value.to_bits());
47 }
48
49 pub fn f64(&mut self, value: f64) {
51 self.u64(value.to_bits());
52 }
53
54 pub fn point(&mut self, point: Point) {
56 self.f64(point.x);
57 self.f64(point.y);
58 }
59
60 pub fn path(&mut self, path: &BezPath) {
62 self.u64(path.elements().len() as u64);
63 for element in path.elements() {
64 match *element {
65 PathEl::MoveTo(p) => {
66 self.mix(b'M');
67 self.point(p);
68 }
69 PathEl::LineTo(p) => {
70 self.mix(b'L');
71 self.point(p);
72 }
73 PathEl::QuadTo(a, b) => {
74 self.mix(b'Q');
75 self.point(a);
76 self.point(b);
77 }
78 PathEl::CurveTo(a, b, c) => {
79 self.mix(b'C');
80 self.point(a);
81 self.point(b);
82 self.point(c);
83 }
84 PathEl::ClosePath => self.mix(b'Z'),
85 }
86 }
87 }
88
89 fn mix(&mut self, byte: u8) {
90 self.0[0] ^= u64::from(byte);
91 self.0[0] = self.0[0].wrapping_mul(0x0000_0100_0000_01b3);
92 self.0[1] ^= u64::from(byte).wrapping_add(0x9d);
93 self.0[1] = self.0[1].wrapping_mul(0x9e37_79b1_85eb_ca87);
94 }
95
96 #[must_use]
98 pub fn finish(&self) -> u128 {
99 (u128::from(self.0[1]) << 64) | u128::from(self.0[0])
100 }
101}