use std::collections::{HashMap, HashSet};
use std::hash::{BuildHasherDefault, Hasher};
pub type Map<K, V> = HashMap<K, V, BuildHasherDefault<Mix>>;
pub type Set<T> = HashSet<T, BuildHasherDefault<Mix>>;
#[derive(Debug, Default, Clone, Copy)]
pub struct Mix(u64);
impl Mix {
fn mix(&mut self, word: u64) {
self.0 = (self.0.rotate_left(5) ^ word).wrapping_mul(0x9e37_79b9_7f4a_7c15);
}
}
impl Hasher for Mix {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, bytes: &[u8]) {
for chunk in bytes.chunks(8) {
let mut word = [0; 8];
word[..chunk.len()].copy_from_slice(chunk);
self.mix(u64::from_le_bytes(word));
}
}
fn write_u8(&mut self, byte: u8) {
self.mix(u64::from(byte));
}
fn write_u16(&mut self, half: u16) {
self.mix(u64::from(half));
}
fn write_u32(&mut self, word: u32) {
self.mix(u64::from(word));
}
fn write_u64(&mut self, word: u64) {
self.mix(word);
}
fn write_usize(&mut self, word: usize) {
self.mix(word as u64);
}
}
#[cfg(test)]
mod tests {
use std::hash::{BuildHasher, BuildHasherDefault};
use super::{Map, Mix, Set};
#[test]
fn a_key_hashes_the_same_way_every_time_and_is_found_again() {
let build = BuildHasherDefault::<Mix>::default();
assert_eq!(build.hash_one((3u8, 17u32)), build.hash_one((3u8, 17u32)));
assert_ne!(build.hash_one(1u32), build.hash_one(2u32));
assert_ne!(build.hash_one("add.i32"), build.hash_one("add.i64"));
let mut map: Map<u32, usize> = Map::default();
let mut set: Set<&str> = Set::default();
for number in 0..1000 {
map.insert(number * 7, number as usize);
}
for name in ["mov", "movzx", "movsx", "lea", "a name longer than eight bytes"] {
set.insert(name);
}
assert!((0..1000).all(|number| map[&(number * 7)] == number as usize));
assert_eq!(map.get(&1), None);
assert!(set.contains("a name longer than eight bytes"));
assert!(!set.contains("a name longer than eight byte"));
}
}