use crate::asm_presets::hash_u64x2_aesni;
use crate::simd::arithmetic::{BatchError, cold_batch_error_length_mismatch};
pub fn batch_hash(keys: &[u64], hashes: &mut [u64]) -> Result<(), BatchError> {
if keys.len() != hashes.len() {
return cold_batch_error_length_mismatch();
}
let n = keys.len();
let mut i = 0;
while i + 2 <= n {
let (lo, hi) = hash_u64x2_aesni::apply(keys[i], keys[i + 1]);
hashes[i] = lo;
hashes[i + 1] = hi;
i += 2;
}
if i < n {
let companion = keys[i].rotate_left(31);
let (lo, hi) = hash_u64x2_aesni::apply(keys[i], companion);
hashes[i] = lo ^ hi;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn distinct_keys_produce_distinct_hashes() {
let keys: Vec<u64> = (0..16u64).map(|i| 0xdead_0000_0000_0000 | i).collect();
let mut hashes = vec![0_u64; 16];
batch_hash(&keys, &mut hashes).expect("ok");
for i in 0..16 {
for j in (i + 1)..16 {
assert_ne!(hashes[i], hashes[j], "collision at {i},{j}");
}
}
}
#[test]
fn hash_is_deterministic() {
let keys = [1_u64, 2, 3, 4, 5];
let mut h1 = [0_u64; 5];
let mut h2 = [0_u64; 5];
batch_hash(&keys, &mut h1).expect("ok");
batch_hash(&keys, &mut h2).expect("ok");
assert_eq!(h1, h2);
}
#[test]
fn length_mismatch_errors() {
let keys = [1_u64, 2];
let mut hashes = [0_u64; 3];
assert!(batch_hash(&keys, &mut hashes).is_err());
}
}