macro_rules! compare_to_c {
($test:ident, $rust_fn:path, $compact_fn:path, $cc_fn:ident) => {
#[test]
fn $test() {
use rand::Rng;
use rapidhash_c::$cc_fn;
let rust_hash = $rust_fn(&[], &DEFAULT_RAPID_SECRETS);
let compact_hash = $compact_fn(&[], &DEFAULT_RAPID_SECRETS);
let c_hash = $cc_fn(&[], DEFAULT_SEED);
assert_eq!(rust_hash, c_hash, "Mismatch with C on zero len input");
assert_eq!(rust_hash, compact_hash, "Mismatch with COMPACT on zero len input");
for len in 0..=512 {
let mut data = std::vec![0; len];
rand::rng().fill(&mut data[..]);
for byte in 0..len {
for bit in 0..8 {
let mut data = data.clone();
data[byte] ^= 1 << bit;
let rust_hash = $rust_fn(&data, &DEFAULT_RAPID_SECRETS);
let compact_hash = $compact_fn(&data, &DEFAULT_RAPID_SECRETS);
let c_hash = $cc_fn(&data, DEFAULT_SEED);
assert_eq!(rust_hash, c_hash, "Mismatch with C on input {} byte {} bit {}", len, byte, bit);
assert_eq!(rust_hash, compact_hash, "Mismatch with COMPACT on input {} byte {} bit {}", len, byte, bit);
}
}
}
}
};
}
macro_rules! flip_bit_trial {
($test:ident, $hash:path) => {
#[test]
fn $test() {
use rand::Rng;
let mut flips = std::vec![];
for len in 1..=256 {
let mut data = std::vec![0; len];
rand::rng().fill(&mut data[..]);
let hash = $hash(&data, &DEFAULT_RAPID_SECRETS);
for byte in 0..len {
for bit in 0..8 {
let mut data = data.clone();
data[byte] ^= 1 << bit;
let new_hash = $hash(&data, &DEFAULT_RAPID_SECRETS);
assert_ne!(hash, new_hash, "Flipping byte {} bit {} did not change hash for input len {}", byte, bit, len);
let xor = hash ^ new_hash;
let flipped = xor.count_ones() as u64;
assert!(xor.count_ones() >= 8, "Flipping bit {byte}:{bit} changed only {flipped} bits");
flips.push(flipped);
}
}
}
let average = flips.iter().sum::<u64>() as f64 / flips.len() as f64;
assert!(average > 31.95 && average < 32.05, "Did not flip an average of half the bits. average: {average}, expected: 32.0");
}
};
}
macro_rules! compare_rapidhash_file {
($test:ident, $hash:path, $file:path) => {
#[test]
fn $test() {
use rand::RngCore;
const LENGTH: usize = 1024;
for len in 1..=LENGTH {
let mut data = vec![0u8; len];
rand::rng().fill_bytes(&mut data);
let mut file = tempfile::tempfile().unwrap();
file.write_all(&data).unwrap();
file.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(
$hash(&data, &DEFAULT_RAPID_SECRETS),
$file(&mut file, &DEFAULT_RAPID_SECRETS).unwrap(),
"Mismatch for input len: {}", &data.len()
);
}
}
};
}
pub(crate) use compare_to_c;
pub(crate) use flip_bit_trial;
pub(crate) use compare_rapidhash_file;