use std::hash::{BuildHasherDefault, Hasher};
const SEED: u64 = 0x51_7c_c1_b7_27_22_0a_95;
#[derive(Default)]
pub(crate) struct FxHasher {
hash: u64,
}
impl FxHasher {
#[inline]
fn add(&mut self, word: u64) {
self.hash = (self.hash.rotate_left(5) ^ word).wrapping_mul(SEED);
}
}
impl Hasher for FxHasher {
#[inline]
fn write(&mut self, mut bytes: &[u8]) {
while bytes.len() >= 8 {
let mut buf = [0u8; 8];
buf.copy_from_slice(&bytes[..8]);
self.add(u64::from_ne_bytes(buf));
bytes = &bytes[8..];
}
if !bytes.is_empty() {
let mut tail = 0u64;
for (i, &b) in bytes.iter().enumerate() {
tail |= (b as u64) << (i * 8);
}
self.add(tail);
}
}
#[inline]
fn write_u8(&mut self, i: u8) {
self.add(i as u64);
}
#[inline]
fn write_u16(&mut self, i: u16) {
self.add(i as u64);
}
#[inline]
fn write_u32(&mut self, i: u32) {
self.add(i as u64);
}
#[inline]
fn write_u64(&mut self, i: u64) {
self.add(i);
}
#[inline]
fn write_usize(&mut self, i: usize) {
self.add(i as u64);
}
#[inline]
fn finish(&self) -> u64 {
self.hash
}
}
pub(crate) type FxBuildHasher = BuildHasherDefault<FxHasher>;
pub(crate) type FxHashMap<K, V> = std::collections::HashMap<K, V, FxBuildHasher>;
#[cfg(feature = "jit")]
pub(crate) type FxHashSet<K> = std::collections::HashSet<K, FxBuildHasher>;
#[cfg(test)]
mod tests {
use super::*;
use std::hash::Hash;
fn hash_of<T: Hash>(value: &T) -> u64 {
let mut hasher = FxHasher::default();
value.hash(&mut hasher);
hasher.finish()
}
#[test]
fn same_input_hashes_equal() {
let bytes = [1u8, 2, 3, 4, 5, 6, 7, 8, 9];
assert_eq!(hash_of(&42u32), hash_of(&42u32));
assert_eq!(hash_of(&42u64), hash_of(&42u64));
assert_eq!(hash_of(&bytes.as_slice()), hash_of(&bytes.as_slice()));
}
#[test]
fn different_inputs_hash_differently() {
assert_ne!(hash_of(&1u32), hash_of(&2u32));
assert_ne!(hash_of(&1u64), hash_of(&2u64));
assert_ne!(hash_of(&0u32), hash_of(&u32::MAX));
assert_ne!(
hash_of(&b"short".as_slice()),
hash_of(&b"a-different-longer-slice".as_slice())
);
}
#[test]
fn hashmap_round_trips_with_collisions_by_construction() {
let keys: Vec<u32> = (0u32..64).chain([1000, 1008, 1016, 2024]).collect();
let mut map: FxHashMap<u32, String> = FxHashMap::default();
for &k in &keys {
map.insert(k, format!("value-{k}"));
}
for &k in &keys {
assert_eq!(map.get(&k), Some(&format!("value-{k}")));
}
assert_eq!(map.len(), keys.len());
assert_eq!(map.get(&999), None);
}
}