| 1 | |
|
| 2 | |
|
| 3 | |
|
| 4 | |
|
| 5 | |
|
| 6 | | use std::hash::{BuildHasher, Hasher}; |
| 7 | | use std::sync::OnceLock; |
| 8 | |
|
| 9 | 213562× | fn seed() -> u64 { |
| 10 | | static SEED: OnceLock<u64> = OnceLock::new(); |
| 11 | 213562× | *SEED.get_or_init(rand::random::<u64>) |
| 12 | 213562× | } |
| 13 | |
|
| 14 | |
|
| 15 | | #[derive(Clone, Copy, Debug)] |
| 16 | | pub struct FastState(u64); |
| 17 | |
|
| 18 | | impl Default for FastState { |
| 19 | 213562× | fn default() -> FastState { |
| 20 | 213562× | FastState(seed()) |
| 21 | 213562× | } |
| 22 | | } |
| 23 | |
|
| 24 | | impl BuildHasher for FastState { |
| 25 | | type Hasher = FastHasher; |
| 26 | 63147272× | fn build_hasher(&self) -> FastHasher { |
| 27 | 63147272× | FastHasher(self.0) |
| 28 | 63147272× | } |
| 29 | | } |
| 30 | |
|
| 31 | |
|
| 32 | |
|
| 33 | | pub struct FastHasher(u64); |
| 34 | |
|
| 35 | | impl FastHasher { |
| 36 | | #[inline] |
| 37 | 305982258× | fn mix(&mut self, word: u64) { |
| 38 | 305982258× | self.0 = (self.0 ^ word) |
| 39 | 305982258× | .wrapping_mul(0x9e37_79b9_7f4a_7c15) |
| 40 | 305982258× | .rotate_left(31); |
| 41 | 305982258× | } |
| 42 | | } |
| 43 | |
|
| 44 | | impl Hasher for FastHasher { |
| 45 | | #[inline] |
| 46 | 62900874× | fn write(&mut self, bytes: &[u8]) { |
| 47 | 62900874× | let (chunks, rest) = bytes.as_chunks::<8>(); |
| 48 | 208410874× | for c in chunks { |
| 49 | 208410874× | self.mix(u64::from_le_bytes(*c)); |
| 50 | 208410874× | } |
| 51 | 62900874× | if !rest.is_empty() { |
| 52 | 2× | let mut b = [0u8; 8]; |
| 53 | 2× | b[..rest.len()].copy_from_slice(rest); |
| 54 | 2× | self.mix(u64::from_le_bytes(b) ^ ((rest.len() as u64) << 59)); |
| 55 | 62900872× | } |
| 56 | 62900874× | } |
| 57 | |
|
| 58 | | #[inline] |
| 59 | 0× | fn write_u8(&mut self, n: u8) { |
| 60 | 0× | self.mix(n as u64 | 0x100); |
| 61 | 0× | } |
| 62 | |
|
| 63 | | #[inline] |
| 64 | 31431874× | fn write_u32(&mut self, n: u32) { |
| 65 | 31431874× | self.mix(n as u64 | 0x1_0000_0000); |
| 66 | 31431874× | } |
| 67 | |
|
| 68 | | #[inline] |
| 69 | 0× | fn write_u64(&mut self, n: u64) { |
| 70 | 0× | self.mix(n); |
| 71 | 0× | } |
| 72 | |
|
| 73 | | #[inline] |
| 74 | 66139508× | fn write_usize(&mut self, n: usize) { |
| 75 | 66139508× | self.mix(n as u64); |
| 76 | 66139508× | } |
| 77 | |
|
| 78 | | #[inline] |
| 79 | 63147272× | fn finish(&self) -> u64 { |
| 80 | 63147272× | let mut z = self.0; |
| 81 | 63147272× | z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9); |
| 82 | 63147272× | z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb); |
| 83 | 63147272× | z ^ (z >> 31) |
| 84 | 63147272× | } |
| 85 | | } |
| 86 | |
|
| 87 | | pub type FastMap<K, V> = std::collections::HashMap<K, V, FastState>; |
| 88 | | pub type FastSet<K> = std::collections::HashSet<K, FastState>; |
| 89 | |
|
| 90 | | #[cfg(test)] |
| 91 | | mod tests { |
| 92 | | use super::*; |
| 93 | | use std::hash::Hash; |
| 94 | |
|
| 95 | 72× | fn h<T: Hash + ?Sized>(state: &FastState, v: &T) -> u64 { |
| 96 | 72× | state.hash_one(v) |
| 97 | 72× | } |
| 98 | |
|
| 99 | | #[test] |
| 100 | 1× | fn distinct_keys_spread_and_equal_keys_agree() { |
| 101 | 1× | let s = FastState(42); |
| 102 | 1× | let a = ([7u8; 32], 1u32); |
| 103 | 1× | assert_eq!(h(&s, &a), h(&s, &a)); |
| 104 | 1× | assert_ne!(h(&s, &a), h(&s, &([7u8; 32], 2u32)), "the index matters"); |
| 105 | 1× | assert_ne!( |
| 106 | 1× | h(&s, &[1u8, 2, 3][..]), |
| 107 | 1× | h(&s, &[1u8, 2, 3, 0][..]), |
| 108 | | "length matters" |
| 109 | | ); |
| 110 | | |
| 111 | 1× | let buckets: std::collections::HashSet<u64> = |
| 112 | 64× | (0..64u32).map(|i| h(&s, &([9u8; 32], i)) & 63).collect(); |
| 113 | 1× | assert!(buckets.len() > 32, "{} of 64 buckets used", buckets.len()); |
| 114 | 1× | assert_ne!( |
| 115 | 1× | h(&FastState(1), &a), |
| 116 | 1× | h(&FastState(2), &a), |
| 117 | | "the seed matters" |
| 118 | | ); |
| 119 | 1× | } |
| 120 | |
|
| 121 | | #[test] |
| 122 | 1× | fn maps_work() { |
| 123 | 1× | let mut m: FastMap<([u8; 32], u32), u32> = FastMap::default(); |
| 124 | 10000× | for i in 0..10_000u32 { |
| 125 | 10000× | m.insert(([(i % 251) as u8; 32], i), i); |
| 126 | 10000× | } |
| 127 | 1× | assert_eq!(m.len(), 10_000); |
| 128 | 1× | assert_eq!(m[&([77u8; 32], 77)], 77); |
| 129 | 1× | } |
| 130 | | } |