1pub const RAPID_SEED: u64 = 0xbdd89aa982704029;
3pub(crate) const RAPID_SECRET: [u64; 3] = [0x2d358dccaa6c78a5, 0x8bb84b93962eacc9, 0x4b33a62ed433d4a3];
4
5#[inline]
7pub const fn rapidhash(data: &[u8]) -> u64 {
8 rapidhash_inline(data, RAPID_SEED)
9}
10
11#[inline]
13pub const fn rapidhash_seeded(data: &[u8], seed: u64) -> u64 {
14 rapidhash_inline(data, seed)
15}
16
17#[inline(always)]
22pub const fn rapidhash_inline(data: &[u8], mut seed: u64) -> u64 {
23 seed = rapidhash_seed(seed, data.len() as u64);
24 let (a, b, _) = rapidhash_core(0, 0, seed, data);
25 rapidhash_finish(a, b, data.len() as u64)
26}
27
28#[inline(always)]
29pub const fn rapid_mum(a: u64, b: u64) -> (u64, u64) {
30 let r = a as u128 * b as u128;
31 (r as u64, (r >> 64) as u64)
32}
33
34#[inline(always)]
35pub const fn rapid_mix(a: u64, b: u64) -> u64 {
36 let (a, b) = rapid_mum(a, b);
37 a ^ b
38}
39
40#[inline(always)]
41pub(crate) const fn rapidhash_seed(seed: u64, len: u64) -> u64 {
42 seed ^ rapid_mix(seed ^ RAPID_SECRET[0], RAPID_SECRET[1]) ^ len
43}
44
45#[inline(always)]
46pub(crate) const fn rapidhash_core(mut a: u64, mut b: u64, mut seed: u64, data: &[u8]) -> (u64, u64, u64) {
47 if data.len() <= 16 {
48 if data.len() >= 8 {
54 let plast = data.len() - 4;
56 let delta = 4;
57 a ^= read_u32_combined(data, 0, plast);
58 b ^= read_u32_combined(data, delta, plast - delta);
59 } else if data.len() >= 4 {
60 let plast = data.len() - 4;
61 let delta = 0;
62 a ^= read_u32_combined(data, 0, plast);
63 b ^= read_u32_combined(data, delta, plast - delta);
64 } else if data.len() > 0 {
65 let len = data.len();
67 a ^= ((data[0] as u64) << 56) | ((data[len >> 1] as u64) << 32) | data[len - 1] as u64;
68 }
70 } else {
71 let mut slice = data;
72
73 let mut see1 = seed;
75 let mut see2 = seed;
76 while slice.len() >= 96 {
77 seed = rapid_mix(read_u64(slice, 0) ^ RAPID_SECRET[0], read_u64(slice, 8) ^ seed);
78 see1 = rapid_mix(read_u64(slice, 16) ^ RAPID_SECRET[1], read_u64(slice, 24) ^ see1);
79 see2 = rapid_mix(read_u64(slice, 32) ^ RAPID_SECRET[2], read_u64(slice, 40) ^ see2);
80 seed = rapid_mix(read_u64(slice , 48) ^ RAPID_SECRET[0], read_u64(slice, 56) ^ seed);
81 see1 = rapid_mix(read_u64(slice, 64) ^ RAPID_SECRET[1], read_u64(slice, 72) ^ see1);
82 see2 = rapid_mix(read_u64(slice, 80) ^ RAPID_SECRET[2], read_u64(slice, 88) ^ see2);
83 let (_, split) = slice.split_at(96);
84 slice = split;
85 }
86 if slice.len() >= 48 {
87 seed = rapid_mix(read_u64(slice, 0) ^ RAPID_SECRET[0], read_u64(slice, 8) ^ seed);
88 see1 = rapid_mix(read_u64(slice, 16) ^ RAPID_SECRET[1], read_u64(slice, 24) ^ see1);
89 see2 = rapid_mix(read_u64(slice, 32) ^ RAPID_SECRET[2], read_u64(slice, 40) ^ see2);
90 let (_, split) = slice.split_at(48);
91 slice = split;
92 }
93 seed ^= see1 ^ see2;
94
95 if slice.len() > 16 {
96 seed = rapid_mix(read_u64(slice, 0) ^ RAPID_SECRET[2], read_u64(slice, 8) ^ seed ^ RAPID_SECRET[1]);
97 if slice.len() > 32 {
98 seed = rapid_mix(read_u64(slice, 16) ^ RAPID_SECRET[2], read_u64(slice, 24) ^ seed);
99 }
100 }
101
102 a ^= read_u64(data, data.len() - 16);
103 b ^= read_u64(data, data.len() - 8);
104 }
105
106 a ^= RAPID_SECRET[1];
107 b ^= seed;
108
109 let (a2, b2) = rapid_mum(a, b);
110 a = a2;
111 b = b2;
112 (a, b, seed)
113}
114
115#[inline(always)]
116pub(crate) const fn rapidhash_finish(a: u64, b: u64, len: u64) -> u64 {
117 rapid_mix(a ^ RAPID_SECRET[0] ^ len, b ^ RAPID_SECRET[1])
118}
119
120#[cfg(not(feature = "unsafe"))]
123#[inline(always)]
124pub(crate) const fn read_u64(slice: &[u8], offset: usize) -> u64 {
125 let maybe_buf = slice.split_at(offset).1.first_chunk::<8>();
127 let buf = match maybe_buf {
128 Some(buf) => *buf,
129 None => panic!("read_u64: slice too short"),
130 };
131 u64::from_le_bytes(buf)
132}
133
134#[cfg(not(feature = "unsafe"))]
137#[inline(always)]
138const fn read_u32(slice: &[u8], offset: usize) -> u32 {
139 let maybe_buf = slice.split_at(offset).1.first_chunk::<4>();
141 let buf = match maybe_buf {
142 Some(buf) => *buf,
143 None => panic!("read_u32: slice too short"),
144 };
145 u32::from_le_bytes(buf)
146}
147
148#[cfg(feature = "unsafe")]
154#[inline(always)]
155pub(crate) const fn read_u64(slice: &[u8], offset: usize) -> u64 {
156 debug_assert!(offset as isize >= 0);
157 debug_assert!(slice.len() >= 8 + offset);
158 let val = unsafe { core::ptr::read_unaligned(slice.as_ptr().offset(offset as isize) as *const u64) };
159 val.to_le() }
161
162#[cfg(feature = "unsafe")]
168#[inline(always)]
169const fn read_u32(slice: &[u8], offset: usize) -> u32 {
170 debug_assert!(offset as isize >= 0);
171 debug_assert!(slice.len() >= 4 + offset);
172 let val = unsafe { core::ptr::read_unaligned(slice.as_ptr().offset(offset as isize) as *const u32) };
173 val.to_le() }
175
176#[inline(always)]
177pub(crate) const fn read_u32_combined(slice: &[u8], offset_top: usize, offset_bot: usize) -> u64 {
178 debug_assert!(slice.len() >= 4 + offset_top && slice.len() >= 4 + offset_bot);
179 let top = read_u32(slice, offset_top) as u64;
180 let bot = read_u32(slice, offset_bot) as u64;
181 (top << 32) | bot
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187
188 #[test]
189 fn test_read_u32() {
190 let bytes = &[23, 145, 3, 34];
191
192 let split_result = bytes.split_at(0).1;
193 assert_eq!(split_result.len(), 4);
194 let maybe_buf = split_result.first_chunk::<4>();
195 assert_eq!(maybe_buf, Some(&[23, 145, 3, 34]));
196
197 assert_eq!(read_u32(bytes, 0), 570659095);
198
199 let bytes = &[24, 54, 3, 23, 145, 3, 34];
200 assert_eq!(read_u32(bytes, 3), 570659095);
201
202 assert_eq!(read_u32(&[0, 0, 0, 0], 0), 0);
203 assert_eq!(read_u32(&[1, 0, 0, 0], 0), 1);
204 assert_eq!(read_u32(&[12, 0, 0, 0], 0), 12);
205 assert_eq!(read_u32(&[0, 10, 0, 0], 0), 2560);
206 }
207
208 #[test]
209 fn test_read_u64() {
210 let bytes = [23, 145, 3, 34, 0, 0, 0, 0, 0, 0, 0].as_slice();
211 assert_eq!(read_u64(bytes, 0), 570659095);
212
213 let bytes = [1, 2, 3, 23, 145, 3, 34, 0, 0, 0, 0, 0, 0, 0].as_slice();
214 assert_eq!(read_u64(bytes, 3), 570659095);
215
216 let bytes = [0, 0, 0, 0, 0, 0, 0, 0].as_slice();
217 assert_eq!(read_u64(bytes, 0), 0);
218 }
219
220 #[cfg(feature = "std")]
221 #[test]
222 fn test_u32_to_u128_delta() {
223 fn formula(len: u64) -> u64 {
224 (len & 24) >> (len >> 3)
225 }
226
227 fn formula2(len: u64) -> u64 {
228 match len {
229 8.. => 4,
230 _ => 0,
231 }
232 }
233
234 let inputs: std::vec::Vec<u64> = (4..=16).collect();
235 let outputs: std::vec::Vec<u64> = inputs.iter().map(|&x| formula(x)).collect();
236 let expected = std::vec![0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4];
237 assert_eq!(outputs, expected);
238 assert_eq!(outputs, inputs.iter().map(|&x| formula2(x)).collect::<Vec<u64>>());
239 }
240
241 #[test]
242 #[should_panic]
243 #[cfg(any(test, not(feature = "unsafe")))]
244 fn test_read_u32_to_short_panics() {
245 let bytes = [23, 145, 0].as_slice();
246 assert_eq!(read_u32(bytes, 0), 0);
247 }
248
249 #[test]
250 #[should_panic]
251 #[cfg(any(test, not(feature = "unsafe")))]
252 fn test_read_u64_to_short_panics() {
253 let bytes = [23, 145, 0].as_slice();
254 assert_eq!(read_u64(bytes, 0), 0);
255 }
256
257 #[test]
258 fn test_rapid_mum() {
259 let (a, b) = rapid_mum(0, 0);
260 assert_eq!(a, 0);
261 assert_eq!(b, 0);
262
263 let (a, b) = rapid_mum(100, 100);
264 assert_eq!(a, 10000);
265 assert_eq!(b, 0);
266
267 let (a, b) = rapid_mum(u64::MAX, 2);
268 assert_eq!(a, u64::MAX - 1);
269 assert_eq!(b, 1);
270 }
271}