1use core::hash::Hasher;
2use super::DEFAULT_RAPID_SECRETS;
3use super::mix_np::rapid_mix_np;
4use super::rapid_const::rapidhash_core;
5use super::seed::rapidhash_seed;
6use crate::util::hints::likely;
7
8macro_rules! write_num {
14 ($write_num:ident, $int:ident, $unsigned:ident) => {
15
16 #[inline(always)]
18 fn $write_num(&mut self, i: $int) {
19 const N: u8 = core::mem::size_of::<$int>() as u8 * 8;
20 if SPONGE {
21 let bytes = (i as $unsigned) as u128;
26 if likely(self.sponge_len + N <= 128) {
27 self.sponge |= bytes << self.sponge_len;
29 self.sponge_len += N;
30 } else {
31 let a = self.sponge as u64;
33 let b = (self.sponge >> 64) as u64;
34 self.seed = rapid_mix_np::<PROTECTED>(a ^ self.seed, b ^ self.secrets[0]);
35 self.sponge = bytes;
36 self.sponge_len = N;
37 }
38 } else {
39 let bytes = (i as $unsigned) as u64;
41 self.seed = rapid_mix_np::<PROTECTED>(bytes ^ self.secrets[0], bytes ^ self.seed);
42 }
43 }
44 };
45}
46
47#[derive(Copy, Clone)]
67pub struct RapidHasher<'s, const AVALANCHE: bool, const SPONGE: bool, const COMPACT: bool = false, const PROTECTED: bool = false> {
68 seed: u64,
69 secrets: &'s [u64; 7],
70 sponge: u128,
71 sponge_len: u8,
72}
73
74impl<'s, const AVALANCHE: bool, const SPONGE: bool, const COMPACT: bool, const PROTECTED: bool> RapidHasher<'s, AVALANCHE, SPONGE, COMPACT, PROTECTED> {
75 pub const DEFAULT_SEED: u64 = super::seed::DEFAULT_SEED;
77
78 #[inline(always)]
83 #[must_use]
84 pub const fn new(mut seed: u64) -> Self {
85 seed = rapidhash_seed(seed);
87 Self::new_precomputed_seed(seed, &DEFAULT_RAPID_SECRETS.secrets)
88 }
89
90 #[inline(always)]
91 #[must_use]
92 pub(super) const fn new_precomputed_seed(seed: u64, secrets: &'s [u64; 7]) -> Self {
93 Self {
94 seed,
95 secrets,
96 sponge: 0,
97 sponge_len: 0,
98 }
99 }
100
101 #[inline(always)]
103 #[must_use]
104 pub const fn default_const() -> Self {
105 Self::new(Self::DEFAULT_SEED)
106 }
107}
108
109impl<const AVALANCHE: bool, const SPONGE: bool, const COMPACT: bool, const PROTECTED: bool> Default for RapidHasher<'_, AVALANCHE, SPONGE, COMPACT, PROTECTED> {
110 #[inline(always)]
115 fn default() -> Self {
116 Self::new(super::seed::DEFAULT_SEED)
117 }
118}
119
120impl<const AVALANCHE: bool, const SPONGE: bool, const COMPACT: bool, const PROTECTED: bool> Hasher for RapidHasher<'_, AVALANCHE, SPONGE, COMPACT, PROTECTED> {
124 #[inline(always)]
126 fn finish(&self) -> u64 {
127 #[allow(clippy::collapsible_else_if)]
129 if SPONGE {
130 if !AVALANCHE {
131 if self.sponge_len > 0 {
132 let a = self.sponge as u64;
133 let b = (self.sponge >> 64) as u64;
134 rapid_mix_np::<PROTECTED>(a ^ self.seed, b ^ self.secrets[0])
135 } else {
136 self.seed
137 }
138 } else {
139 let mut seed = self.seed;
140 if self.sponge_len > 0 {
141 let a = self.sponge as u64;
142 let b = (self.sponge >> 64) as u64;
143 seed = rapid_mix_np::<PROTECTED>(a ^ self.seed, b ^ self.secrets[0]);
144 }
145 rapid_mix_np::<PROTECTED>(seed, DEFAULT_RAPID_SECRETS.secrets[1])
148 }
149 } else {
150 if !AVALANCHE {
151 self.seed
152 } else {
153 rapid_mix_np::<PROTECTED>(self.seed, DEFAULT_RAPID_SECRETS.secrets[1])
154 }
155 }
156 }
157
158 #[inline(always)]
160 fn write(&mut self, bytes: &[u8]) {
161 self.seed = rapidhash_core::<AVALANCHE, COMPACT, PROTECTED>(self.seed, self.secrets, bytes);
162 }
163
164 write_num!(write_u8, u8, u8);
165 write_num!(write_u16, u16, u16);
166 write_num!(write_u32, u32, u32);
167 write_num!(write_u64, u64, u64);
168 write_num!(write_usize, usize, usize);
169 write_num!(write_i8, i8, u8);
170 write_num!(write_i16, i16, u16);
171 write_num!(write_i32, i32, u32);
172 write_num!(write_i64, i64, u64);
173 write_num!(write_isize, isize, usize);
174
175 #[inline(always)]
177 fn write_u128(&mut self, i: u128) {
178 let a = i as u64;
179 let b = (i >> 64) as u64;
180 self.seed = rapid_mix_np::<PROTECTED>(a ^ self.seed, b ^ self.secrets[0]);
181 }
182
183 #[inline(always)]
185 fn write_i128(&mut self, i: i128) {
186 let a = (i as u128) as u64;
187 let b = (i as u128 >> 64) as u64;
188 self.seed = rapid_mix_np::<PROTECTED>(a ^ self.seed, b ^ self.secrets[0]);
189 }
190
191 #[cfg(feature = "nightly")]
193 #[inline(always)]
194 fn write_length_prefix(&mut self, len: usize) {
195 self.write_usize(len);
196 }
197
198 #[cfg(feature = "nightly")]
201 #[inline(always)]
202 fn write_str(&mut self, s: &str) {
203 self.write(s.as_bytes());
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 extern crate std;
210
211 use std::hash::BuildHasher;
212 use crate::fast::SeedableState;
213 use super::*;
214
215 #[test]
216 fn test_hasher_size() {
217 assert_eq!(core::mem::size_of::<RapidHasher::<true, true, false, false>>(), 48);
218 }
219
220 #[ignore]
224 #[test]
225 fn test_hasher_write_u64() {
226 assert_eq!((8 & 24) >> (8 >> 3), 4);
227
228 let ints = [1234u64, 0, 1, u64::MAX, u64::MAX - 2385962040453523];
229
230 for int in ints {
231 let mut hasher = RapidHasher::<true, false>::default();
232 hasher.write(int.to_le_bytes().as_slice());
233 let a = hasher.finish();
234
235 assert_eq!(int.to_le_bytes().as_slice().len(), 8);
236
237 let mut hasher = RapidHasher::<true, false>::default();
238 hasher.write_u64(int);
239 let b = hasher.finish();
240
241 assert_eq!(a, b, "Mismatching hash for u64 with input {int}");
242 }
243 }
244
245 #[test]
247 #[ignore]
248 #[cfg(feature = "std")]
249 fn test_num_collisions() {
250 let builder = SeedableState::default();
251 let mut collisions = 0;
252 let mut set = std::collections::HashSet::new();
253 for i in 0..=u16::MAX {
254 let hash_u16 = builder.hash_one(i) & 0xFFFFFF;
255 if set.contains(&hash_u16) {
256 collisions += 1;
257 } else {
258 set.insert(hash_u16);
259 }
260
261 }
270 assert_eq!(collisions, 0, "Collisions found when hashing numbers with seed {builder:?}");
271 }
272}