#[allow(dead_code)] pub fn normalize_content(content: &str) -> String {
let lower = content.to_lowercase();
let mut out = String::with_capacity(lower.len());
let mut in_space = false;
for c in lower.chars() {
if c.is_whitespace() {
if !in_space && !out.is_empty() {
out.push(' ');
in_space = true;
}
} else {
out.push(c);
in_space = false;
}
}
out.trim().to_string()
}
#[allow(dead_code)] pub fn fnv1a64_hex(input: &str) -> String {
let mut hash: u64 = 0xcbf2_9ce4_8422_2325; for byte in input.as_bytes() {
hash ^= *byte as u64;
hash = hash.wrapping_mul(0x0000_0100_0000_01b3); }
format!("{:016x}", hash)
}
#[allow(dead_code)] pub fn content_hash(content: &str) -> String {
let normalized = normalize_content(content);
fnv1a64_hex(&normalized)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_normalize_lowercases() {
assert_eq!(normalize_content("Hello World"), "hello world");
}
#[test]
fn test_normalize_collapses_whitespace() {
assert_eq!(normalize_content("Hello World"), "hello world");
assert_eq!(normalize_content("a\tb\nc\nd"), "a b c d");
}
#[test]
fn test_normalize_trims_leading_trailing() {
assert_eq!(normalize_content(" hello world "), "hello world");
}
#[test]
fn test_content_hash_deterministic() {
let h1 = content_hash("hello world");
let h2 = content_hash("hello world");
assert_eq!(h1, h2);
}
#[test]
fn test_content_hash_case_and_whitespace_insensitive() {
assert_eq!(content_hash("Hello World"), content_hash("hello world"));
}
#[test]
fn test_content_hash_differs_for_different_content() {
assert_ne!(content_hash("foo bar"), content_hash("bar foo"));
}
#[test]
fn test_fnv1a64_hex_output_format() {
let h = fnv1a64_hex("x");
assert_eq!(h.len(), 16);
assert!(h.chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
fn test_parity_with_deleted_content_hash_for() {
let cases: &[(&str, &str)] = &[
("hello world", "779a65e7023cd2e7"),
("leading and trailing", "0502c48fed08ef80"),
("foo", "dcb27518fed9d577"),
("bar", "003934191339461a"),
("test content", "26877eb147d6f408"),
("same input", "b94119c1d5202297"),
("shared content", "74c6a290029c9309"),
];
for (input, expected) in cases {
assert_eq!(
&content_hash(input),
expected,
"parity broken for input: {input:?}"
);
}
assert_eq!(
normalize_content(" leading and trailing "),
normalize_content("leading and trailing"),
"trim vs trim_end must not diverge for normalised output"
);
}
}