use std::hash::{BuildHasher, Hasher};
#[derive(Debug, Clone)]
pub struct DetHasher {
state: u64,
}
impl DetHasher {
const SEED: u64 = 0x16f1_1fe8_9b0d_677c;
const MULTIPLIER: u64 = 0x517c_c1b7_2722_0a95;
#[inline]
fn mix_byte(&mut self, byte: u8) {
self.state = self.state.wrapping_mul(Self::MULTIPLIER);
self.state ^= u64::from(byte);
}
}
impl Default for DetHasher {
#[inline]
fn default() -> Self {
Self { state: Self::SEED }
}
}
impl Hasher for DetHasher {
#[inline]
fn write(&mut self, bytes: &[u8]) {
for &byte in bytes {
self.mix_byte(byte);
}
}
#[inline]
fn write_u8(&mut self, i: u8) {
self.mix_byte(i);
}
#[inline]
fn write_u16(&mut self, i: u16) {
let bytes = i.to_le_bytes();
self.mix_byte(bytes[0]);
self.mix_byte(bytes[1]);
}
#[inline]
fn write_u32(&mut self, i: u32) {
let bytes = i.to_le_bytes();
self.mix_byte(bytes[0]);
self.mix_byte(bytes[1]);
self.mix_byte(bytes[2]);
self.mix_byte(bytes[3]);
}
#[inline]
fn write_u64(&mut self, i: u64) {
let bytes = i.to_le_bytes();
self.mix_byte(bytes[0]);
self.mix_byte(bytes[1]);
self.mix_byte(bytes[2]);
self.mix_byte(bytes[3]);
self.mix_byte(bytes[4]);
self.mix_byte(bytes[5]);
self.mix_byte(bytes[6]);
self.mix_byte(bytes[7]);
}
#[inline]
fn write_u128(&mut self, i: u128) {
let bytes = i.to_le_bytes();
for &byte in &bytes {
self.mix_byte(byte);
}
}
#[inline]
fn write_usize(&mut self, i: usize) {
self.write_u64(i as u64);
}
#[inline]
fn write_i8(&mut self, i: i8) {
self.write_u8(i.cast_unsigned());
}
#[inline]
fn write_i16(&mut self, i: i16) {
self.write_u16(i.cast_unsigned());
}
#[inline]
fn write_i32(&mut self, i: i32) {
self.write_u32(i.cast_unsigned());
}
#[inline]
fn write_i64(&mut self, i: i64) {
self.write_u64(i.cast_unsigned());
}
#[inline]
fn write_i128(&mut self, i: i128) {
self.write_u128(i.cast_unsigned());
}
#[inline]
fn write_isize(&mut self, i: isize) {
self.write_i64(i as i64);
}
#[inline]
fn finish(&self) -> u64 {
let mut h = self.state;
h ^= h >> 33;
h = h.wrapping_mul(0xff51_afd7_ed55_8ccd);
h ^= h >> 33;
h = h.wrapping_mul(0xc4ce_b9fe_1a85_ec53);
h ^= h >> 33;
h
}
}
#[derive(Clone, Default)]
pub struct DetBuildHasher;
impl BuildHasher for DetBuildHasher {
type Hasher = DetHasher;
#[inline]
fn build_hasher(&self) -> Self::Hasher {
DetHasher::default()
}
}
pub type DetHashMap<K, V> = std::collections::HashMap<K, V, DetBuildHasher>;
pub type DetHashSet<K> = std::collections::HashSet<K, DetBuildHasher>;
pub use std::collections::{BTreeMap, BTreeSet};
#[cfg(test)]
mod tests {
use super::*;
use std::hash::Hash;
fn hash_value<T: Hash>(value: &T) -> u64 {
let mut hasher = DetHasher::default();
value.hash(&mut hasher);
hasher.finish()
}
#[test]
fn det_hasher_same_input_same_hash() {
let h1 = hash_value(&"hello");
let h2 = hash_value(&"hello");
assert_eq!(h1, h2);
}
#[test]
fn det_hasher_different_input_different_hash() {
let h1 = hash_value(&"hello");
let h2 = hash_value(&"world");
assert_ne!(h1, h2);
}
#[test]
fn det_hasher_numeric_values() {
let h1 = hash_value(&42u64);
let h2 = hash_value(&42u64);
assert_eq!(h1, h2);
let h3 = hash_value(&43u64);
assert_ne!(h1, h3);
}
#[test]
fn det_hasher_empty_slice() {
let h1 = hash_value(&[0u8; 0]);
let h2 = hash_value(&[0u8; 0]);
assert_eq!(h1, h2);
}
#[test]
fn det_hasher_incremental_write() {
let mut h1 = DetHasher::default();
h1.write(&[1, 2, 3, 4]);
let result1 = h1.finish();
let mut h2 = DetHasher::default();
h2.write(&[1, 2]);
h2.write(&[3, 4]);
let result2 = h2.finish();
assert_eq!(result1, result2);
}
#[test]
fn det_hasher_write_u64_consistent_with_write() {
let mut h1 = DetHasher::default();
h1.write_u64(0xDEAD_BEEF_CAFE_BABE);
let mut h2 = DetHasher::default();
h2.write(&0xDEAD_BEEF_CAFE_BABEu64.to_le_bytes());
assert_eq!(
h1.finish(),
h2.finish(),
"write_u64 must match write for same bytes",
);
}
#[test]
fn det_hashmap_deterministic_lookup() {
let mut map1: DetHashMap<String, i32> = DetHashMap::default();
let mut map2: DetHashMap<String, i32> = DetHashMap::default();
map1.insert("a".to_string(), 1);
map1.insert("b".to_string(), 2);
map1.insert("c".to_string(), 3);
map2.insert("a".to_string(), 1);
map2.insert("b".to_string(), 2);
map2.insert("c".to_string(), 3);
assert_eq!(map1.get("a"), map2.get("a"));
assert_eq!(map1.get("b"), map2.get("b"));
assert_eq!(map1.get("c"), map2.get("c"));
}
#[test]
fn det_hashmap_iteration_order_consistent() {
let mut map: DetHashMap<i32, i32> = DetHashMap::default();
for i in 0..100 {
map.insert(i, i * 2);
}
for i in 0..100 {
assert_eq!(map.get(&i), Some(&(i * 2)));
}
}
#[test]
fn det_hashset_deterministic_contains() {
let mut set1: DetHashSet<String> = DetHashSet::default();
let mut set2: DetHashSet<String> = DetHashSet::default();
set1.insert("alpha".to_string());
set1.insert("beta".to_string());
set1.insert("gamma".to_string());
set2.insert("alpha".to_string());
set2.insert("beta".to_string());
set2.insert("gamma".to_string());
assert_eq!(set1.contains("alpha"), set2.contains("alpha"));
assert_eq!(set1.contains("beta"), set2.contains("beta"));
assert_eq!(set1.contains("delta"), set2.contains("delta"));
}
#[test]
fn det_hashset_len() {
let mut set: DetHashSet<i32> = DetHashSet::default();
assert_eq!(set.len(), 0);
set.insert(1);
set.insert(2);
set.insert(3);
assert_eq!(set.len(), 3);
set.insert(1);
assert_eq!(set.len(), 3);
}
#[test]
fn det_build_hasher_produces_deterministic_hashers() {
let builder = DetBuildHasher;
let mut h1 = builder.build_hasher();
let mut h2 = builder.build_hasher();
h1.write(b"test data");
h2.write(b"test data");
assert_eq!(h1.finish(), h2.finish());
}
#[test]
fn det_hasher_debug_clone() {
let mut h = DetHasher::default();
h.write(b"partial");
let dbg = format!("{h:?}");
assert!(dbg.contains("DetHasher"), "{dbg}");
let h2 = h.clone();
assert_eq!(h.finish(), h2.finish());
}
#[test]
fn det_build_hasher_clone_default() {
let b1 = DetBuildHasher;
let b2 = b1;
let b3 = DetBuildHasher;
let mut x = b2.build_hasher();
let mut y = b3.build_hasher();
x.write(b"same");
y.write(b"same");
assert_eq!(x.finish(), y.finish());
}
}