#[cfg(any(feature = "std", docsrs))]
mod collections;
mod rapid_const;
mod rapid_hasher;
mod state;
pub(crate) mod seeding;
mod mix_np;
mod seed;
mod read_np;
#[doc(inline)]
pub use rapid_hasher::*;
#[doc(inline)]
#[cfg(any(feature = "std", docsrs))]
pub use collections::*;
#[doc(inline)]
pub use state::*;
#[doc(inline)]
use seed::*;
#[cfg(test)]
mod tests {
extern crate std;
use std::hash::{BuildHasher, Hash, Hasher};
use std::collections::BTreeSet;
use rand::Rng;
use crate::inner::mix_np::rapid_mix_np;
use super::seed::{DEFAULT_RAPID_SECRETS, DEFAULT_SEED};
use super::rapid_const::{rapidhash_rs, rapidhash_rs_seeded};
type RapidHasher = super::RapidHasher<true, true, true>;
type RapidBuildHasher = super::RapidBuildHasher<true, true, true>;
#[derive(Hash)]
struct Object {
bytes: std::vec::Vec<u8>,
}
#[cfg(target_endian = "little")]
#[test]
fn derive_hash_works() {
let object = Object { bytes: b"hello world".to_vec() };
let mut hasher = RapidHasher::default();
object.hash(&mut hasher);
assert_eq!(hasher.finish(), 9938606849760368330);
let mut hasher = RapidHasher::default();
hasher.write_usize(b"hello world".len());
hasher.write(b"hello world");
assert_eq!(hasher.finish(), 9938606849760368330);
}
#[test]
fn all_sizes() {
let mut hashes = BTreeSet::new();
for size in 0..=1024 {
let mut data = std::vec![0; size];
rand::rng().fill(data.as_mut_slice());
let hash1 = rapidhash_rs(&data);
let mut hasher = RapidHasher::default();
hasher.write(&data);
let hash2 = hasher.finish();
assert_eq!(hash1, hash2, "Failed on size {}", size);
assert!(!hashes.contains(&hash1), "Duplicate for size {}", size);
hashes.insert(hash1);
}
}
#[test]
fn flip_bit_trial() {
use rand::Rng;
let mut flips = std::vec![];
for len in 1..=512 {
let mut data = std::vec![0; len];
rand::rng().fill(&mut data[..]);
let hash = rapidhash_rs(&data);
for byte in 0..len {
for bit in 0..8 {
let mut data = data.clone();
data[byte] ^= 1 << bit;
let new_hash = rapidhash_rs(&data);
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");
}
fn streaming_hash(data: &[u8]) -> u64 {
let mut hasher = RapidHasher::default();
for byte in data {
hasher.write_u8(*byte);
}
hasher.finish()
}
#[test]
fn flip_bit_trial_streaming() {
use rand::Rng;
let mut flips = std::vec![];
for len in 1..=300 {
let mut data = std::vec![0; len];
rand::rng().fill(&mut data[..]);
let hash = streaming_hash(&data);
for byte in 0..len {
for bit in 0..8 {
let mut data = data.clone();
data[byte] ^= 1 << bit;
let new_hash = streaming_hash(&data);
assert_ne!(hash, new_hash, "Flipping bit {byte}:{bit} for input len {len} did not change hash");
let xor = hash ^ new_hash;
let flipped = xor.count_ones() as u64;
assert!(xor.count_ones() >= 8, "Flipping bit {byte}:{bit} for input len {len} 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");
}
#[cfg(target_endian = "little")]
#[test]
fn compare_to_c() {
use rand::Rng;
use rapidhash_c::rapidhashcc_rs;
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 = rapidhash_rs_seeded(&data, &DEFAULT_RAPID_SECRETS);
let mut c_hash = rapidhashcc_rs(&data, DEFAULT_SEED);
c_hash = rapid_mix_np::<false>(c_hash, DEFAULT_RAPID_SECRETS.secrets[1]);
assert_eq!(rust_hash, c_hash, "Mismatch with input {} byte {} bit {}", len, byte, bit);
let mut rust_hasher = RapidBuildHasher::default().build_hasher();
rust_hasher.write(&data);
let rust_hasher_hash = rust_hasher.finish();
assert_eq!(rust_hash, rust_hasher_hash, "Hasher mismatch with input {} byte {} bit {}", len, byte, bit);
}
}
}
}
#[test]
fn disambiguation_check() {
use std::vec::Vec;
let hasher = RapidBuildHasher::default();
let a = [std::vec![1], std::vec![2, 3]];
let b = [std::vec![1, 2], std::vec![3]];
assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
let a = [std::vec![], std::vec![1]];
let b = [std::vec![1], std::vec![]];
assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
let a: [Vec<Vec<u64>>; 2] = [std::vec![], std::vec![std::vec![]]];
let b: [Vec<Vec<u64>>; 2] = [std::vec![std::vec![]], std::vec![]];
assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
let a = ["abc", "def"];
let b = ["fed", "abc"];
assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
let a = ["abc", "def"];
let b = ["abcd", "ef"];
assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
let a = [1u8, 2];
let b = [2u8, 1];
assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
let a = [1u16, 2];
let b = [2u16, 1];
assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
let a = [1u32, 2];
let b = [2u32, 1];
assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
let a = [1u64, 2];
let b = [2u64, 1];
assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
let a = [1u128, 2];
let b = [2u128, 1];
assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
}
}