1use kurbo::{BezPath, PathEl, Point};
5
6#[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 #[must_use]
20 pub const fn new() -> Self {
21 Self([0xcbf2_9ce4_8422_2325, 0x6c62_272e_07bb_0142])
22 }
23
24 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 pub fn u32(&mut self, value: u32) {
34 for byte in value.to_le_bytes() {
35 self.mix(byte);
36 }
37 }
38
39 pub fn u64(&mut self, value: u64) {
41 for byte in value.to_le_bytes() {
42 self.mix(byte);
43 }
44 }
45
46 pub fn f32(&mut self, value: f32) {
48 self.u32(value.to_bits());
49 }
50
51 pub fn f64(&mut self, value: f64) {
53 self.u64(value.to_bits());
54 }
55
56 pub fn point(&mut self, point: Point) {
58 self.f64(point.x);
59 self.f64(point.y);
60 }
61
62 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 #[must_use]
100 pub fn finish(&self) -> u128 {
101 (u128::from(self.0[1]) << 64) | u128::from(self.0[0])
102 }
103}