Skip to main content

klayout_core/
hash.rs

1//! Content-addressable hashing for cells and component params.
2//!
3//! `ContentHash` is a 32-byte blake3 of a cell's canonical encoding. Two cells
4//! with byte-identical canonical encodings hash equal across runs and machines.
5//! This is what enables structural dedup, cross-library comparison, and
6//! incremental verification caching.
7
8use std::fmt;
9
10#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
11pub struct ContentHash(pub [u8; 32]);
12
13impl ContentHash {
14    pub const ZERO: Self = Self([0; 32]);
15
16    pub fn from_bytes(bytes: [u8; 32]) -> Self {
17        Self(bytes)
18    }
19
20    pub fn short_hex(&self) -> String {
21        let mut s = String::with_capacity(16);
22        for b in &self.0[..8] {
23            s.push_str(&format!("{:02x}", b));
24        }
25        s
26    }
27
28    pub fn full_hex(&self) -> String {
29        let mut s = String::with_capacity(64);
30        for b in &self.0 {
31            s.push_str(&format!("{:02x}", b));
32        }
33        s
34    }
35}
36
37impl fmt::Debug for ContentHash {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        write!(f, "ContentHash({})", self.short_hex())
40    }
41}
42
43impl fmt::Display for ContentHash {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        f.write_str(&self.short_hex())
46    }
47}
48
49#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
50pub struct ParamHash(pub [u8; 32]);
51
52impl ParamHash {
53    pub fn from_bytes(bytes: [u8; 32]) -> Self {
54        Self(bytes)
55    }
56
57    pub fn short_hex(&self) -> String {
58        let mut s = String::with_capacity(16);
59        for b in &self.0[..8] {
60            s.push_str(&format!("{:02x}", b));
61        }
62        s
63    }
64}
65
66impl fmt::Debug for ParamHash {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        write!(f, "ParamHash({})", self.short_hex())
69    }
70}
71
72/// `std::hash::Hasher` adapter that streams into blake3 and produces 32 bytes.
73///
74/// Used by the default `Component::param_hash` impl so any `T: Hash` whose
75/// `Hash` impl is deterministic gets a stable cross-run cache key. Floats
76/// must be quantized before reaching this — see [crate::component].
77pub struct ParamHasher(blake3::Hasher);
78
79impl Default for ParamHasher {
80    fn default() -> Self {
81        Self(blake3::Hasher::new())
82    }
83}
84
85impl ParamHasher {
86    pub fn new() -> Self {
87        Self::default()
88    }
89
90    pub fn finalize(self) -> ParamHash {
91        ParamHash(*self.0.finalize().as_bytes())
92    }
93}
94
95impl std::hash::Hasher for ParamHasher {
96    fn write(&mut self, bytes: &[u8]) {
97        self.0.update(bytes);
98    }
99
100    fn finish(&self) -> u64 {
101        let h = self.0.finalize();
102        let mut buf = [0u8; 8];
103        buf.copy_from_slice(&h.as_bytes()[..8]);
104        u64::from_le_bytes(buf)
105    }
106}