1const SECRET: [u64; 4] = [
15 0x2d35_8dcc_aa6c_78a5,
16 0x8bb8_4b93_962e_acc9,
17 0x4b33_a62e_d433_d4a3,
18 0x4d5a_2da5_1de1_aa47,
19];
20
21#[inline(always)]
23fn mum(a: u64, b: u64) -> (u64, u64) {
24 let r = (a as u128).wrapping_mul(b as u128);
25 (r as u64, (r >> 64) as u64)
26}
27
28#[inline(always)]
29fn mix(a: u64, b: u64) -> u64 {
30 let (lo, hi) = mum(a, b);
31 lo ^ hi
32}
33
34#[inline(always)]
35fn r8(p: &[u8]) -> u64 {
36 u64::from_le_bytes([p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]])
37}
38
39#[inline(always)]
40fn r4(p: &[u8]) -> u64 {
41 u32::from_le_bytes([p[0], p[1], p[2], p[3]]) as u64
42}
43
44#[inline(always)]
45fn r3(p: &[u8], k: usize) -> u64 {
46 ((p[0] as u64) << 16) | ((p[k >> 1] as u64) << 8) | (p[k - 1] as u64)
47}
48
49#[inline]
54pub fn wyhash(key: &[u8], seed: u64) -> u64 {
55 let len = key.len();
56 let mut seed = seed ^ mix(seed ^ SECRET[0], SECRET[1]);
57 let a: u64;
58 let b: u64;
59
60 if len <= 16 {
61 if len >= 4 {
62 let q = (len >> 3) << 2;
66 a = (r4(key) << 32) | r4(&key[q..]);
67 b = (r4(&key[len - 4..]) << 32) | r4(&key[len - 4 - q..]);
68 } else if len > 0 {
69 a = r3(key, len);
70 b = 0;
71 } else {
72 a = 0;
73 b = 0;
74 }
75 } else {
76 let mut off = 0usize;
80 let mut i = len;
81
82 if i >= 48 {
83 let mut see1 = seed;
84 let mut see2 = seed;
85 while i >= 48 {
86 seed = mix(r8(&key[off..]) ^ SECRET[1], r8(&key[off + 8..]) ^ seed);
87 see1 = mix(
88 r8(&key[off + 16..]) ^ SECRET[2],
89 r8(&key[off + 24..]) ^ see1,
90 );
91 see2 = mix(
92 r8(&key[off + 32..]) ^ SECRET[3],
93 r8(&key[off + 40..]) ^ see2,
94 );
95 off += 48;
96 i -= 48;
97 }
98 seed ^= see1 ^ see2;
99 }
100
101 while i > 16 {
102 seed = mix(r8(&key[off..]) ^ SECRET[1], r8(&key[off + 8..]) ^ seed);
103 off += 16;
104 i -= 16;
105 }
106
107 a = r8(&key[len - 16..]);
109 b = r8(&key[len - 8..]);
110 }
111
112 let (a, b) = mum(a ^ SECRET[1], b ^ seed);
113 mix(a ^ SECRET[0] ^ (len as u64), b ^ SECRET[1])
114}
115
116#[inline(always)]
121pub fn hash_key(key: &[u8]) -> u64 {
122 wyhash(key, 0)
123}
124
125#[inline(always)]
132pub const fn tag_of(hash: u64) -> u8 {
133 let t = (hash >> 56) as u8;
134 if t == 0 { 1 } else { t }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 fn push_num(out: &mut Vec<u8>, n: u32) {
143 if n >= 10 {
144 push_num(out, n / 10);
145 }
146 out.push(b'0' + (n % 10) as u8);
147 }
148
149 #[test]
162 fn reference_vectors() {
163 const VECTORS: &[(usize, u64, u64)] = &[
164 (0, 0, 0x93228a4de0eec5a2),
165 (0, 0xdeadbeefcafe1234, 0x503eb6b76bdddf6a),
166 (1, 0, 0xb743f05e0334871c),
167 (1, 0xdeadbeefcafe1234, 0xa6d34f7586585149),
168 (2, 0, 0x28b8761367a2c6fe),
169 (2, 0xdeadbeefcafe1234, 0xfb04ad55c47b05d5),
170 (3, 0, 0x51f03dcee96db0fa),
171 (3, 0xdeadbeefcafe1234, 0xb3b1e9b933f562dc),
172 (4, 0, 0x63e00718a6d4f4b7),
173 (4, 0xdeadbeefcafe1234, 0x4855ac52ab265d8f),
174 (5, 0, 0x9ef5dce95adebad0),
175 (5, 0xdeadbeefcafe1234, 0x8e3109951c14d448),
176 (7, 0, 0x78f6520add84ac5e),
177 (7, 0xdeadbeefcafe1234, 0x1d55d13730d8415c),
178 (8, 0, 0x98bbb3a2b3bba145),
179 (8, 0xdeadbeefcafe1234, 0x1d7d14601c86064f),
180 (9, 0, 0x8a4336709aa03e64),
181 (9, 0xdeadbeefcafe1234, 0xa6114ea701c654b9),
182 (15, 0, 0x398099ca2f2dfcd5),
183 (15, 0xdeadbeefcafe1234, 0x07400d0acd97aa2f),
184 (16, 0, 0x31692917ca3ab597),
185 (16, 0xdeadbeefcafe1234, 0xb8ff17cdb0d4b1a8),
186 (17, 0, 0x1cde6dc1d5cb836a),
187 (17, 0xdeadbeefcafe1234, 0x3c83b9dfecbe18a4),
188 (18, 0, 0x7d6c4fc7691eec86),
189 (18, 0xdeadbeefcafe1234, 0x2e41d8a4f547009b),
190 (31, 0, 0x559ac6703ac3d00e),
191 (31, 0xdeadbeefcafe1234, 0x7490b7565d08d3aa),
192 (32, 0, 0x869edcb7f8c6e88d),
193 (32, 0xdeadbeefcafe1234, 0xfde153bdc921f628),
194 (33, 0, 0x25a357dbecbab818),
195 (33, 0xdeadbeefcafe1234, 0x75d4db744c1ad719),
196 (47, 0, 0x26a8fa17ae56094e),
197 (47, 0xdeadbeefcafe1234, 0xc7abd5000339a9db),
198 (48, 0, 0xc7f0608db098eb0d),
199 (48, 0xdeadbeefcafe1234, 0x8b90c58340ad764c),
200 (49, 0, 0xd3f0a40ba8f9b1a6),
201 (49, 0xdeadbeefcafe1234, 0x132f911560ef3fa5),
202 (63, 0, 0x325a689473b1c1a5),
203 (63, 0xdeadbeefcafe1234, 0x6b7f55c7b078e75c),
204 (64, 0, 0x3d6436f595468fea),
205 (64, 0xdeadbeefcafe1234, 0x6421f0f5d6084af7),
206 (65, 0, 0x37d6f92314dcc53b),
207 (65, 0xdeadbeefcafe1234, 0xfcf7ea1b14bfc627),
208 (95, 0, 0x648820b80a4835b4),
209 (95, 0xdeadbeefcafe1234, 0xc379879b80991aae),
210 (96, 0, 0x31105c4a5a7062f0),
211 (96, 0xdeadbeefcafe1234, 0xb55bf2517b5725fb),
212 (97, 0, 0x8d28a022c26475ab),
213 (97, 0xdeadbeefcafe1234, 0xbf1d8e4618c274db),
214 (127, 0, 0xf58addc6ccbb43d7),
215 (127, 0xdeadbeefcafe1234, 0xca48f21107ab1ce0),
216 (128, 0, 0x2d766ba73db255df),
217 (128, 0xdeadbeefcafe1234, 0x913fedabb47d5cad),
218 (200, 0, 0xe11fd2a4ab654ef4),
219 (200, 0xdeadbeefcafe1234, 0x592bbccac660734e),
220 (255, 0, 0x54ad42c80e00a501),
221 (255, 0xdeadbeefcafe1234, 0x67f85b63c0948ebf),
222 (299, 0, 0x5ffcff99c0b5fa98),
223 (299, 0xdeadbeefcafe1234, 0x7aa342b1566d19f8),
224 ];
225
226 let mut buf = [0u8; 300];
227 for (i, b) in buf.iter_mut().enumerate() {
228 *b = ((i * 7 + 13) % 251) as u8;
229 }
230 for &(len, seed, want) in VECTORS {
231 assert_eq!(
232 wyhash(&buf[..len], seed),
233 want,
234 "length {len} seed {seed:#x} does not match the reference"
235 );
236 }
237 }
238
239 #[test]
243 fn all_lengths_up_to_200() {
244 let buf: Vec<u8> = (0..200u32).map(|i| (i % 251) as u8).collect();
245 for n in 0..=200 {
246 let _ = wyhash(&buf[..n], 0);
247 }
248 }
249
250 #[test]
251 fn tag_is_never_zero() {
252 assert_eq!(tag_of(0x00ff_ffff_ffff_ffff), 1);
254 assert_eq!(tag_of(0xab00_0000_0000_0000), 0xab);
255 }
256
257 #[test]
261 fn sequential_keys_spread_over_buckets() {
262 let (buckets, keys): (usize, u32) = if cfg!(miri) {
269 (64, 6_400)
270 } else {
271 (1024, 100_000)
272 };
273 let mut counts = vec![0u32; buckets];
274 let mut k = Vec::with_capacity(16);
275 for i in 0..keys {
276 k.clear();
281 k.extend_from_slice(b"key:");
282 push_num(&mut k, i);
283 counts[(hash_key(&k) as usize) & (buckets - 1)] += 1;
284 }
285 let max = *counts.iter().max().unwrap();
286 let mean = keys / buckets as u32;
287 assert!(max < mean * 3, "worst bucket {max} against mean {mean}");
288 assert!(counts.iter().all(|&c| c > 0), "some bucket got nothing");
289 }
290}