use std::collections::HashMap;
use std::hash::{BuildHasherDefault, Hasher};
#[cfg(target_pointer_width = "64")]
const MULTIPLIER: usize = 0xf135_7aea_2e62_a9c5;
#[cfg(not(target_pointer_width = "64"))]
const MULTIPLIER: usize = 0x93d7_65dd;
#[cfg(target_pointer_width = "64")]
const ROTATE: u32 = 26;
#[cfg(not(target_pointer_width = "64"))]
const ROTATE: u32 = 15;
#[derive(Clone, Copy, Default)]
pub struct FxHasher {
hash: usize,
}
impl FxHasher {
fn add(&mut self, word: usize) {
self.hash = self.hash.wrapping_add(word).wrapping_mul(MULTIPLIER);
}
}
impl Hasher for FxHasher {
fn write(&mut self, bytes: &[u8]) {
const WIDTH: usize = std::mem::size_of::<usize>();
let mut words = bytes.chunks_exact(WIDTH);
for w in &mut words {
let mut word = [0u8; WIDTH];
word.copy_from_slice(w);
self.add(usize::from_le_bytes(word));
}
let rest = words.remainder();
if !rest.is_empty() {
let mut word = [0u8; WIDTH];
word[..rest.len()].copy_from_slice(rest);
self.add(usize::from_le_bytes(word));
}
}
fn write_u8(&mut self, i: u8) {
self.add(usize::from(i));
}
fn write_u16(&mut self, i: u16) {
self.add(usize::from(i));
}
fn write_u32(&mut self, i: u32) {
self.add(i as usize);
}
fn write_u64(&mut self, i: u64) {
self.add(i as usize);
if usize::BITS < 64 {
self.add((i >> 32) as usize);
}
}
fn write_usize(&mut self, i: usize) {
self.add(i);
}
fn finish(&self) -> u64 {
self.hash.rotate_left(ROTATE) as u64
}
}
pub type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FxHasher>>;
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
use std::hash::BuildHasher;
fn hash(cell: (usize, u32, u32)) -> u64 {
BuildHasherDefault::<FxHasher>::default().hash_one(cell)
}
#[test]
fn a_cell_hashes_the_same_every_time_and_life_s_cells_apart() {
assert_eq!(hash((5, 2, 3)), hash((5, 2, 3)));
let mut seen = HashSet::new();
for sheet in [0usize, 5] {
for row in 1..=200u32 {
for col in 1..=320u32 {
assert!(seen.insert(hash((sheet, row, col))), "{sheet} {row} {col}");
}
}
}
assert_eq!(seen.len(), 128_000);
}
#[test]
fn a_map_under_it_is_a_map() {
let mut m: FxHashMap<(usize, u32, u32), u32> = FxHashMap::default();
for i in 0..1000u32 {
m.insert((0, i, i % 7), i);
}
assert_eq!(m.len(), 1000);
assert_eq!(m.get(&(0, 700, 0)), Some(&700));
assert_eq!(m.remove(&(0, 701, 1)), Some(701));
assert_eq!(m.get(&(0, 701, 1)), None);
}
}