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}