#[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()));
}
}