1use crate::Backend;
28
29#[inline]
34#[must_use]
35pub fn hash_key(key: &str) -> u64 {
36 hash_bytes(key.as_bytes())
37}
38
39#[inline]
41#[must_use]
42pub fn hash_bytes(bytes: &[u8]) -> u64 {
43 const K: u64 = 0x517c_c1b7_2722_0a95;
45 let mut hash: u64 = 0;
46
47 let chunks = bytes.chunks_exact(8);
49 let remainder = chunks.remainder();
50
51 for chunk in chunks {
52 let word =
53 u64::from_le_bytes(chunk.try_into().expect("chunks_exact(8) guarantees 8 bytes"));
54 hash = hash.rotate_left(5).bitxor(word).wrapping_mul(K);
55 }
56
57 for &byte in remainder {
59 hash = hash.rotate_left(5).bitxor(u64::from(byte)).wrapping_mul(K);
60 }
61
62 hash
63}
64
65#[must_use]
70pub fn hash_keys_batch(keys: &[&str]) -> Vec<u64> {
71 hash_keys_batch_with_backend(keys, Backend::Auto)
72}
73
74#[must_use]
76pub fn hash_keys_batch_with_backend(keys: &[&str], backend: Backend) -> Vec<u64> {
77 match backend {
78 Backend::Auto => {
79 #[cfg(target_arch = "x86_64")]
80 {
81 if is_x86_feature_detected!("avx2") {
82 return hash_keys_avx2(keys);
83 }
84 }
85 hash_keys_scalar(keys)
86 }
87 Backend::AVX2 | Backend::AVX512 => hash_keys_avx2_or_scalar(keys),
88 Backend::Scalar
89 | Backend::SSE2
90 | Backend::AVX
91 | Backend::NEON
92 | Backend::WasmSIMD
93 | Backend::GPU => hash_keys_scalar(keys),
94 }
95}
96
97#[inline]
99fn hash_keys_scalar(keys: &[&str]) -> Vec<u64> {
100 keys.iter().map(|k| hash_key(k)).collect()
101}
102
103#[inline]
105fn hash_keys_avx2_or_scalar(keys: &[&str]) -> Vec<u64> {
106 #[cfg(target_arch = "x86_64")]
107 {
108 hash_keys_avx2(keys)
109 }
110 #[cfg(not(target_arch = "x86_64"))]
111 {
112 hash_keys_scalar(keys)
113 }
114}
115
116#[cfg(target_arch = "x86_64")]
118fn hash_keys_avx2(keys: &[&str]) -> Vec<u64> {
119 hash_keys_scalar(keys)
122}
123
124use std::ops::BitXor;
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
135 fn test_hash_key_deterministic() {
136 let h1 = hash_key("hello");
137 let h2 = hash_key("hello");
138 assert_eq!(h1, h2, "Same key must produce same hash");
139 }
140
141 #[test]
142 fn test_hash_key_different_keys() {
143 let h1 = hash_key("hello");
144 let h2 = hash_key("world");
145 assert_ne!(h1, h2, "Different keys should produce different hashes");
146 }
147
148 #[test]
149 fn test_hash_key_empty() {
150 let h = hash_key("");
151 assert_eq!(h, 0);
153 }
154
155 #[test]
156 fn test_hash_key_single_char() {
157 let h = hash_key("a");
158 assert_ne!(h, 0);
159 }
160
161 #[test]
162 fn test_hash_key_long_string() {
163 let long = "a".repeat(1000);
164 let h = hash_key(&long);
165 assert_ne!(h, 0);
166 }
167
168 #[test]
169 fn test_hash_bytes_matches_key() {
170 let key = "test_key";
171 assert_eq!(hash_key(key), hash_bytes(key.as_bytes()));
172 }
173
174 #[test]
175 fn test_hash_keys_batch_empty() {
176 let keys: &[&str] = &[];
177 let hashes = hash_keys_batch(keys);
178 assert!(hashes.is_empty());
179 }
180
181 #[test]
182 fn test_hash_keys_batch_single() {
183 let hashes = hash_keys_batch(&["hello"]);
184 assert_eq!(hashes.len(), 1);
185 assert_eq!(hashes[0], hash_key("hello"));
186 }
187
188 #[test]
189 fn test_hash_keys_batch_multiple() {
190 let keys = ["a", "b", "c", "d"];
191 let hashes = hash_keys_batch(&keys);
192
193 assert_eq!(hashes.len(), 4);
194 for (i, key) in keys.iter().enumerate() {
195 assert_eq!(hashes[i], hash_key(key), "Batch hash must match single hash");
196 }
197 }
198
199 #[test]
200 fn test_hash_keys_batch_large() {
201 let keys: Vec<&str> = (0..100)
202 .map(|i| {
203 Box::leak(format!("key{i}").into_boxed_str()) as &str
205 })
206 .collect();
207
208 let hashes = hash_keys_batch(&keys);
209 assert_eq!(hashes.len(), 100);
210
211 let unique: std::collections::HashSet<_> = hashes.iter().collect();
213 assert_eq!(unique.len(), 100, "All keys should have unique hashes");
214 }
215
216 #[test]
217 fn test_backend_parity_scalar_vs_auto() {
218 let keys = ["foo", "bar", "baz", "qux"];
219
220 let scalar = hash_keys_batch_with_backend(&keys, Backend::Scalar);
221 let auto = hash_keys_batch_with_backend(&keys, Backend::Auto);
222
223 assert_eq!(scalar, auto, "Scalar and Auto must produce identical results");
224 }
225
226 #[test]
227 fn test_hash_distribution() {
228 let keys: Vec<String> = (0..1000).map(|i| format!("key{i}")).collect();
230 let refs: Vec<&str> = keys.iter().map(|s| s.as_str()).collect();
231 let hashes = hash_keys_batch(&refs);
232
233 let high_bits_used = hashes.iter().any(|h| h >> 56 != 0);
235 assert!(high_bits_used, "Hash should use high bits");
236
237 let low_nibbles: std::collections::HashSet<_> = hashes.iter().map(|h| h & 0xF).collect();
239 assert!(low_nibbles.len() >= 8, "Hash should have varied low bits");
240 }
241
242 #[test]
243 fn test_hash_avalanche_single_bit() {
244 let h1 = hash_key("aaa");
246 let h2 = hash_key("aab"); let diff = (h1 ^ h2).count_ones();
249 assert!(diff >= 15, "Avalanche effect: {} bits differ, expected >=15", diff);
251 }
252
253 #[test]
254 fn test_backend_avx2_explicit() {
255 let keys = ["foo", "bar", "baz", "qux"];
256 let avx2 = hash_keys_batch_with_backend(&keys, Backend::AVX2);
257 let scalar = hash_keys_batch_with_backend(&keys, Backend::Scalar);
258 assert_eq!(avx2, scalar, "AVX2 must match Scalar");
259 }
260
261 #[test]
262 fn test_backend_avx512_explicit() {
263 let keys = ["foo", "bar", "baz", "qux"];
264 let avx512 = hash_keys_batch_with_backend(&keys, Backend::AVX512);
265 let scalar = hash_keys_batch_with_backend(&keys, Backend::Scalar);
266 assert_eq!(avx512, scalar, "AVX512 must match Scalar");
267 }
268
269 #[test]
270 fn test_backend_sse2_fallback() {
271 let keys = ["a", "b", "c"];
272 let sse2 = hash_keys_batch_with_backend(&keys, Backend::SSE2);
273 let scalar = hash_keys_batch_with_backend(&keys, Backend::Scalar);
274 assert_eq!(sse2, scalar, "SSE2 must fall back to Scalar");
275 }
276
277 #[test]
278 fn test_backend_neon_fallback() {
279 let keys = ["x", "y", "z"];
280 let neon = hash_keys_batch_with_backend(&keys, Backend::NEON);
281 let scalar = hash_keys_batch_with_backend(&keys, Backend::Scalar);
282 assert_eq!(neon, scalar, "NEON must fall back to Scalar");
283 }
284
285 #[test]
286 fn test_hash_keys_avx2_or_scalar_coverage() {
287 let keys = ["test1", "test2"];
289 let result = hash_keys_batch_with_backend(&keys, Backend::AVX2);
290 assert_eq!(result.len(), 2);
291 assert_eq!(result[0], hash_key("test1"));
292 assert_eq!(result[1], hash_key("test2"));
293 }
294}