1mod rapid_const;
31mod rapid_hasher;
32mod state;
33pub(crate) mod seeding;
34mod mix_np;
35mod seed;
36mod read_np;
37
38#[doc(inline)]
39pub use rapid_hasher::*;
40#[doc(inline)]
41pub use state::*;
42#[doc(inline)]
43use seed::*;
44
45#[cfg(test)]
46mod tests {
47 extern crate std;
48
49 use std::hash::{BuildHasher, Hash, Hasher};
50 use std::collections::BTreeSet;
51 use rand::RngExt;
52 use rapidrand::RapidRng;
53 use crate::inner::mix_np::rapid_mix_np;
54 use super::seed::{DEFAULT_RAPID_SECRETS, DEFAULT_SEED};
55 use super::rapid_const::{rapidhash_rs, rapidhash_rs_seeded};
56
57 type RapidHasher = super::RapidHasher<'static, true, true, true, false>;
58 type SeedableState = super::SeedableState<'static, true, true, true, false>;
59
60 #[derive(Hash)]
61 struct Object {
62 string: &'static str,
63 }
64
65 #[cfg(any(
71 all(
72 target_pointer_width = "64",
73 not(any(target_arch = "sparc64", target_arch = "wasm64")),
74 ),
75 target_arch = "aarch64",
76 target_arch = "x86_64",
77 all(target_family = "wasm", target_feature = "wide-arithmetic"),
78 ))]
79 #[cfg(target_endian = "little")]
80 #[test]
81 fn derive_hash_works() {
82 #[cfg(not(feature = "nightly"))]
83 const EXPECTED: u64 = 7608958509739739138;
84
85 #[cfg(feature = "nightly")]
86 const EXPECTED: u64 = 8977256838778740407;
87
88 let object = Object { string: "hello world" };
89 let mut hasher = RapidHasher::default();
90 object.hash(&mut hasher);
91 assert_eq!(hasher.finish(), EXPECTED);
92
93 let mut hasher = RapidHasher::default();
94 hasher.write(object.string.as_bytes());
95 #[cfg(not(feature = "nightly"))] {
96 hasher.write_u8(0xFF);
97 }
98 assert_eq!(hasher.finish(), EXPECTED);
99 }
100
101 #[test]
105 fn all_sizes() {
106 let mut rng: RapidRng = rand::make_rng();
107 let mut hashes = BTreeSet::new();
108
109 for size in 0..=1024 {
110 let mut data = std::vec![0; size];
111 rng.fill(data.as_mut_slice());
112
113 let hash1 = rapidhash_rs(&data);
114 let mut hasher = RapidHasher::default();
115 hasher.write(&data);
116 let hash2 = hasher.finish();
117
118 assert_eq!(hash1, hash2, "Failed on size {}", size);
119 assert!(!hashes.contains(&hash1), "Duplicate for size {}", size);
120
121 hashes.insert(hash1);
122 }
123 }
124
125 #[test]
130 fn flip_bit_trial() {
131 let mut rng: RapidRng = rand::make_rng();
132 let mut flips = std::vec![];
133
134 for len in 1..=512 {
135 let mut data = std::vec![0; len];
136 rng.fill(&mut data[..]);
137
138 let hash = rapidhash_rs(&data);
139 for byte in 0..len {
140 for bit in 0..8 {
141 let mut data = data.clone();
142 data[byte] ^= 1 << bit;
143 let new_hash = rapidhash_rs(&data);
144 assert_ne!(hash, new_hash, "Flipping byte {} bit {} did not change hash for input len {}", byte, bit, len);
145 let xor = hash ^ new_hash;
146 let flipped = xor.count_ones() as u64;
147 assert!(xor.count_ones() >= 8, "Flipping bit {byte}:{bit} changed only {flipped} bits");
148
149 flips.push(flipped);
150 }
151 }
152 }
153
154 let average = flips.iter().sum::<u64>() as f64 / flips.len() as f64;
155 assert!(average > 31.95 && average < 32.05, "Did not flip an average of half the bits. average: {average}, expected: 32.0");
156 }
157
158 fn streaming_hash(data: &[u8]) -> u64 {
160 let mut hasher = RapidHasher::default();
161 for byte in data {
162 hasher.write_u8(*byte);
163 }
164 hasher.finish()
165 }
166
167 #[test]
171 fn sponge_buffer_stability() {
172 use std::collections::HashSet;
173
174 fn manual_string_hash(data: &[u8]) -> u64 {
176 let mut hasher = crate::inner::SeedableState::<'static, false, true, false, false>::fixed().build_hasher();
178 for byte in data {
179 hasher.write_u8(*byte);
180 }
181 hasher.write_u8(0xFF); hasher.finish()
183 }
184
185 let mut hashes = HashSet::new();
186
187 for len in 1..=64 {
188 for byte in 0u8..=255 {
189 let data = std::vec![byte; len];
191
192 let hash1 = manual_string_hash(&data);
193 let hash2 = manual_string_hash(&data);
194 assert_eq!(hash1, hash2, "Mismatch for length {}", len);
195
196 assert!(!hashes.contains(&hash1), "Duplicate hash at length {}", len);
197 hashes.insert(hash1);
198 }
199 }
200 }
201
202 #[test]
209 fn flip_bit_trial_streaming() {
210 let mut rng: RapidRng = rand::make_rng();
211 let mut flips = std::vec![];
212
213 for len in 1..=300 {
214 let mut data = std::vec![0; len];
215 rng.fill(&mut data[..]);
216
217 let hash = streaming_hash(&data);
218 for byte in 0..len {
219 for bit in 0..8 {
220 let mut data = data.clone();
221 data[byte] ^= 1 << bit;
222
223 let new_hash = streaming_hash(&data);
225 assert_ne!(hash, new_hash, "Flipping bit {byte}:{bit} for input len {len} did not change hash");
226
227 let xor = hash ^ new_hash;
229 let flipped = xor.count_ones() as u64;
230 assert!(xor.count_ones() >= 8, "Flipping bit {byte}:{bit} for input len {len} changed only {flipped} bits");
231 flips.push(flipped);
232 }
233 }
234 }
235
236 let average = flips.iter().sum::<u64>() as f64 / flips.len() as f64;
238 assert!(average > 31.95 && average < 32.05, "Did not flip an average of half the bits. average: {average}, expected: 32.0");
239 }
240
241 #[cfg(any(
246 all(
247 target_pointer_width = "64",
248 not(any(target_arch = "sparc64", target_arch = "wasm64")),
249 ),
250 target_arch = "aarch64",
251 target_arch = "x86_64",
252 all(target_family = "wasm", target_feature = "wide-arithmetic"),
253 ))]
254 #[cfg(target_endian = "little")]
255 #[test]
256 fn compare_to_c() {
257 use rapidhash_c::rapidhashcc_rs;
258 let mut rng: RapidRng = rand::make_rng();
259
260 for len in 0..=512 {
261 let mut data = std::vec![0; len];
262 rng.fill(&mut data[..]);
263
264 for byte in 0..len {
265 for bit in 0..8 {
266 let mut data = data.clone();
267 data[byte] ^= 1 << bit;
268
269 let rust_hash = rapidhash_rs_seeded(&data, &DEFAULT_RAPID_SECRETS);
270 let mut c_hash = rapidhashcc_rs(&data, DEFAULT_SEED);
271 c_hash = rapid_mix_np::<false>(c_hash, DEFAULT_RAPID_SECRETS.secrets[1]);
273 assert_eq!(rust_hash, c_hash, "Mismatch with input {} byte {} bit {}", len, byte, bit);
274
275 let mut rust_hasher = SeedableState::fixed().build_hasher();
276 rust_hasher.write(&data);
277 let rust_hasher_hash = rust_hasher.finish();
278 assert_eq!(rust_hash, rust_hasher_hash, "Hasher mismatch with input {} byte {} bit {}", len, byte, bit);
279 }
280 }
281 }
282 }
283
284 #[test]
285 fn disambiguation_check() {
286 use std::vec::Vec;
287
288 let hasher = SeedableState::default();
289
290 let a = [std::vec![1], std::vec![2, 3]];
291 let b = [std::vec![1, 2], std::vec![3]];
292 assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
293
294 let a = [std::vec![], std::vec![1]];
295 let b = [std::vec![1], std::vec![]];
296 assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
297
298 let a: [Vec<Vec<u64>>; 2] = [std::vec![], std::vec![std::vec![]]];
299 let b: [Vec<Vec<u64>>; 2] = [std::vec![std::vec![]], std::vec![]];
300 assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
301
302 let a = ["abc", "def"];
303 let b = ["fed", "abc"];
304 assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
305
306 let a = ["abc", "def"];
307 let b = ["abcd", "ef"];
308 assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
309
310 let a = [1u8, 2];
311 let b = [2u8, 1];
312 assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
313
314 let a = [1u16, 2];
315 let b = [2u16, 1];
316 assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
317
318 let a = [1u32, 2];
319 let b = [2u32, 1];
320 assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
321
322 let a = [1u64, 2];
323 let b = [2u64, 1];
324 assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
325
326 let a = [1u128, 2];
327 let b = [2u128, 1];
328 assert_ne!(hasher.hash_one(a), hasher.hash_one(b));
329 }
330}