use core::hash::{BuildHasherDefault, Hasher};
const K: u64 = 0xf135_7aea_2e62_a9c5;
#[derive(Debug, Default, Clone, Copy)]
pub struct MapHasher(u64);
impl MapHasher {
#[inline]
fn add(&mut self, word: u64) {
self.0 = self.0.wrapping_add(word).wrapping_mul(K);
}
}
impl Hasher for MapHasher {
#[inline]
fn write(&mut self, bytes: &[u8]) {
let (words, rest) = bytes.as_chunks::<8>();
for word in words {
self.add(u64::from_le_bytes(*word));
}
if !rest.is_empty() {
let mut word = [0u8; 8];
word[..rest.len()].copy_from_slice(rest);
self.add(u64::from_le_bytes(word) ^ ((rest.len() as u64) << 56));
}
}
#[inline]
fn write_u8(&mut self, value: u8) {
self.add(u64::from(value));
}
#[inline]
fn write_u16(&mut self, value: u16) {
self.add(u64::from(value));
}
#[inline]
fn write_u32(&mut self, value: u32) {
self.add(u64::from(value));
}
#[inline]
fn write_u64(&mut self, value: u64) {
self.add(value);
}
#[inline]
fn write_usize(&mut self, value: usize) {
self.add(value as u64);
}
#[inline]
fn finish(&self) -> u64 {
self.0.rotate_left(26)
}
}
pub type BuildMapHasher = BuildHasherDefault<MapHasher>;
#[cfg(all(test, feature = "alloc"))]
mod tests {
use super::*;
use crate::MacAddr;
use core::hash::BuildHasher;
use std::collections::HashMap;
fn hash(key: &(MacAddr, u8)) -> u64 {
BuildMapHasher::default().hash_one(key)
}
#[test]
fn distinct_peers_and_invoke_ids_hash_apart() {
let macs: [&[u8]; 6] = [
&[1],
&[1, 0],
&[0, 1],
&[192, 168, 1, 10, 0xba, 0xc0],
&[192, 168, 1, 11, 0xba, 0xc0],
&[0xff, 0x52, 0, 5, 1, 3, 9],
];
let mut seen = std::collections::HashSet::new();
for mac in macs {
for invoke in [0u8, 1, 255] {
assert!(seen.insert(hash(&(MacAddr::from_slice(mac), invoke))));
}
}
}
#[test]
fn a_map_finds_what_it_holds() {
let mut map: HashMap<_, _, BuildMapHasher> = HashMap::default();
for station in 0..=255u8 {
for invoke in 0..4u8 {
map.insert(
(MacAddr::from_slice(&[station]), invoke),
u16::from(station),
);
}
}
assert_eq!(map.len(), 1024);
assert_eq!(map.get(&(MacAddr::from_slice(&[77]), 3)), Some(&77));
assert!(!map.contains_key(&(MacAddr::from_slice(&[77, 0]), 3)));
}
}