const HASH_ALPHABET: &[u8; 16] = b"ZPMQVRWSNKTXJBYH";
const FNV_OFFSET_BASIS: u32 = 2_166_136_261;
const FNV_PRIME: u32 = 16_777_619;
fn fnv1a_32(data: &[u8]) -> u32 {
let mut hash = FNV_OFFSET_BASIS;
for &byte in data {
hash ^= u32::from(byte);
hash = hash.wrapping_mul(FNV_PRIME);
}
hash
}
pub fn hash_line(content: &str) -> String {
let h = fnv1a_32(content.as_bytes());
let hi = ((h >> 4) & 0xF) as usize;
let lo = (h & 0xF) as usize;
format!("{}{}", HASH_ALPHABET[hi] as char, HASH_ALPHABET[lo] as char)
}
pub fn hash_all_lines(content: &str) -> Vec<(usize, String)> {
content
.lines()
.enumerate()
.map(|(i, line)| (i + 1, hash_line(line)))
.collect()
}
pub fn format_hashline(_line_number: usize, hash: &str, content: &str) -> String {
format!("{}|{}", hash, content)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hash_deterministic() {
let h1 = hash_line("hello world");
let h2 = hash_line("hello world");
assert_eq!(h1, h2);
assert_eq!(h1.len(), 2);
}
#[test]
fn test_hash_two_chars_from_alphabet() {
let h = hash_line("some code here");
assert_eq!(h.len(), 2);
for c in h.chars() {
assert!(
HASH_ALPHABET.contains(&(c as u8)),
"char '{}' not in alphabet",
c
);
}
}
#[test]
fn test_different_content_different_hash() {
let h1 = hash_line("hello");
let h2 = hash_line("world");
assert_ne!(h1, h2);
}
#[test]
fn test_identical_content_same_hash() {
let h1 = hash_line("same content");
let h2 = hash_line("same content");
assert_eq!(h1, h2);
}
#[test]
fn test_blank_lines_same_hash() {
let h1 = hash_line("");
let h5 = hash_line("");
assert_eq!(h1, h5);
}
#[test]
fn test_hash_all_lines() {
let content = "line one\nline two\nline three";
let hashes = hash_all_lines(content);
assert_eq!(hashes.len(), 3);
assert_eq!(hashes[0].0, 1);
assert_eq!(hashes[1].0, 2);
assert_eq!(hashes[2].0, 3);
assert_eq!(hashes[0].1.len(), 2);
}
#[test]
fn test_format_hashline() {
let formatted = format_hashline(12, "VK", "function hello() {");
assert_eq!(formatted, "VK|function hello() {");
}
#[test]
fn test_empty_content() {
let h = hash_line("");
assert_eq!(h.len(), 2);
}
#[test]
fn test_unicode_content() {
let h = hash_line("héllo wörld 🦀");
assert_eq!(h.len(), 2);
}
}