use std::{
collections::HashMap,
hash::{BuildHasherDefault, Hasher},
};
#[derive(Default)]
pub struct FastHasher(u64);
const SEED: u64 = 0x51_7c_c1_b7_27_22_0a_95;
impl FastHasher {
#[inline]
fn add(&mut self, word: u64) {
self.0 = (self.0.rotate_left(5) ^ word).wrapping_mul(SEED);
}
}
impl Hasher for FastHasher {
#[inline]
fn finish(&self) -> u64 {
self.0.rotate_left(20)
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
let mut rest = bytes;
while let Some((word, tail)) = rest.split_first_chunk::<8>() {
self.add(u64::from_le_bytes(*word));
rest = tail;
}
if !rest.is_empty() {
let mut last = [0u8; 8];
last[..rest.len()].copy_from_slice(rest);
self.add(u64::from_le_bytes(last));
}
self.add(bytes.len() as u64);
}
#[inline]
fn write_u8(&mut self, value: u8) {
self.add(u64::from(value));
}
#[inline]
fn write_u16(&mut self, value: u16) {
self.add(u64::from(value));
}
#[inline]
fn write_u32(&mut self, value: u32) {
self.add(u64::from(value));
}
#[inline]
fn write_u64(&mut self, value: u64) {
self.add(value);
}
#[inline]
fn write_u128(&mut self, value: u128) {
self.add(value as u64);
self.add((value >> 64) as u64);
}
#[inline]
fn write_usize(&mut self, value: usize) {
self.add(value as u64);
}
}
pub type FastBuildHasher = BuildHasherDefault<FastHasher>;
pub(crate) type FastMap<K, V> = HashMap<K, V, FastBuildHasher>;
#[cfg(test)]
mod tests {
use super::*;
use std::{collections::HashSet, hash::Hash};
fn hash_of(value: impl Hash) -> u64 {
let mut hasher = FastHasher::default();
value.hash(&mut hasher);
hasher.finish()
}
#[test]
fn consecutive_integers_spread() {
let hashes: HashSet<u64> = (0u32..1000).map(hash_of).collect();
assert_eq!(hashes.len(), 1000);
let buckets: HashSet<u64> = (0u32..64).map(|value| hash_of(value) & 0x3f).collect();
assert!(buckets.len() > 32, "bucket index must vary: {buckets:?}");
}
#[test]
fn similar_strings_spread() {
let hashes: HashSet<u64> = (0..1000).map(|i| hash_of(format!("row-{i:04}"))).collect();
assert_eq!(hashes.len(), 1000);
}
#[test]
fn trailing_zero_bytes_are_not_padding() {
assert_ne!(hash_of([1u8].as_slice()), hash_of([1u8, 0].as_slice()));
assert_ne!(hash_of(""), hash_of("\0"));
}
#[test]
fn the_same_value_hashes_the_same() {
assert_eq!(hash_of(7u32), hash_of(7u32));
assert_eq!(hash_of("abc"), hash_of("abc"));
}
#[test]
fn every_word_of_a_long_key_counts() {
let base = "0123456789abcdef0123456789abcdef";
for index in 0..base.len() {
let mut other = base.to_string();
other.replace_range(index..=index, "X");
assert_ne!(hash_of(base), hash_of(other.as_str()), "byte {index}");
}
}
#[test]
fn map_round_trip() {
let mut map: FastMap<String, u32> = FastMap::default();
for i in 0..100u32 {
map.insert(format!("k{i}"), i);
}
for i in 0..100u32 {
assert_eq!(map.get(&format!("k{i}")), Some(&i));
}
assert_eq!(map.get("missing"), None);
}
}