#[must_use]
pub fn compute_blake3_128_hex(bytes: &[u8]) -> String {
let full = blake3::hash(bytes);
let short = &full.as_bytes()[..16];
let mut out = String::with_capacity(32);
for byte in short {
use std::fmt::Write as _;
let _ = write!(out, "{byte:02x}");
}
out
}
#[must_use]
pub fn is_blake3_128_hex(s: &str) -> bool {
s.len() == 32
&& s.bytes()
.all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compute_is_stable_and_lowercase_hex() {
let a = compute_blake3_128_hex(b"hello world");
let b = compute_blake3_128_hex(b"hello world");
assert_eq!(a, b);
assert_eq!(a.len(), 32);
assert!(is_blake3_128_hex(&a));
}
#[test]
fn compute_distinguishes_different_inputs() {
assert_ne!(compute_blake3_128_hex(b"a"), compute_blake3_128_hex(b"b"));
}
#[test]
fn hex_validator_accepts_canonical_shape() {
assert!(is_blake3_128_hex("af42c0d18e9b3f4aa18b7c3ef1de93a4"));
assert!(is_blake3_128_hex("00000000000000000000000000000000"));
assert!(is_blake3_128_hex("ffffffffffffffffffffffffffffffff"));
}
#[test]
fn hex_validator_rejects_wrong_length() {
assert!(!is_blake3_128_hex(""));
assert!(!is_blake3_128_hex("af42"));
assert!(!is_blake3_128_hex("af42c0d18e9b3f4aa18b7c3ef1de93a4a"));
}
#[test]
fn hex_validator_rejects_uppercase() {
assert!(!is_blake3_128_hex("AF42c0d18e9b3f4aa18b7c3ef1de93a4"));
}
#[test]
fn hex_validator_rejects_non_hex_chars() {
assert!(!is_blake3_128_hex("gf42c0d18e9b3f4aa18b7c3ef1de93a4"));
assert!(!is_blake3_128_hex("af42c0d18e9b3f4aa18b7c3ef1de93a_"));
}
}