gglib_core/domain/agent/fnv1a.rs
1//! FNV-1a 64-bit hash utility backing the loop and stagnation detectors.
2//!
3//! # Why inline rather than using the `fnv` crate?
4//!
5//! The full implementation is 10 lines with no unsafe code and no trait
6//! machinery. Vendoring it keeps the `gglib-core` dependency graph minimal
7//! and avoids pulling in an additional crate for a single trivial function.
8
9// =============================================================================
10// FNV-1a 64-bit hash
11// =============================================================================
12
13/// FNV-1a 64-bit hash of `s`.
14///
15/// Parameters:
16/// - Offset basis: `14_695_981_039_346_656_037`
17/// - Prime: `1_099_511_628_211`
18/// - Wrapping 64-bit multiplication
19///
20/// The Rust implementation hashes UTF-8 bytes.
21pub(crate) fn fnv1a_64(s: &str) -> u64 {
22 const OFFSET: u64 = 14_695_981_039_346_656_037;
23 const PRIME: u64 = 1_099_511_628_211;
24 let mut hash = OFFSET;
25 for byte in s.bytes() {
26 hash ^= u64::from(byte);
27 hash = hash.wrapping_mul(PRIME);
28 }
29 hash
30}
31
32// =============================================================================
33// Tests
34// =============================================================================
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39
40 #[test]
41 fn fnv1a_64_empty_string_is_offset_basis() {
42 // FNV-1a of "" is the offset basis unchanged.
43 assert_eq!(fnv1a_64(""), 14_695_981_039_346_656_037);
44 }
45
46 #[test]
47 fn fnv1a_64_is_deterministic_and_distinct() {
48 // Hashing the same string twice must yield the same value.
49 assert_eq!(fnv1a_64("hello"), fnv1a_64("hello"));
50 // Different inputs must produce different hashes.
51 assert_ne!(fnv1a_64("hello"), fnv1a_64("world"));
52 }
53
54 #[test]
55 fn fnv1a_64_differs_from_32_bit_basis() {
56 // Sanity check: the 64-bit offset basis is different from the 32-bit one.
57 #[allow(clippy::cast_possible_truncation)]
58 let low32 = fnv1a_64("") as u32;
59 assert_ne!(low32, 2_166_136_261_u32);
60 }
61}