use std::hash::{BuildHasherDefault, Hasher};
const FOLD: u64 = 0x517c_c1b7_2722_0a95;
const FOLD_ROTATE: u32 = 5;
const LANE_BYTES: usize = 64;
const LANE_SEEDS: [u64; 3] = [
0x9e37_79b9_7f4a_7c15,
0xc2b2_ae3d_27d4_eb4f,
0x1656_67b1_9e37_79f9,
];
const FINALIZE_A: u64 = 0xbf58_476d_1ce4_e5b9;
const FINALIZE_B: u64 = 0x94d0_49bb_1331_11eb;
#[derive(Clone, Copy, Default)]
pub struct FxHasher {
hash: u64,
}
pub type FxBuildHasher = BuildHasherDefault<FxHasher>;
impl FxHasher {
#[inline]
fn fold(&mut self, word: u64) {
self.hash = fold(self.hash, word);
}
fn fold_blocks(&mut self, blocks: &[[u8; 32]]) {
let mut lanes = [
self.hash,
self.hash ^ LANE_SEEDS[0],
self.hash ^ LANE_SEEDS[1],
self.hash ^ LANE_SEEDS[2],
];
for block in blocks {
for (lane, word) in lanes.iter_mut().zip(block.as_chunks::<8>().0) {
*lane = fold(*lane, u64::from_le_bytes(*word));
}
}
for lane in lanes {
self.fold(lane);
}
}
}
#[inline]
fn fold(hash: u64, word: u64) -> u64 {
(hash.rotate_left(FOLD_ROTATE) ^ word).wrapping_mul(FOLD)
}
impl Hasher for FxHasher {
#[inline]
fn write(&mut self, bytes: &[u8]) {
let bytes = if bytes.len() >= LANE_BYTES {
let (blocks, rest) = bytes.as_chunks::<32>();
self.fold_blocks(blocks);
rest
} else {
bytes
};
let (chunks, tail) = bytes.as_chunks::<8>();
for chunk in chunks {
self.fold(u64::from_le_bytes(*chunk));
}
if !tail.is_empty() {
let mut word = [0u8; 8];
word[..tail.len()].copy_from_slice(tail);
self.fold(u64::from_le_bytes(word) ^ ((tail.len() as u64) << 56));
}
}
#[inline]
fn write_u8(&mut self, value: u8) {
self.fold(u64::from(value));
}
#[inline]
fn write_u16(&mut self, value: u16) {
self.fold(u64::from(value));
}
#[inline]
fn write_u32(&mut self, value: u32) {
self.fold(u64::from(value));
}
#[inline]
fn write_u64(&mut self, value: u64) {
self.fold(value);
}
#[inline]
fn write_u128(&mut self, value: u128) {
self.fold(value as u64);
self.fold((value >> 64) as u64);
}
#[inline]
fn write_usize(&mut self, value: usize) {
self.fold(value as u64);
}
#[inline]
fn finish(&self) -> u64 {
let mut hash = self.hash;
hash ^= hash >> 30;
hash = hash.wrapping_mul(FINALIZE_A);
hash ^= hash >> 27;
hash = hash.wrapping_mul(FINALIZE_B);
hash ^ (hash >> 31)
}
}
#[cfg(test)]
#[path = "tests/fx_hash_tests.rs"]
mod tests;