metrale_closure/
hex.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-30: Lower-case hex, the one spelling every SHA-256 this workspace records uses.
4//!
5//! Owner: metrale-closure (the lowest crate that hashes; metrale-bench reaches it too).
6//! Invariants: two lower-case digits per byte, no separator, no prefix. sha2 0.11's output
7//! (`hybrid_array::Array`) has no `LowerHex`, so `format!("{:x}", ..)` no longer compiles on a
8//! digest; every recorded digest keeps the spelling it had under 0.10 by going through here.
9
10use std::fmt::Write as _;
11
12/// 2026-09-30: `bytes` as lower-case hex.
13pub fn hex_lower(bytes: &[u8]) -> String {
14    let mut s = String::with_capacity(bytes.len() * 2);
15    for b in bytes {
16        let _ = write!(s, "{b:02x}");
17    }
18    s
19}
20
21#[cfg(test)]
22mod tests {
23    use super::hex_lower;
24    use sha2::{Digest, Sha256};
25
26    /// 2026-09-30: FIPS 180-2's "abc" vector, so a record digest is spelled exactly as before.
27    #[test]
28    fn a_sha256_digest_reads_as_the_published_vector() {
29        assert_eq!(
30            hex_lower(&Sha256::digest(b"abc")),
31            "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
32        );
33        assert_eq!(hex_lower(&[0x00, 0x0f, 0xa0]), "000fa0");
34        assert_eq!(hex_lower(&[]), "");
35    }
36}