1use std::hash::{BuildHasherDefault, Hasher};
11
12pub type FastMap<K, V> = std::collections::HashMap<K, V, BuildHasherDefault<FxHasher>>;
14
15pub type FastSet<K> = std::collections::HashSet<K, BuildHasherDefault<FxHasher>>;
17
18const SEED: u64 = 0x51_7c_c1_b7_27_22_0a_95;
19const ROTATE: u32 = 5;
20
21#[derive(Default)]
22pub struct FxHasher {
23 hash: u64,
24}
25
26impl FxHasher {
27 #[inline]
28 fn add(&mut self, word: u64) {
29 self.hash = (self.hash.rotate_left(ROTATE) ^ word).wrapping_mul(SEED);
30 }
31}
32
33impl Hasher for FxHasher {
34 #[inline]
35 fn write(&mut self, mut bytes: &[u8]) {
36 while bytes.len() >= 8 {
37 let mut chunk = [0u8; 8];
38 chunk.copy_from_slice(&bytes[..8]);
39 self.add(u64::from_le_bytes(chunk));
40 bytes = &bytes[8..];
41 }
42 if bytes.len() >= 4 {
43 let mut chunk = [0u8; 4];
44 chunk.copy_from_slice(&bytes[..4]);
45 self.add(u64::from(u32::from_le_bytes(chunk)));
46 bytes = &bytes[4..];
47 }
48 for &byte in bytes {
49 self.add(u64::from(byte));
50 }
51 }
52
53 #[inline]
54 fn write_u8(&mut self, value: u8) {
55 self.add(u64::from(value));
56 }
57
58 #[inline]
59 fn write_u16(&mut self, value: u16) {
60 self.add(u64::from(value));
61 }
62
63 #[inline]
64 fn write_u32(&mut self, value: u32) {
65 self.add(u64::from(value));
66 }
67
68 #[inline]
69 fn write_u64(&mut self, value: u64) {
70 self.add(value);
71 }
72
73 #[inline]
74 fn write_usize(&mut self, value: usize) {
75 self.add(value as u64);
76 }
77
78 #[inline]
79 fn finish(&self) -> u64 {
80 self.hash
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn equal_keys_hash_equal_and_differ_from_neighbours() {
90 let hash = |key: &str| {
91 let mut hasher = FxHasher::default();
92 hasher.write(key.as_bytes());
93 hasher.finish()
94 };
95 assert_eq!(hash("ppt/slides/slide1.xml"), hash("ppt/slides/slide1.xml"));
96 assert_ne!(hash("ppt/slides/slide1.xml"), hash("ppt/slides/slide2.xml"));
97 assert_ne!(hash("rId1"), hash("rId2"));
98 }
99
100 #[test]
101 fn fast_map_round_trips() {
102 let mut map: FastMap<&str, u32> = FastMap::default();
103 map.insert("a", 1);
104 map.insert("b", 2);
105 assert_eq!(map.get("a"), Some(&1));
106 assert_eq!(map.get("c"), None);
107 }
108}