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