use core::hash::{BuildHasherDefault, Hasher};
const FNV_OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
#[derive(Clone, Copy, Debug)]
pub(crate) struct DetHasher(u64);
impl Default for DetHasher {
fn default() -> Self {
Self(FNV_OFFSET_BASIS)
}
}
impl Hasher for DetHasher {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, bytes: &[u8]) {
for &b in bytes {
self.0 = (self.0 ^ u64::from(b)).wrapping_mul(FNV_PRIME);
}
}
fn write_u16(&mut self, n: u16) {
self.write(&n.to_le_bytes());
}
fn write_u32(&mut self, n: u32) {
self.write(&n.to_le_bytes());
}
fn write_u64(&mut self, n: u64) {
self.write(&n.to_le_bytes());
}
fn write_u128(&mut self, n: u128) {
self.write(&n.to_le_bytes());
}
fn write_usize(&mut self, n: usize) {
self.write_u64(n as u64);
}
fn write_i16(&mut self, n: i16) {
self.write(&n.to_le_bytes());
}
fn write_i32(&mut self, n: i32) {
self.write(&n.to_le_bytes());
}
fn write_i64(&mut self, n: i64) {
self.write(&n.to_le_bytes());
}
fn write_i128(&mut self, n: i128) {
self.write(&n.to_le_bytes());
}
fn write_isize(&mut self, n: isize) {
self.write_i64(n as i64);
}
}
pub(crate) type DetBuildHasher = BuildHasherDefault<DetHasher>;
#[allow(clippy::disallowed_types)]
pub(crate) type DetHashMap<K, V> = std::collections::HashMap<K, V, DetBuildHasher>;
#[cfg(test)]
mod tests {
use super::{DetHashMap, DetHasher};
use core::hash::{BuildHasher, Hasher};
fn fnv(bytes: &[u8]) -> u64 {
let mut h = DetHasher::default();
h.write(bytes);
h.finish()
}
#[test]
fn fnv1a_matches_the_published_vectors() {
assert_eq!(fnv(b""), 0xcbf2_9ce4_8422_2325);
assert_eq!(fnv(b"a"), 0xaf63_dc4c_8601_ec8c);
assert_eq!(fnv(b"foobar"), 0x8594_4171_f739_67e8);
}
#[test]
fn integers_hash_as_their_little_endian_bytes() {
let mut h = DetHasher::default();
h.write_u32(0x0403_0201);
assert_eq!(h.finish(), fnv(&[1, 2, 3, 4]));
let mut a = DetHasher::default();
a.write_usize(7);
let mut b = DetHasher::default();
b.write_u64(7);
assert_eq!(a.finish(), b.finish());
}
#[test]
fn two_maps_hash_a_key_the_same() {
let a: DetHashMap<(i32, i32, i32), u32> = DetHashMap::default();
let b: DetHashMap<(i32, i32, i32), u32> = DetHashMap::default();
let key = (3, -1, 7);
let hash = |m: &DetHashMap<(i32, i32, i32), u32>| m.hasher().hash_one(key);
assert_eq!(hash(&a), hash(&b));
}
}