use std::collections::HashMap;
use std::hash::{BuildHasherDefault, Hasher};
#[derive(Default)]
pub(crate) struct TypeIdHasher {
state: u64,
}
impl Hasher for TypeIdHasher {
#[inline]
fn finish(&self) -> u64 {
self.state
}
#[inline]
fn write_u64(&mut self, n: u64) {
self.state = n;
}
#[inline]
fn write_u128(&mut self, n: u128) {
self.state = (n as u64) ^ ((n >> 64) as u64);
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
let mut state = self.state;
for chunk in bytes.chunks(8) {
let mut buf = [0u8; 8];
buf[..chunk.len()].copy_from_slice(chunk);
state = state.rotate_left(5) ^ u64::from_ne_bytes(buf);
}
self.state = state;
}
}
pub(crate) type TypeIdMap<V> = HashMap<std::any::TypeId, V, BuildHasherDefault<TypeIdHasher>>;
#[cfg(test)]
mod tests {
use super::*;
use std::any::TypeId;
#[test]
fn distinct_typeids_map_to_distinct_buckets_in_typical_use() {
let mut map: TypeIdMap<&'static str> = TypeIdMap::default();
let _ = map.insert(TypeId::of::<u8>(), "u8");
let _ = map.insert(TypeId::of::<u16>(), "u16");
let _ = map.insert(TypeId::of::<u32>(), "u32");
let _ = map.insert(TypeId::of::<u64>(), "u64");
let _ = map.insert(TypeId::of::<String>(), "String");
assert_eq!(map.get(&TypeId::of::<u8>()).copied(), Some("u8"));
assert_eq!(map.get(&TypeId::of::<u16>()).copied(), Some("u16"));
assert_eq!(map.get(&TypeId::of::<u32>()).copied(), Some("u32"));
assert_eq!(map.get(&TypeId::of::<u64>()).copied(), Some("u64"));
assert_eq!(map.get(&TypeId::of::<String>()).copied(), Some("String"));
assert_eq!(map.get(&TypeId::of::<i8>()), None);
}
#[test]
fn hasher_finish_is_identity_for_write_u64() {
let mut h = TypeIdHasher::default();
h.write_u64(0xDEAD_BEEF_CAFE_BABE);
assert_eq!(h.finish(), 0xDEAD_BEEF_CAFE_BABE);
}
#[test]
fn hasher_finish_is_xor_fold_for_write_u128() {
let mut h = TypeIdHasher::default();
h.write_u128(0xAAAA_BBBB_CCCC_DDDD_1111_2222_3333_4444);
assert_eq!(h.finish(), 0x1111_2222_3333_4444 ^ 0xAAAA_BBBB_CCCC_DDDD);
}
}