Skip to main content

iris_format/
digest.rs

1//! Content digests.
2//!
3//! Every section carries one, the footer commits to all of them, and the trailer commits to the
4//! footer. That chain is what makes it possible to say this is the same dataset without comparing
5//! whole files, and it is what lets a host swap in a native decoder it already trusts for a
6//! sandboxed one it does not: the substitution is keyed on the digest of the decoder module, so
7//! there is nothing to guess about which decoder a dataset actually asked for.
8
9use core::fmt;
10
11use crate::layout::DIGEST_SIZE;
12
13/// A BLAKE3 hash of some part of a container.
14///
15/// BLAKE3 rather than SHA-256 because this gets computed over whole datasets on the write path and
16/// over whole sections on any read that verifies, and the difference is large enough to change
17/// whether verification is on by default.
18#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
19pub struct Digest(pub [u8; DIGEST_SIZE]);
20
21impl Digest {
22    /// The digest of some bytes.
23    #[must_use]
24    pub fn of(bytes: &[u8]) -> Self {
25        Self(*blake3::hash(bytes).as_bytes())
26    }
27
28    /// The raw bytes.
29    #[must_use]
30    pub const fn as_bytes(&self) -> &[u8; DIGEST_SIZE] {
31        &self.0
32    }
33
34    /// The first sixteen hex characters, which is what belongs in a log line.
35    ///
36    /// Never use this to decide whether two things are the same. It is here because a full digest
37    /// in an error message is noise a reader skips, and a short one is something they read.
38    #[must_use]
39    pub fn short(&self) -> String {
40        let full = self.to_string();
41        full[..16].to_owned()
42    }
43}
44
45impl fmt::Display for Digest {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        for byte in self.0 {
48            write!(f, "{byte:02x}")?;
49        }
50        Ok(())
51    }
52}
53
54impl fmt::Debug for Digest {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        write!(f, "Digest({})", self.short())
57    }
58}