1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(feature = "std"), no_std)]
3
4#![cfg_attr(docsrs, feature(doc_auto_cfg))]
5#![cfg_attr(docsrs, feature(doc_cfg_hide))]
6#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))]
7
8#[deny(missing_docs)]
9#[deny(unused_must_use)]
10
11mod rapid_const;
12mod rapid_hasher;
13mod rapid_hasher_inline;
14#[cfg(any(feature = "std", docsrs))]
15mod rapid_file;
16#[cfg(any(feature = "std", feature = "rand", docsrs))]
17mod random_state;
18mod rng;
19
20#[doc(inline)]
21pub use crate::rapid_const::{rapidhash, rapidhash_inline, rapidhash_seeded, RAPID_SEED};
22#[doc(inline)]
23pub use crate::rapid_hasher::*;
24#[doc(inline)]
25pub use crate::rapid_hasher_inline::*;
26#[doc(inline)]
27#[cfg(any(feature = "std", docsrs))]
28pub use crate::rapid_file::*;
29#[doc(inline)]
30#[cfg(any(feature = "std", feature = "rand", docsrs))]
31pub use crate::random_state::*;
32#[doc(inline)]
33pub use crate::rng::*;
34
35
36#[cfg(test)]
37mod tests {
38 extern crate std;
39
40 use std::hash::{Hash, Hasher};
41 use std::collections::BTreeSet;
42 use rand::Rng;
43 use super::*;
44
45 #[derive(Hash)]
46 struct Object {
47 bytes: std::vec::Vec<u8>,
48 }
49
50 #[test]
52 fn hasher_equivalent_to_oneshot() {
53 let hash = rapidhash(b"hello world");
54 assert_ne!(hash, 0);
55 assert_eq!(hash, 17498481775468162579);
56
57 let mut hasher = RapidHasher::default();
58 hasher.write(b"hello world");
59 assert_eq!(hasher.finish(), 17498481775468162579);
60
61 let hash = rapidhash(b"hello world!");
62 assert_eq!(hash, 12238759925102402976);
63 }
64
65 #[test]
67 fn derive_hash_works() {
68 let object = Object { bytes: b"hello world".to_vec() };
69 let mut hasher = RapidHasher::default();
70 object.hash(&mut hasher);
71 assert_eq!(hasher.finish(), 3415994554582211120);
72
73 let mut hasher = RapidHasher::default();
74 hasher.write_usize(b"hello world".len());
75 hasher.write(b"hello world");
76 assert_eq!(hasher.finish(), 3415994554582211120);
77 }
78
79 #[test]
83 fn all_sizes() {
84 let mut hashes = BTreeSet::new();
85
86 for size in 0..=1024 {
87 let mut data = std::vec![0; size];
88 rand::rng().fill(data.as_mut_slice());
89
90 let hash1 = rapidhash(&data);
91 let mut hasher = RapidHasher::default();
92 hasher.write(&data);
93 let hash2 = hasher.finish();
94
95 assert_eq!(hash1, hash2, "Failed on size {}", size);
96 assert!(!hashes.contains(&hash1), "Duplicate for size {}", size);
97
98 hashes.insert(hash1);
99 }
100 }
101
102 #[test]
104 fn hashes_to_expected_values() {
105 assert_eq!( rapidhash(0u128.to_le_bytes().as_slice()), 8755926293314635566);
106 assert_eq!( rapidhash(1u128.to_le_bytes().as_slice()), 17996969877019643443);
107 assert_eq!( rapidhash(0x1000u128.to_le_bytes().as_slice()), 3752997491443908878);
108 assert_eq!( rapidhash(0x1000_0000u128.to_le_bytes().as_slice()), 1347028408682550078);
109 assert_eq!( rapidhash(0x10000000_00000000u128.to_le_bytes().as_slice()), 3593052489046108800);
110 assert_eq!( rapidhash(0x10000000_00000001u128.to_le_bytes().as_slice()), 7365235785575411947);
111 assert_eq!( rapidhash(0x10000000_00000000_00000000u128.to_le_bytes().as_slice()), 5399386355486589714);
112 assert_eq!(rapidhash(0x10000000_00000000_00000000_00000000u128.to_le_bytes().as_slice()), 13365378750111633005);
113 assert_eq!(rapidhash(0xffffffff_ffffffff_ffffffff_ffffffffu128.to_le_bytes().as_slice()), 10466158564987642889);
114 }
115
116 #[test]
121 fn flip_bit_trial() {
122 use rand::Rng;
123
124 let mut flips = std::vec![];
125
126 for len in 1..=256 {
127 let mut data = std::vec![0; len];
128 rand::rng().fill(&mut data[..]);
129
130 let hash = rapidhash(&data);
131 for byte in 0..len {
132 for bit in 0..8 {
133 let mut data = data.clone();
134 data[byte] ^= 1 << bit;
135 let new_hash = rapidhash(&data);
136 assert_ne!(hash, new_hash, "Flipping bit {} did not change hash", byte);
137 let xor = hash ^ new_hash;
138 let flipped = xor.count_ones() as u64;
139 assert!(xor.count_ones() >= 10, "Flipping bit {byte}:{bit} changed only {flipped} bits");
140
141 flips.push(flipped);
142 }
143 }
144 }
145
146 let average = flips.iter().sum::<u64>() as f64 / flips.len() as f64;
147 assert!(average > 31.95 && average < 32.05, "Did not flip an average of half the bits. average: {average}, expected: 32.0");
148 }
149
150 fn streaming_hash(data: &[u8]) -> u64 {
152 let mut hasher = RapidHasher::default();
153 for byte in data {
154 hasher.write_u8(*byte);
155 }
156 hasher.finish()
157 }
158
159 #[test]
166 fn flip_bit_trial_streaming() {
167 use rand::Rng;
168
169 let mut flips = std::vec![];
170
171 for len in 1..=256 {
172 let mut data = std::vec![0; len];
173 rand::rng().fill(&mut data[..]);
174
175 let hash = streaming_hash(&data);
176 for byte in 0..len {
177 for bit in 0..8 {
178 let mut data = data.clone();
179 data[byte] ^= 1 << bit;
180
181 let new_hash = streaming_hash(&data);
183 assert_ne!(hash, new_hash, "Flipping bit {} did not change hash", byte);
184
185 let xor = hash ^ new_hash;
187 let flipped = xor.count_ones() as u64;
188 assert!(xor.count_ones() >= 10, "Flipping bit {byte}:{bit} changed only {flipped} bits");
189 flips.push(flipped);
190 }
191 }
192 }
193
194 let average = flips.iter().sum::<u64>() as f64 / flips.len() as f64;
196 assert!(average > 31.95 && average < 32.05, "Did not flip an average of half the bits. average: {average}, expected: 32.0");
197 }
198}