/rust/registry/src/index.crates.io-1949cf8c6b5b557f/xxhash-rust-0.8.17/src/xxh3.rs
Line | Count | Source |
1 | | //!XXH3 implementation |
2 | | //! |
3 | | //!Provides `Hasher` only for 64bit as 128bit variant would not be much different due to trait |
4 | | //!being limited to `u64` outputs. |
5 | | |
6 | | use core::{ptr, mem, slice, hash}; |
7 | | |
8 | | use crate::xxh32_common as xxh32; |
9 | | use crate::xxh64_common as xxh64; |
10 | | use crate::xxh3_common::*; |
11 | | use crate::utils::{Buffer, get_unaligned_chunk, get_aligned_chunk_ref}; |
12 | | |
13 | | // Code is as close to original C implementation as possible |
14 | | // It does make it look ugly, but it is fast and easy to update once xxhash gets new version. |
15 | | |
16 | | #[cfg(all(any(target_feature = "sse2", target_feature = "neon", all(target_family = "wasm", target_feature = "simd128")), not(any(target_feature = "avx2", target_feature = "avx512f"))))] |
17 | | #[repr(align(16))] |
18 | | #[derive(Clone)] |
19 | | struct Acc([u64; ACC_NB]); |
20 | | #[cfg(all(target_feature = "avx2", not(target_feature = "avx512f")))] |
21 | | #[repr(align(32))] |
22 | | #[derive(Clone)] |
23 | | struct Acc([u64; ACC_NB]); |
24 | | #[cfg(target_feature = "avx512f")] |
25 | | #[repr(align(64))] |
26 | | #[derive(Clone)] |
27 | | struct Acc([u64; ACC_NB]); |
28 | | #[cfg(not(any(target_feature = "avx512f", target_feature = "avx2", target_feature = "neon", all(target_family = "wasm", target_feature = "simd128"), target_feature = "sse2")))] |
29 | | #[repr(align(8))] |
30 | | #[derive(Clone)] |
31 | | struct Acc([u64; ACC_NB]); |
32 | | |
33 | | const INITIAL_ACC: Acc = Acc([ |
34 | | xxh32::PRIME_3 as u64, xxh64::PRIME_1, xxh64::PRIME_2, xxh64::PRIME_3, |
35 | | xxh64::PRIME_4, xxh32::PRIME_2 as u64, xxh64::PRIME_5, xxh32::PRIME_1 as u64 |
36 | | ]); |
37 | | |
38 | | type LongHashFn = fn(&[u8], u64, &[u8]) -> u64; |
39 | | type LongHashFn128 = fn(&[u8], u64, &[u8]) -> u128; |
40 | | |
41 | | #[cfg(all(target_family = "wasm", target_feature = "simd128"))] |
42 | | type StripeLanes = [[u8; mem::size_of::<core::arch::wasm32::v128>()]; STRIPE_LEN / mem::size_of::<core::arch::wasm32::v128>()]; |
43 | | #[cfg(all(target_arch = "x86", target_feature = "avx512f"))] |
44 | | type StripeLanes = [[u8; mem::size_of::<core::arch::x86::__m512i>()]; STRIPE_LEN / mem::size_of::<core::arch::x86::__m512i>()]; |
45 | | #[cfg(all(target_arch = "x86_64", target_feature = "avx512f"))] |
46 | | type StripeLanes = [[u8; mem::size_of::<core::arch::x86_64::__m512i>()]; STRIPE_LEN / mem::size_of::<core::arch::x86_64::__m512i>()]; |
47 | | #[cfg(all(target_arch = "x86", target_feature = "avx2", not(target_feature = "avx512f")))] |
48 | | type StripeLanes = [[u8; mem::size_of::<core::arch::x86::__m256i>()]; STRIPE_LEN / mem::size_of::<core::arch::x86::__m256i>()]; |
49 | | #[cfg(all(target_arch = "x86_64", target_feature = "avx2", not(target_feature = "avx512f")))] |
50 | | type StripeLanes = [[u8; mem::size_of::<core::arch::x86_64::__m256i>()]; STRIPE_LEN / mem::size_of::<core::arch::x86_64::__m256i>()]; |
51 | | #[cfg(all(target_arch = "x86", target_feature = "sse2", not(any(target_feature = "avx2", target_feature = "avx512f"))))] |
52 | | type StripeLanes = [[u8; mem::size_of::<core::arch::x86::__m128i>()]; STRIPE_LEN / mem::size_of::<core::arch::x86::__m128i>()]; |
53 | | #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(any(target_feature = "avx2", target_feature = "avx512f"))))] |
54 | | type StripeLanes = [[u8; mem::size_of::<core::arch::x86_64::__m128i>()]; STRIPE_LEN / mem::size_of::<core::arch::x86_64::__m128i>()]; |
55 | | #[cfg(target_feature = "neon")] |
56 | | type StripeLanes = [[u8; mem::size_of::<core::arch::aarch64::uint8x16_t>()]; STRIPE_LEN / mem::size_of::<core::arch::aarch64::uint8x16_t>()]; |
57 | | |
58 | | ///Secret validation wrapper |
59 | | pub struct SecretInput<T>(T); |
60 | | |
61 | | impl<T: AsRef<[u8]>> SecretInput<T> { |
62 | | #[inline(always)] |
63 | | ///Creates secret input validating its length is sufficient |
64 | 0 | pub fn try_new(input: T) -> Option<Self> { |
65 | 0 | if input.as_ref().len() >= SECRET_SIZE_MIN { |
66 | 0 | Some(Self(input)) |
67 | | } else { |
68 | 0 | None |
69 | | } |
70 | 0 | } |
71 | | } |
72 | | |
73 | | impl<const N: usize> SecretInput<[u8; N]> { |
74 | | ///Creates secret input from static array, suitable to be used at compile time. |
75 | | /// |
76 | | ///On insufficient length it shall panic. |
77 | | /// |
78 | | ///Prefer to generate secret at compile time using [const_custom_default_secret](../const_xxh3/fn.const_custom_default_secret.html) |
79 | 0 | pub const fn new(input: [u8; N]) -> Self { |
80 | 0 | assert!(N >= SECRET_SIZE_MIN, "input length must be equal or greater than SECRET_SIZE_MIN=136"); |
81 | | |
82 | 0 | Self(input) |
83 | 0 | } |
84 | | } |
85 | | |
86 | | // TODO: replace with [`core::arch::x86::_MM_SHUFFLE`](https://doc.rust-lang.org/core/arch/x86/fn._MM_SHUFFLE.html) |
87 | | // when it stabilizes |
88 | | #[cfg(any(target_feature = "sse2", target_feature = "avx2", target_feature = "avx512f"))] |
89 | | #[inline] |
90 | 0 | const fn _mm_shuffle(z: u32, y: u32, x: u32, w: u32) -> i32 { |
91 | 0 | ((z << 6) | (y << 4) | (x << 2) | w) as i32 |
92 | 0 | } |
93 | | |
94 | | #[inline(always)] |
95 | 0 | const fn mult32_to64(left: u32, right: u32) -> u64 { |
96 | 0 | (left as u64).wrapping_mul(right as u64) |
97 | 0 | } |
98 | | |
99 | | //#[inline(always)] |
100 | | //fn _mm_prefetch(_ptr: *const i8, _offset: isize) { |
101 | | // #[cfg(target_arch = "x86")] |
102 | | // unsafe { |
103 | | // core::arch::x86::_mm_prefetch(_ptr.offset(_offset), core::arch::x86::_MM_HINT_T0); |
104 | | // } |
105 | | // |
106 | | // #[cfg(target_arch = "x86_64")] |
107 | | // unsafe { |
108 | | // core::arch::x86_64::_mm_prefetch(_ptr.offset(_offset), core::arch::x86_64::_MM_HINT_T0); |
109 | | // } |
110 | | //} |
111 | | |
112 | | macro_rules! to_u128 { |
113 | | ($lo:expr, $hi:expr) => { |
114 | | ($lo) as u128 | ((($hi) as u128) << 64) |
115 | | }; |
116 | | } |
117 | | |
118 | | macro_rules! slice_offset_ptr { |
119 | | ($slice:expr, $offset:expr) => {{ |
120 | | let slice = $slice; |
121 | | let offset = $offset; |
122 | | debug_assert!(slice.len() >= offset); |
123 | | |
124 | | #[allow(unused_unsafe)] |
125 | | unsafe { |
126 | | (slice.as_ptr() as *const u8).add(offset) |
127 | | } |
128 | | }} |
129 | | } |
130 | | |
131 | | #[inline(always)] |
132 | 0 | fn read_32le_unaligned(data: &[u8], offset: usize) -> u32 { |
133 | 0 | u32::from_ne_bytes(*get_aligned_chunk_ref(data, offset)).to_le() |
134 | 0 | } |
135 | | |
136 | | #[inline(always)] |
137 | 0 | fn read_64le_unaligned(data: &[u8], offset: usize) -> u64 { |
138 | 0 | u64::from_ne_bytes(*get_aligned_chunk_ref(data, offset)).to_le() |
139 | 0 | } |
140 | | |
141 | | #[inline(always)] |
142 | 0 | fn mix_two_accs(acc: &mut Acc, offset: usize, secret: &[[u8; 8]; 2]) -> u64 { |
143 | 0 | mul128_fold64(acc.0[offset] ^ u64::from_ne_bytes(secret[0]).to_le(), |
144 | 0 | acc.0[offset + 1] ^ u64::from_ne_bytes(secret[1]).to_le()) |
145 | 0 | } |
146 | | |
147 | | #[inline] |
148 | 0 | fn merge_accs(acc: &mut Acc, secret: &[[[u8; 8]; 2]; 4], mut result: u64) -> u64 { |
149 | | macro_rules! mix_two_accs { |
150 | | ($idx:literal) => { |
151 | | result = result.wrapping_add(mix_two_accs(acc, $idx * 2, &secret[$idx])) |
152 | | } |
153 | | } |
154 | | |
155 | 0 | mix_two_accs!(0); |
156 | 0 | mix_two_accs!(1); |
157 | 0 | mix_two_accs!(2); |
158 | 0 | mix_two_accs!(3); |
159 | | |
160 | 0 | avalanche(result) |
161 | 0 | } |
162 | | |
163 | | #[inline(always)] |
164 | 0 | fn mix16_b(input: &[[u8; 8]; 2], secret: &[[u8; 8]; 2], seed: u64) -> u64 { |
165 | 0 | let mut input_lo = u64::from_ne_bytes(input[0]).to_le(); |
166 | 0 | let mut input_hi = u64::from_ne_bytes(input[1]).to_le(); |
167 | | |
168 | 0 | input_lo ^= u64::from_ne_bytes(secret[0]).to_le().wrapping_add(seed); |
169 | 0 | input_hi ^= u64::from_ne_bytes(secret[1]).to_le().wrapping_sub(seed); |
170 | | |
171 | 0 | mul128_fold64(input_lo, input_hi) |
172 | 0 | } |
173 | | |
174 | | #[inline(always)] |
175 | | //Inputs are two chunks of unaligned u64 |
176 | | //Secret are two chunks of unaligned (u64, u64) |
177 | 0 | fn mix32_b(lo: &mut u64, hi: &mut u64, input_1: &[[u8; 8]; 2], input_2: &[[u8; 8]; 2], secret: &[[[u8; 8]; 2]; 2], seed: u64) { |
178 | 0 | *lo = lo.wrapping_add(mix16_b(input_1, &secret[0], seed)); |
179 | 0 | *lo ^= u64::from_ne_bytes(input_2[0]).to_le().wrapping_add(u64::from_ne_bytes(input_2[1]).to_le()); |
180 | | |
181 | 0 | *hi = hi.wrapping_add(mix16_b(input_2, &secret[1], seed)); |
182 | 0 | *hi ^= u64::from_ne_bytes(input_1[0]).to_le().wrapping_add(u64::from_ne_bytes(input_1[1]).to_le()); |
183 | 0 | } |
184 | | |
185 | | #[inline(always)] |
186 | 0 | fn custom_default_secret(seed: u64) -> [u8; DEFAULT_SECRET_SIZE] { |
187 | 0 | let mut result = mem::MaybeUninit::<[u8; DEFAULT_SECRET_SIZE]>::uninit(); |
188 | | |
189 | 0 | let nb_rounds = DEFAULT_SECRET_SIZE / 16; |
190 | | |
191 | 0 | for idx in 0..nb_rounds { |
192 | 0 | let low = get_unaligned_chunk::<u64>(&DEFAULT_SECRET, idx * 16).to_le().wrapping_add(seed); |
193 | 0 | let hi = get_unaligned_chunk::<u64>(&DEFAULT_SECRET, idx * 16 + 8).to_le().wrapping_sub(seed); |
194 | 0 |
|
195 | 0 | Buffer { |
196 | 0 | ptr: result.as_mut_ptr() as *mut u8, |
197 | 0 | len: DEFAULT_SECRET_SIZE, |
198 | 0 | offset: idx * 16, |
199 | 0 | }.copy_from_slice(&low.to_le_bytes()); |
200 | 0 | Buffer { |
201 | 0 | ptr: result.as_mut_ptr() as *mut u8, |
202 | 0 | len: DEFAULT_SECRET_SIZE, |
203 | 0 | offset: idx * 16 + 8, |
204 | 0 | }.copy_from_slice(&hi.to_le_bytes()); |
205 | 0 | } |
206 | | |
207 | | unsafe { |
208 | 0 | result.assume_init() |
209 | | } |
210 | 0 | } |
211 | | |
212 | | #[cfg(all(target_family = "wasm", target_feature = "simd128"))] |
213 | | fn accumulate_512_wasm(acc: &mut Acc, input: &StripeLanes, secret: &StripeLanes) { |
214 | | const LANES: usize = ACC_NB; |
215 | | |
216 | | use core::arch::wasm32::*; |
217 | | |
218 | | let mut idx = 0usize; |
219 | | let xacc = acc.0.as_mut_ptr() as *mut v128; |
220 | | |
221 | | unsafe { |
222 | | while idx.wrapping_add(1) < LANES / 2 { |
223 | | let data_vec_1 = v128_load(input[idx].as_ptr() as _); |
224 | | let data_vec_2 = v128_load(input[idx.wrapping_add(1)].as_ptr() as _); |
225 | | |
226 | | let key_vec_1 = v128_load(secret[idx].as_ptr() as _); |
227 | | let key_vec_2 = v128_load(secret[idx.wrapping_add(1)].as_ptr() as _); |
228 | | |
229 | | let data_key_1 = v128_xor(data_vec_1, key_vec_1); |
230 | | let data_key_2 = v128_xor(data_vec_2, key_vec_2); |
231 | | |
232 | | let data_swap_1 = i64x2_shuffle::<1, 0>(data_vec_1, data_vec_1); |
233 | | let data_swap_2 = i64x2_shuffle::<1, 0>(data_vec_2, data_vec_2); |
234 | | |
235 | | let mixed_lo = i32x4_shuffle::<0, 2, 4, 6>(data_key_1, data_key_2); |
236 | | let mixed_hi = i32x4_shuffle::<1, 3, 5, 7>(data_key_1, data_key_2); |
237 | | |
238 | | let prod_1 = u64x2_extmul_low_u32x4(mixed_lo, mixed_hi); |
239 | | let prod_2 = u64x2_extmul_high_u32x4(mixed_lo, mixed_hi); |
240 | | |
241 | | let sum_1 = i64x2_add(prod_1, data_swap_1); |
242 | | let sum_2 = i64x2_add(prod_2, data_swap_2); |
243 | | |
244 | | xacc.add(idx).write(i64x2_add(sum_1, *xacc.add(idx))); |
245 | | xacc.add(idx.wrapping_add(1)).write(i64x2_add(sum_2, *xacc.add(idx.wrapping_add(1)))); |
246 | | |
247 | | idx = idx.wrapping_add(2); |
248 | | } |
249 | | } |
250 | | } |
251 | | |
252 | | #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] |
253 | | macro_rules! vld1q_u8 { |
254 | | ($ptr:expr) => { |
255 | | core::arch::aarch64::vld1q_u8($ptr) |
256 | | |
257 | | } |
258 | | } |
259 | | |
260 | | //For some dumb reasons vld1q_u8 is unstable for arm |
261 | | #[cfg(all(target_arch = "arm", target_feature = "neon"))] |
262 | | macro_rules! vld1q_u8 { |
263 | | ($ptr:expr) => { |
264 | | core::ptr::read_unaligned($ptr as *const core::arch::arm::uint8x16_t) |
265 | | } |
266 | | } |
267 | | |
268 | | #[cfg(target_feature = "neon")] |
269 | | fn accumulate_512_neon(acc: &mut Acc, input: &StripeLanes, secret: &StripeLanes) { |
270 | | //Full Neon version from xxhash source |
271 | | const NEON_LANES: usize = ACC_NB; |
272 | | |
273 | | unsafe { |
274 | | #[cfg(target_arch = "arm")] |
275 | | use core::arch::arm::*; |
276 | | #[cfg(target_arch = "aarch64")] |
277 | | use core::arch::aarch64::*; |
278 | | |
279 | | let mut idx = 0usize; |
280 | | let xacc = acc.0.as_mut_ptr() as *mut uint64x2_t; |
281 | | |
282 | | while idx.wrapping_add(1) < NEON_LANES / 2 { |
283 | | /* data_vec = xinput[i]; */ |
284 | | let data_vec_1 = vreinterpretq_u64_u8(vld1q_u8!(input[idx].as_ptr())); |
285 | | let data_vec_2 = vreinterpretq_u64_u8(vld1q_u8!(input[idx.wrapping_add(1)].as_ptr())); |
286 | | /* key_vec = xsecret[i]; */ |
287 | | let key_vec_1 = vreinterpretq_u64_u8(vld1q_u8!(secret[idx].as_ptr())); |
288 | | let key_vec_2 = vreinterpretq_u64_u8(vld1q_u8!(secret[idx.wrapping_add(1)].as_ptr())); |
289 | | /* data_swap = swap(data_vec) */ |
290 | | let data_swap_1 = vextq_u64(data_vec_1, data_vec_1, 1); |
291 | | let data_swap_2 = vextq_u64(data_vec_2, data_vec_2, 1); |
292 | | /* data_key = data_vec ^ key_vec; */ |
293 | | let data_key_1 = veorq_u64(data_vec_1, key_vec_1); |
294 | | let data_key_2 = veorq_u64(data_vec_2, key_vec_2); |
295 | | |
296 | | let unzipped = vuzpq_u32( |
297 | | vreinterpretq_u32_u64(data_key_1), |
298 | | vreinterpretq_u32_u64(data_key_2) |
299 | | ); |
300 | | /* data_key_lo = data_key & 0xFFFFFFFF */ |
301 | | let data_key_lo = unzipped.0; |
302 | | /* data_key_hi = data_key >> 32 */ |
303 | | let data_key_hi = unzipped.1; |
304 | | |
305 | | //xxhash does it with inline assembly, but idk if I want to embed it here |
306 | | let sum_1 = vmlal_u32(data_swap_1, vget_low_u32(data_key_lo), vget_low_u32(data_key_hi)); |
307 | | #[cfg(target_arch = "aarch64")] |
308 | | let sum_2 = vmlal_high_u32(data_swap_2, data_key_lo, data_key_hi); |
309 | | #[cfg(target_arch = "arm")] |
310 | | let sum_2 = vmlal_u32(data_swap_2, vget_high_u32(data_key_lo), vget_high_u32(data_key_hi)); |
311 | | |
312 | | xacc.add(idx).write(vaddq_u64(*xacc.add(idx), sum_1)); |
313 | | xacc.add(idx.wrapping_add(1)).write(vaddq_u64(*xacc.add(idx.wrapping_add(1)), sum_2)); |
314 | | |
315 | | idx = idx.wrapping_add(2); |
316 | | } |
317 | | } |
318 | | } |
319 | | |
320 | | #[cfg(all(target_feature = "sse2", not(any(target_feature = "avx2", target_feature = "avx512f"))))] |
321 | 0 | fn accumulate_512_sse2(acc: &mut Acc, input: &StripeLanes, secret: &StripeLanes) { |
322 | | unsafe { |
323 | | #[cfg(target_arch = "x86")] |
324 | | use core::arch::x86::*; |
325 | | #[cfg(target_arch = "x86_64")] |
326 | | use core::arch::x86_64::*; |
327 | | |
328 | 0 | let xacc = acc.0.as_mut_ptr() as *mut __m128i; |
329 | | |
330 | 0 | for idx in 0..secret.len() { |
331 | 0 | let data_vec = _mm_loadu_si128(input[idx].as_ptr() as _); |
332 | 0 | let key_vec = _mm_loadu_si128(secret[idx].as_ptr() as _); |
333 | 0 | let data_key = _mm_xor_si128(data_vec, key_vec); |
334 | 0 |
|
335 | 0 | let data_key_lo = _mm_shuffle_epi32(data_key, _mm_shuffle(0, 3, 0, 1)); |
336 | 0 | let product = _mm_mul_epu32(data_key, data_key_lo); |
337 | 0 |
|
338 | 0 | let data_swap = _mm_shuffle_epi32(data_vec, _mm_shuffle(1,0,3,2)); |
339 | 0 | let sum = _mm_add_epi64(*xacc.add(idx), data_swap); |
340 | 0 | xacc.add(idx).write(_mm_add_epi64(product, sum)); |
341 | 0 | } |
342 | | } |
343 | 0 | } |
344 | | |
345 | | #[cfg(all(target_feature = "avx2", not(target_feature = "avx512f")))] |
346 | | fn accumulate_512_avx2(acc: &mut Acc, input: &StripeLanes, secret: &StripeLanes) { |
347 | | unsafe { |
348 | | #[cfg(target_arch = "x86")] |
349 | | use core::arch::x86::*; |
350 | | #[cfg(target_arch = "x86_64")] |
351 | | use core::arch::x86_64::*; |
352 | | |
353 | | let xacc = acc.0.as_mut_ptr() as *mut __m256i; |
354 | | |
355 | | for idx in 0..secret.len() { |
356 | | let data_vec = _mm256_loadu_si256(input[idx].as_ptr() as _); |
357 | | let key_vec = _mm256_loadu_si256(secret[idx].as_ptr() as _); |
358 | | let data_key = _mm256_xor_si256(data_vec, key_vec); |
359 | | |
360 | | let data_key_lo = _mm256_srli_epi64(data_key, 32); |
361 | | let product = _mm256_mul_epu32(data_key, data_key_lo); |
362 | | |
363 | | let data_swap = _mm256_shuffle_epi32(data_vec, _mm_shuffle(1,0,3,2)); |
364 | | let sum = _mm256_add_epi64(*xacc.add(idx), data_swap); |
365 | | xacc.add(idx).write(_mm256_add_epi64(product, sum)); |
366 | | } |
367 | | } |
368 | | } |
369 | | |
370 | | #[cfg(target_feature = "avx512f")] |
371 | | fn accumulate_512_avx512(acc: &mut Acc, input: &StripeLanes, secret: &StripeLanes) { |
372 | | unsafe { |
373 | | #[cfg(target_arch = "x86")] |
374 | | use core::arch::x86::*; |
375 | | #[cfg(target_arch = "x86_64")] |
376 | | use core::arch::x86_64::*; |
377 | | |
378 | | let xacc = acc.0.as_mut_ptr() as *mut __m512i; |
379 | | |
380 | | let idx = 0; |
381 | | |
382 | | let data_vec = _mm512_loadu_si512(input[idx].as_ptr() as _); |
383 | | let key_vec = _mm512_loadu_si512(secret[idx].as_ptr() as _); |
384 | | let data_key = _mm512_xor_si512(data_vec, key_vec); |
385 | | |
386 | | let data_key_lo = _mm512_srli_epi64(data_key, 32); |
387 | | let product = _mm512_mul_epu32(data_key, data_key_lo); |
388 | | |
389 | | let data_swap = _mm512_shuffle_epi32(data_vec, _mm_shuffle(1, 0, 3, 2)); |
390 | | let sum = _mm512_add_epi64(*xacc.add(idx), data_swap); |
391 | | xacc.add(idx).write(_mm512_add_epi64(product, sum)); |
392 | | } |
393 | | } |
394 | | |
395 | | #[cfg(not(any(target_feature = "avx512f", target_feature = "avx2", target_feature = "sse2", target_feature = "neon", all(target_family = "wasm", target_feature = "simd128"))))] |
396 | | fn accumulate_512_scalar(acc: &mut Acc, input: &[[u8; 8]; ACC_NB], secret: &[[u8; 8]; ACC_NB]) { |
397 | | for idx in 0..ACC_NB { |
398 | | let data_val = u64::from_ne_bytes(input[idx]).to_le(); |
399 | | let data_key = data_val ^ u64::from_ne_bytes(secret[idx]).to_le(); |
400 | | |
401 | | acc.0[idx ^ 1] = acc.0[idx ^ 1].wrapping_add(data_val); |
402 | | acc.0[idx] = acc.0[idx].wrapping_add(mult32_to64((data_key & 0xFFFFFFFF) as u32, (data_key >> 32) as u32)); |
403 | | } |
404 | | } |
405 | | |
406 | | #[cfg(all(target_family = "wasm", target_feature = "simd128"))] |
407 | | use accumulate_512_wasm as accumulate_512; |
408 | | #[cfg(target_feature = "neon")] |
409 | | use accumulate_512_neon as accumulate_512; |
410 | | #[cfg(all(target_feature = "sse2", not(any(target_feature = "avx2", target_feature = "avx512f"))))] |
411 | | use accumulate_512_sse2 as accumulate_512; |
412 | | #[cfg(all(target_feature = "avx2", not(target_feature = "avx512f")))] |
413 | | use accumulate_512_avx2 as accumulate_512; |
414 | | #[cfg(target_feature = "avx512f")] |
415 | | use accumulate_512_avx512 as accumulate_512; |
416 | | #[cfg(not(any(target_feature = "avx512f", target_feature = "avx2", target_feature = "sse2", target_feature = "neon", all(target_family = "wasm", target_feature = "simd128"))))] |
417 | | use accumulate_512_scalar as accumulate_512; |
418 | | |
419 | | #[cfg(all(target_family = "wasm", target_feature = "simd128"))] |
420 | | fn scramble_acc_wasm(acc: &mut Acc, secret: &StripeLanes) { |
421 | | use core::arch::wasm32::*; |
422 | | |
423 | | let xacc = acc.0.as_mut_ptr() as *mut v128; |
424 | | let prime = u64x2_splat(xxh32::PRIME_1 as _); |
425 | | |
426 | | unsafe { |
427 | | for idx in 0..secret.len() { |
428 | | let acc_vec = v128_load(xacc.add(idx) as _); |
429 | | let shifted = u64x2_shr(acc_vec, 47); |
430 | | let data_vec = v128_xor(acc_vec, shifted); |
431 | | let key_vec = v128_load(secret[idx].as_ptr() as _); |
432 | | let mixed = v128_xor(data_vec, key_vec); |
433 | | xacc.add(idx).write(i64x2_mul(mixed, prime)); |
434 | | } |
435 | | } |
436 | | } |
437 | | |
438 | | #[cfg(target_feature = "neon")] |
439 | | fn scramble_acc_neon(acc: &mut Acc, secret: &StripeLanes) { |
440 | | //Full Neon version from xxhash source |
441 | | unsafe { |
442 | | #[cfg(target_arch = "arm")] |
443 | | use core::arch::arm::*; |
444 | | #[cfg(target_arch = "aarch64")] |
445 | | use core::arch::aarch64::*; |
446 | | |
447 | | let xacc = acc.0.as_mut_ptr() as *mut uint64x2_t; |
448 | | |
449 | | let prime_low = vdup_n_u32(xxh32::PRIME_1); |
450 | | let prime_hi = vreinterpretq_u32_u64(vdupq_n_u64((xxh32::PRIME_1 as u64) << 32)); |
451 | | |
452 | | for idx in 0..secret.len() { |
453 | | /* xacc[i] ^= (xacc[i] >> 47); */ |
454 | | let acc_vec = *xacc.add(idx); |
455 | | let shifted = vshrq_n_u64(acc_vec, 47); |
456 | | let data_vec = veorq_u64(acc_vec, shifted); |
457 | | |
458 | | /* xacc[i] ^= xsecret[i]; */ |
459 | | //According to xxhash sources you can do unaligned read here |
460 | | //but since Rust is kinda retarded about unaligned reads I'll avoid it for now |
461 | | let key_vec = vreinterpretq_u64_u8(vld1q_u8!(secret[idx].as_ptr())); |
462 | | let data_key = veorq_u64(data_vec, key_vec); |
463 | | |
464 | | let prod_hi = vmulq_u32(vreinterpretq_u32_u64(data_key), prime_hi); |
465 | | let data_key_lo = vmovn_u64(data_key); |
466 | | xacc.add(idx).write(vmlal_u32(vreinterpretq_u64_u32(prod_hi), data_key_lo, prime_low)); |
467 | | } |
468 | | } |
469 | | } |
470 | | |
471 | | #[cfg(all(target_feature = "sse2", not(any(target_feature = "avx2", target_feature = "avx512f"))))] |
472 | 0 | fn scramble_acc_sse2(acc: &mut Acc, secret: &StripeLanes) { |
473 | | unsafe { |
474 | | #[cfg(target_arch = "x86")] |
475 | | use core::arch::x86::*; |
476 | | #[cfg(target_arch = "x86_64")] |
477 | | use core::arch::x86_64::*; |
478 | | |
479 | 0 | let xacc = acc.0.as_mut_ptr() as *mut __m128i; |
480 | 0 | let prime32 = _mm_set1_epi32(xxh32::PRIME_1 as i32); |
481 | | |
482 | 0 | for idx in 0..secret.len() { |
483 | 0 | let acc_vec = *xacc.add(idx); |
484 | 0 | let shifted = _mm_srli_epi64(acc_vec, 47); |
485 | 0 | let data_vec = _mm_xor_si128(acc_vec, shifted); |
486 | 0 |
|
487 | 0 | let key_vec = _mm_loadu_si128(secret[idx].as_ptr() as _); |
488 | 0 | let data_key = _mm_xor_si128(data_vec, key_vec); |
489 | 0 |
|
490 | 0 | let data_key_hi = _mm_shuffle_epi32(data_key, _mm_shuffle(0, 3, 0, 1)); |
491 | 0 | let prod_lo = _mm_mul_epu32(data_key, prime32); |
492 | 0 | let prod_hi = _mm_mul_epu32(data_key_hi, prime32); |
493 | 0 | xacc.add(idx).write(_mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32))); |
494 | 0 | } |
495 | | } |
496 | 0 | } |
497 | | |
498 | | #[cfg(all(target_feature = "avx2", not(target_feature = "avx512f")))] |
499 | | fn scramble_acc_avx2(acc: &mut Acc, secret: &StripeLanes) { |
500 | | unsafe { |
501 | | #[cfg(target_arch = "x86")] |
502 | | use core::arch::x86::*; |
503 | | #[cfg(target_arch = "x86_64")] |
504 | | use core::arch::x86_64::*; |
505 | | |
506 | | let xacc = acc.0.as_mut_ptr() as *mut __m256i; |
507 | | let prime32 = _mm256_set1_epi32(xxh32::PRIME_1 as i32); |
508 | | |
509 | | for idx in 0..secret.len() { |
510 | | let acc_vec = *xacc.add(idx); |
511 | | let shifted = _mm256_srli_epi64(acc_vec, 47); |
512 | | let data_vec = _mm256_xor_si256(acc_vec, shifted); |
513 | | |
514 | | let key_vec = _mm256_loadu_si256(secret[idx].as_ptr() as _); |
515 | | let data_key = _mm256_xor_si256(data_vec, key_vec); |
516 | | |
517 | | let data_key_hi = _mm256_srli_epi64(data_key, 32); |
518 | | let prod_lo = _mm256_mul_epu32(data_key, prime32); |
519 | | let prod_hi = _mm256_mul_epu32(data_key_hi, prime32); |
520 | | xacc.add(idx).write(_mm256_add_epi64(prod_lo, _mm256_slli_epi64(prod_hi, 32))); |
521 | | } |
522 | | } |
523 | | } |
524 | | |
525 | | #[cfg(target_feature = "avx512f")] |
526 | | fn scramble_acc_avx512(acc: &mut Acc, secret: &StripeLanes) { |
527 | | unsafe { |
528 | | #[cfg(target_arch = "x86")] |
529 | | use core::arch::x86::*; |
530 | | #[cfg(target_arch = "x86_64")] |
531 | | use core::arch::x86_64::*; |
532 | | |
533 | | let xacc = acc.0.as_mut_ptr() as *mut __m512i; |
534 | | let prime32 = _mm512_set1_epi32(xxh32::PRIME_1 as i32); |
535 | | |
536 | | let idx = 0; |
537 | | |
538 | | let acc_vec = *xacc.add(idx); |
539 | | let shifted = _mm512_srli_epi64(acc_vec, 47); |
540 | | |
541 | | let key_vec = _mm512_loadu_si512(secret[idx].as_ptr() as _); |
542 | | let data_key = _mm512_ternarylogic_epi32(key_vec, acc_vec, shifted, 0x96); |
543 | | |
544 | | let data_key_hi = _mm512_srli_epi64(data_key, 32); |
545 | | let prod_lo = _mm512_mul_epu32(data_key, prime32); |
546 | | let prod_hi = _mm512_mul_epu32(data_key_hi, prime32); |
547 | | xacc.add(idx).write(_mm512_add_epi64(prod_lo, _mm512_slli_epi64(prod_hi, 32))); |
548 | | } |
549 | | } |
550 | | |
551 | | #[cfg(not(any(target_feature = "avx512f", target_feature = "avx2", target_feature = "sse2", target_feature = "neon", all(target_family = "wasm", target_feature = "simd128"))))] |
552 | | fn scramble_acc_scalar(acc: &mut Acc, secret: &[[u8; 8]; ACC_NB]) { |
553 | | for idx in 0..secret.len() { |
554 | | let key = u64::from_ne_bytes(secret[idx]).to_le(); |
555 | | let mut acc_val = xorshift64(acc.0[idx], 47); |
556 | | acc_val ^= key; |
557 | | acc.0[idx] = acc_val.wrapping_mul(xxh32::PRIME_1 as u64); |
558 | | } |
559 | | } |
560 | | |
561 | | #[cfg(all(target_family = "wasm", target_feature = "simd128"))] |
562 | | use scramble_acc_wasm as scramble_acc; |
563 | | |
564 | | #[cfg(target_feature = "neon")] |
565 | | use scramble_acc_neon as scramble_acc; |
566 | | |
567 | | #[cfg(all(target_feature = "sse2", not(any(target_feature = "avx2", target_feature = "avx512f"))))] |
568 | | use scramble_acc_sse2 as scramble_acc; |
569 | | |
570 | | #[cfg(all(target_feature = "avx2", not(target_feature = "avx512f")))] |
571 | | use scramble_acc_avx2 as scramble_acc; |
572 | | |
573 | | #[cfg(target_feature = "avx512f")] |
574 | | use scramble_acc_avx512 as scramble_acc; |
575 | | |
576 | | #[cfg(not(any(target_feature = "avx512f", target_feature = "avx2", target_feature = "sse2", target_feature = "neon", all(target_family = "wasm", target_feature = "simd128"))))] |
577 | | use scramble_acc_scalar as scramble_acc; |
578 | | |
579 | | #[inline(always)] |
580 | 0 | fn accumulate_loop(acc: &mut Acc, input: *const u8, secret: *const u8, nb_stripes: usize) { |
581 | 0 | for idx in 0..nb_stripes { |
582 | 0 | unsafe { |
583 | 0 | let input = input.add(idx * STRIPE_LEN); |
584 | 0 | //Miri complains about it for dumb reason so for not turn off prefetch |
585 | 0 | //_mm_prefetch(input as _, 320); |
586 | 0 |
|
587 | 0 | accumulate_512(acc, |
588 | 0 | &*(input as *const _), |
589 | 0 | &*(secret.add(idx * SECRET_CONSUME_RATE) as *const _) |
590 | 0 | ); |
591 | 0 | } |
592 | | } |
593 | 0 | } |
594 | | |
595 | | #[inline] |
596 | 0 | fn hash_long_internal_loop(acc: &mut Acc, input: &[u8], secret: &[u8]) { |
597 | 0 | let nb_stripes = (secret.len() - STRIPE_LEN) / SECRET_CONSUME_RATE; |
598 | 0 | let block_len = STRIPE_LEN * nb_stripes; |
599 | 0 | let nb_blocks = (input.len() - 1) / block_len; |
600 | | |
601 | 0 | for idx in 0..nb_blocks { |
602 | 0 | accumulate_loop(acc, slice_offset_ptr!(input, idx * block_len), secret.as_ptr(), nb_stripes); |
603 | 0 | scramble_acc(acc, get_aligned_chunk_ref(secret, secret.len() - STRIPE_LEN)); |
604 | | } |
605 | | |
606 | | //last partial block |
607 | 0 | debug_assert!(input.len() > STRIPE_LEN); |
608 | | |
609 | 0 | let nb_stripes = ((input.len() - 1) - (block_len * nb_blocks)) / STRIPE_LEN; |
610 | 0 | debug_assert!(nb_stripes <= (secret.len() / SECRET_CONSUME_RATE)); |
611 | 0 | accumulate_loop(acc, slice_offset_ptr!(input, nb_blocks * block_len), secret.as_ptr(), nb_stripes); |
612 | | |
613 | | //last stripe |
614 | 0 | accumulate_512(acc, get_aligned_chunk_ref(input, input.len() - STRIPE_LEN), get_aligned_chunk_ref(secret, secret.len() - STRIPE_LEN - SECRET_LASTACC_START)); |
615 | 0 | } |
616 | | |
617 | | #[inline(always)] |
618 | 0 | fn xxh3_64_1to3(input: &[u8], seed: u64, secret: &[u8]) -> u64 { |
619 | | let c1; let c2; let c3; |
620 | 0 | unsafe { |
621 | 0 | c1 = *input.get_unchecked(0); |
622 | 0 | c2 = *input.get_unchecked(input.len() >> 1); |
623 | 0 | c3 = *input.get_unchecked(input.len() - 1); |
624 | 0 | }; |
625 | | |
626 | 0 | let combo = (c1 as u32) << 16 | (c2 as u32) << 24 | (c3 as u32) << 0 | (input.len() as u32) << 8; |
627 | 0 | let flip = ((read_32le_unaligned(secret, 0) ^ read_32le_unaligned(secret, 4)) as u64).wrapping_add(seed); |
628 | 0 | xxh64::avalanche((combo as u64) ^ flip) |
629 | 0 | } |
630 | | |
631 | | #[inline(always)] |
632 | 0 | fn xxh3_64_4to8(input: &[u8], mut seed: u64, secret: &[u8]) -> u64 { |
633 | 0 | debug_assert!(input.len() >= 4 && input.len() <= 8); |
634 | | |
635 | 0 | seed ^= ((seed as u32).swap_bytes() as u64) << 32; |
636 | | |
637 | 0 | let input1 = read_32le_unaligned(input, 0); |
638 | 0 | let input2 = read_32le_unaligned(input, input.len() - 4); |
639 | | |
640 | 0 | let flip = (read_64le_unaligned(secret, 8) ^ read_64le_unaligned(secret, 16)).wrapping_sub(seed); |
641 | 0 | let input64 = (input2 as u64).wrapping_add((input1 as u64) << 32); |
642 | 0 | let keyed = input64 ^ flip; |
643 | | |
644 | 0 | strong_avalanche(keyed, input.len() as u64) |
645 | 0 | } |
646 | | |
647 | | #[inline(always)] |
648 | 0 | fn xxh3_64_9to16(input: &[u8], seed: u64, secret: &[u8]) -> u64 { |
649 | 0 | debug_assert!(input.len() >= 9 && input.len() <= 16); |
650 | | |
651 | 0 | let flip1 = (read_64le_unaligned(secret, 24) ^ read_64le_unaligned(secret, 32)).wrapping_add(seed); |
652 | 0 | let flip2 = (read_64le_unaligned(secret, 40) ^ read_64le_unaligned(secret, 48)).wrapping_sub(seed); |
653 | | |
654 | 0 | let input_lo = read_64le_unaligned(input, 0) ^ flip1; |
655 | 0 | let input_hi = read_64le_unaligned(input, input.len() - 8) ^ flip2; |
656 | | |
657 | 0 | let acc = (input.len() as u64).wrapping_add(input_lo.swap_bytes()) |
658 | 0 | .wrapping_add(input_hi) |
659 | 0 | .wrapping_add(mul128_fold64(input_lo, input_hi)); |
660 | | |
661 | 0 | avalanche(acc) |
662 | 0 | } |
663 | | |
664 | | #[inline(always)] |
665 | 0 | fn xxh3_64_0to16(input: &[u8], seed: u64, secret: &[u8]) -> u64 { |
666 | 0 | if input.len() > 8 { |
667 | 0 | xxh3_64_9to16(input, seed, secret) |
668 | 0 | } else if input.len() >= 4 { |
669 | 0 | xxh3_64_4to8(input, seed, secret) |
670 | 0 | } else if input.len() > 0 { |
671 | 0 | xxh3_64_1to3(input, seed, secret) |
672 | | } else { |
673 | 0 | xxh64::avalanche(seed ^ (read_64le_unaligned(secret, 56) ^ read_64le_unaligned(secret, 64))) |
674 | | } |
675 | 0 | } |
676 | | |
677 | | #[inline(always)] |
678 | 0 | fn xxh3_64_7to128(input: &[u8], seed: u64, secret: &[u8]) -> u64 { |
679 | 0 | let mut acc = (input.len() as u64).wrapping_mul(xxh64::PRIME_1); |
680 | | |
681 | 0 | if input.len() > 32 { |
682 | 0 | if input.len() > 64 { |
683 | 0 | if input.len() > 96 { |
684 | 0 | acc = acc.wrapping_add(mix16_b( |
685 | 0 | get_aligned_chunk_ref(input, 48), |
686 | 0 | get_aligned_chunk_ref(secret, 96), |
687 | 0 | seed |
688 | 0 | )); |
689 | 0 | acc = acc.wrapping_add(mix16_b( |
690 | 0 | get_aligned_chunk_ref(input, input.len() - 64), |
691 | 0 | get_aligned_chunk_ref(secret, 112), |
692 | 0 | seed |
693 | 0 | )); |
694 | 0 | } |
695 | | |
696 | 0 | acc = acc.wrapping_add(mix16_b( |
697 | 0 | get_aligned_chunk_ref(input, 32), |
698 | 0 | get_aligned_chunk_ref(secret, 64), |
699 | 0 | seed |
700 | 0 | )); |
701 | 0 | acc = acc.wrapping_add(mix16_b( |
702 | 0 | get_aligned_chunk_ref(input, input.len() - 48), |
703 | 0 | get_aligned_chunk_ref(secret, 80), |
704 | 0 | seed |
705 | 0 | )); |
706 | 0 | } |
707 | | |
708 | 0 | acc = acc.wrapping_add(mix16_b( |
709 | 0 | get_aligned_chunk_ref(input, 16), |
710 | 0 | get_aligned_chunk_ref(secret, 32), |
711 | 0 | seed |
712 | 0 | )); |
713 | 0 | acc = acc.wrapping_add(mix16_b( |
714 | 0 | get_aligned_chunk_ref(input, input.len() - 32), |
715 | 0 | get_aligned_chunk_ref(secret, 48), |
716 | 0 | seed |
717 | 0 | )); |
718 | 0 | } |
719 | | |
720 | 0 | acc = acc.wrapping_add(mix16_b( |
721 | 0 | get_aligned_chunk_ref(input, 0), |
722 | 0 | get_aligned_chunk_ref(secret, 0), |
723 | 0 | seed |
724 | 0 | )); |
725 | 0 | acc = acc.wrapping_add(mix16_b( |
726 | 0 | get_aligned_chunk_ref(input, input.len() - 16), |
727 | 0 | get_aligned_chunk_ref(secret, 16), |
728 | 0 | seed |
729 | 0 | )); |
730 | | |
731 | 0 | avalanche(acc) |
732 | 0 | } |
733 | | |
734 | | #[inline(never)] |
735 | 0 | fn xxh3_64_129to240(input: &[u8], seed: u64, secret: &[u8]) -> u64 { |
736 | | const START_OFFSET: usize = 3; |
737 | | const LAST_OFFSET: usize = 17; |
738 | | |
739 | 0 | let mut acc = (input.len() as u64).wrapping_mul(xxh64::PRIME_1); |
740 | 0 | let nb_rounds = input.len() / 16; |
741 | 0 | debug_assert!(nb_rounds >= 8); |
742 | | |
743 | 0 | let mut idx = 0; |
744 | 0 | while idx < 8 { |
745 | 0 | acc = acc.wrapping_add( |
746 | 0 | mix16_b( |
747 | 0 | get_aligned_chunk_ref(input, 16*idx), |
748 | 0 | get_aligned_chunk_ref(secret, 16*idx), |
749 | 0 | seed |
750 | 0 | ) |
751 | 0 | ); |
752 | 0 | idx = idx.wrapping_add(1); |
753 | 0 | } |
754 | 0 | acc = avalanche(acc); |
755 | | |
756 | 0 | while idx < nb_rounds { |
757 | 0 | acc = acc.wrapping_add( |
758 | 0 | mix16_b( |
759 | 0 | get_aligned_chunk_ref(input, 16*idx), |
760 | 0 | get_aligned_chunk_ref(secret, 16*(idx-8) + START_OFFSET), |
761 | 0 | seed |
762 | 0 | ) |
763 | 0 | ); |
764 | 0 | idx = idx.wrapping_add(1); |
765 | 0 | } |
766 | | |
767 | 0 | acc = acc.wrapping_add( |
768 | 0 | mix16_b( |
769 | 0 | get_aligned_chunk_ref(input, input.len()-16), |
770 | 0 | get_aligned_chunk_ref(secret, SECRET_SIZE_MIN-LAST_OFFSET), |
771 | 0 | seed |
772 | 0 | ) |
773 | 0 | ); |
774 | | |
775 | 0 | avalanche(acc) |
776 | 0 | } |
777 | | |
778 | | #[inline(always)] |
779 | 0 | fn xxh3_64_internal(input: &[u8], seed: u64, secret: &[u8], long_hash_fn: LongHashFn) -> u64 { |
780 | 0 | debug_assert!(secret.len() >= SECRET_SIZE_MIN); |
781 | | |
782 | 0 | if input.len() <= 16 { |
783 | 0 | xxh3_64_0to16(input, seed, secret) |
784 | 0 | } else if input.len() <= 128 { |
785 | 0 | xxh3_64_7to128(input, seed, secret) |
786 | 0 | } else if input.len() <= MID_SIZE_MAX { |
787 | 0 | xxh3_64_129to240(input, seed, secret) |
788 | | } else { |
789 | 0 | long_hash_fn(input, seed, secret) |
790 | | } |
791 | 0 | } |
792 | | |
793 | | #[inline(always)] |
794 | 0 | fn xxh3_64_long_impl(input: &[u8], secret: &[u8]) -> u64 { |
795 | 0 | let mut acc = INITIAL_ACC; |
796 | | |
797 | 0 | hash_long_internal_loop(&mut acc, input, secret); |
798 | | |
799 | 0 | merge_accs(&mut acc, get_aligned_chunk_ref(secret, SECRET_MERGEACCS_START), (input.len() as u64).wrapping_mul(xxh64::PRIME_1)) |
800 | 0 | } |
801 | | |
802 | | #[inline(never)] |
803 | 0 | fn xxh3_64_long_with_seed(input: &[u8], seed: u64, _secret: &[u8]) -> u64 { |
804 | 0 | match seed { |
805 | 0 | 0 => xxh3_64_long_impl(input, &DEFAULT_SECRET), |
806 | 0 | seed => xxh3_64_long_impl(input, &custom_default_secret(seed)), |
807 | | } |
808 | 0 | } |
809 | | |
810 | | #[inline(never)] |
811 | 0 | fn xxh3_64_long_default(input: &[u8], _seed: u64, _secret: &[u8]) -> u64 { |
812 | 0 | xxh3_64_long_impl(input, &DEFAULT_SECRET) |
813 | 0 | } |
814 | | |
815 | | #[inline(never)] |
816 | 0 | fn xxh3_64_long_with_secret(input: &[u8], _seed: u64, secret: &[u8]) -> u64 { |
817 | 0 | xxh3_64_long_impl(input, secret) |
818 | 0 | } |
819 | | |
820 | | #[inline] |
821 | | ///Returns 64bit hash for provided input. |
822 | 0 | pub fn xxh3_64(input: &[u8]) -> u64 { |
823 | 0 | xxh3_64_internal(input, 0, &DEFAULT_SECRET, xxh3_64_long_default) |
824 | 0 | } |
825 | | |
826 | | #[inline] |
827 | | ///Returns 64bit hash for provided input using seed. |
828 | | /// |
829 | | ///Note: While overhead of deriving new secret from provided seed is low, |
830 | | ///it would more efficient to generate secret at compile time using special function |
831 | | ///`const_custom_default_secret` from `const_xxh3` |
832 | 0 | pub fn xxh3_64_with_seed(input: &[u8], seed: u64) -> u64 { |
833 | 0 | xxh3_64_internal(input, seed, &DEFAULT_SECRET, xxh3_64_long_with_seed) |
834 | 0 | } |
835 | | |
836 | | #[inline] |
837 | | ///Returns 64bit hash for provided input using custom secret. |
838 | | /// |
839 | | ///This function panics if `secret` doesn't fit minimum required secret size. |
840 | | /// |
841 | | ///Prefer to use [SecretInput] with [xxh3_64_with_secret_input] to avoid assert |
842 | 0 | pub fn xxh3_64_with_secret(input: &[u8], secret: &[u8]) -> u64 { |
843 | 0 | assert!(secret.len() >= SECRET_SIZE_MIN); |
844 | 0 | xxh3_64_internal(input, 0, secret, xxh3_64_long_with_secret) |
845 | 0 | } |
846 | | |
847 | | #[inline] |
848 | | ///Returns 64bit hash for provided input using custom secret. |
849 | 0 | pub fn xxh3_64_with_secret_input(input: &[u8], secret: &SecretInput<impl AsRef<[u8]>>) -> u64 { |
850 | 0 | xxh3_64_internal(input, 0, secret.0.as_ref(), xxh3_64_long_with_secret) |
851 | 0 | } |
852 | | |
853 | | const INTERNAL_BUFFER_SIZE: usize = 256; |
854 | | const STRIPES_PER_BLOCK: usize = (DEFAULT_SECRET_SIZE - STRIPE_LEN) / SECRET_CONSUME_RATE; |
855 | | |
856 | | #[derive(Clone)] |
857 | | #[repr(align(64))] |
858 | | struct Aligned64<T>(T); |
859 | | |
860 | | #[inline] |
861 | | //Internal function shared between Xxh3 and Xxh3Default |
862 | 0 | fn xxh3_stateful_consume_stripes(acc: &mut Acc, nb_stripes: usize, nb_stripes_acc: usize, input: *const u8, secret: &[u8; DEFAULT_SECRET_SIZE]) -> usize { |
863 | 0 | if (STRIPES_PER_BLOCK - nb_stripes_acc) <= nb_stripes { |
864 | 0 | let stripes_to_end = STRIPES_PER_BLOCK - nb_stripes_acc; |
865 | 0 | let stripes_after_end = nb_stripes - stripes_to_end; |
866 | | |
867 | 0 | accumulate_loop(acc, input, slice_offset_ptr!(secret, nb_stripes_acc * SECRET_CONSUME_RATE), stripes_to_end); |
868 | 0 | scramble_acc(acc, get_aligned_chunk_ref(secret, DEFAULT_SECRET_SIZE - STRIPE_LEN)); |
869 | 0 | accumulate_loop(acc, unsafe { input.add(stripes_to_end * STRIPE_LEN) }, secret.as_ptr(), stripes_after_end); |
870 | 0 | stripes_after_end |
871 | | } else { |
872 | 0 | accumulate_loop(acc, input, slice_offset_ptr!(secret, nb_stripes_acc * SECRET_CONSUME_RATE), nb_stripes); |
873 | 0 | nb_stripes_acc.wrapping_add(nb_stripes) |
874 | | } |
875 | 0 | } |
876 | | |
877 | | //Internal function shared between Xxh3 and Xxh3Default |
878 | 0 | fn xxh3_stateful_update( |
879 | 0 | input: &[u8], |
880 | 0 | total_len: &mut u64, |
881 | 0 | acc: &mut Acc, |
882 | 0 | buffer: &mut Aligned64<[mem::MaybeUninit<u8>; INTERNAL_BUFFER_SIZE]>, buffered_size: &mut u16, |
883 | 0 | nb_stripes_acc: &mut usize, |
884 | 0 | secret: &Aligned64<[u8; DEFAULT_SECRET_SIZE]> |
885 | 0 | ) { |
886 | | const INTERNAL_BUFFER_STRIPES: usize = INTERNAL_BUFFER_SIZE / STRIPE_LEN; |
887 | | |
888 | 0 | let mut input_ptr = input.as_ptr(); |
889 | 0 | let mut input_len = input.len(); |
890 | 0 | *total_len = total_len.wrapping_add(input_len as u64); |
891 | | |
892 | 0 | if (input_len + *buffered_size as usize) <= INTERNAL_BUFFER_SIZE { |
893 | | unsafe { |
894 | 0 | ptr::copy_nonoverlapping(input_ptr, (buffer.0.as_mut_ptr() as *mut u8).offset(*buffered_size as isize), input_len) |
895 | | } |
896 | 0 | *buffered_size += input_len as u16; |
897 | 0 | return; |
898 | 0 | } |
899 | | |
900 | 0 | if *buffered_size > 0 { |
901 | 0 | let fill_len = INTERNAL_BUFFER_SIZE - *buffered_size as usize; |
902 | 0 |
|
903 | 0 | unsafe { |
904 | 0 | ptr::copy_nonoverlapping(input_ptr, (buffer.0.as_mut_ptr() as *mut u8).offset(*buffered_size as isize), fill_len); |
905 | 0 | input_ptr = input_ptr.add(fill_len); |
906 | 0 | input_len -= fill_len; |
907 | 0 | } |
908 | 0 |
|
909 | 0 | *nb_stripes_acc = xxh3_stateful_consume_stripes(acc, INTERNAL_BUFFER_STRIPES, *nb_stripes_acc, buffer.0.as_ptr() as *const u8, &secret.0); |
910 | 0 |
|
911 | 0 | *buffered_size = 0; |
912 | 0 | } |
913 | | |
914 | 0 | debug_assert_ne!(input_len, 0); |
915 | 0 | if input_len > INTERNAL_BUFFER_SIZE { |
916 | | loop { |
917 | 0 | *nb_stripes_acc = xxh3_stateful_consume_stripes(acc, INTERNAL_BUFFER_STRIPES, *nb_stripes_acc, input_ptr, &secret.0); |
918 | 0 | input_ptr = unsafe { |
919 | 0 | input_ptr.add(INTERNAL_BUFFER_SIZE) |
920 | 0 | }; |
921 | 0 | input_len = input_len - INTERNAL_BUFFER_SIZE; |
922 | | |
923 | 0 | if input_len <= INTERNAL_BUFFER_SIZE { |
924 | 0 | break; |
925 | 0 | } |
926 | | } |
927 | | |
928 | | unsafe { |
929 | 0 | ptr::copy_nonoverlapping(input_ptr.offset(-(STRIPE_LEN as isize)), (buffer.0.as_mut_ptr() as *mut u8).add(buffer.0.len() - STRIPE_LEN), STRIPE_LEN) |
930 | | } |
931 | 0 | } |
932 | | |
933 | 0 | debug_assert_ne!(input_len, 0); |
934 | 0 | debug_assert_eq!(*buffered_size, 0); |
935 | | unsafe { |
936 | 0 | ptr::copy_nonoverlapping(input_ptr, buffer.0.as_mut_ptr() as *mut u8, input_len) |
937 | | } |
938 | 0 | *buffered_size = input_len as u16; |
939 | 0 | } |
940 | | |
941 | | #[inline(always)] |
942 | | //Internal function shared between Xxh3 and Xxh3Default |
943 | 0 | fn xxh3_stateful_digest_internal(acc: &mut Acc, nb_stripes_acc: usize, buffer: &[u8], old_buffer: &[mem::MaybeUninit<u8>], secret: &Aligned64<[u8; DEFAULT_SECRET_SIZE]>) { |
944 | 0 | if buffer.len() >= STRIPE_LEN { |
945 | 0 | let nb_stripes = (buffer.len() - 1) / STRIPE_LEN; |
946 | 0 | xxh3_stateful_consume_stripes(acc, nb_stripes, nb_stripes_acc, buffer.as_ptr(), &secret.0); |
947 | 0 |
|
948 | 0 | accumulate_512(acc, |
949 | 0 | get_aligned_chunk_ref(buffer, buffer.len() - STRIPE_LEN), |
950 | 0 | get_aligned_chunk_ref(&secret.0, DEFAULT_SECRET_SIZE - STRIPE_LEN - SECRET_LASTACC_START) |
951 | 0 | ); |
952 | 0 | } else { |
953 | 0 | let mut last_stripe = mem::MaybeUninit::<[u8; STRIPE_LEN]>::uninit(); |
954 | 0 | let catchup_size = STRIPE_LEN - buffer.len(); |
955 | 0 | debug_assert!(buffer.len() > 0); |
956 | | |
957 | 0 | let last_stripe = unsafe { |
958 | 0 | ptr::copy_nonoverlapping((old_buffer.as_ptr() as *const u8).add(INTERNAL_BUFFER_SIZE - buffer.len() - catchup_size), last_stripe.as_mut_ptr() as _, catchup_size); |
959 | 0 | ptr::copy_nonoverlapping(buffer.as_ptr(), (last_stripe.as_mut_ptr() as *mut u8).add(catchup_size), buffer.len()); |
960 | 0 | slice::from_raw_parts(last_stripe.as_ptr() as *const u8, buffer.len() + catchup_size) |
961 | | }; |
962 | | |
963 | 0 | accumulate_512(acc, get_aligned_chunk_ref(&last_stripe, 0), get_aligned_chunk_ref(&secret.0, DEFAULT_SECRET_SIZE - STRIPE_LEN - SECRET_LASTACC_START)); |
964 | | } |
965 | 0 | } |
966 | | |
967 | | #[derive(Clone)] |
968 | | ///Default XXH3 Streaming algorithm |
969 | | /// |
970 | | ///This is optimized version of Xxh3 struct that uses default seed/secret |
971 | | /// |
972 | | ///Optimal for use in hash maps |
973 | | pub struct Xxh3Default { |
974 | | acc: Acc, |
975 | | buffer: Aligned64<[mem::MaybeUninit<u8>; INTERNAL_BUFFER_SIZE]>, |
976 | | buffered_size: u16, |
977 | | nb_stripes_acc: usize, |
978 | | total_len: u64, |
979 | | } |
980 | | |
981 | | impl Xxh3Default { |
982 | | const DEFAULT_SECRET: Aligned64<[u8; DEFAULT_SECRET_SIZE]> = Aligned64(DEFAULT_SECRET); |
983 | | |
984 | | #[inline(always)] |
985 | | ///Creates new hasher with default settings |
986 | 0 | pub const fn new() -> Self { |
987 | 0 | Self { |
988 | 0 | acc: INITIAL_ACC, |
989 | 0 | buffer: Aligned64([mem::MaybeUninit::uninit(); INTERNAL_BUFFER_SIZE]), |
990 | 0 | buffered_size: 0, |
991 | 0 | nb_stripes_acc: 0, |
992 | 0 | total_len: 0, |
993 | 0 | } |
994 | 0 | } |
995 | | |
996 | | #[inline(always)] |
997 | | ///Resets state |
998 | 0 | pub fn reset(&mut self) { |
999 | 0 | self.acc = INITIAL_ACC; |
1000 | 0 | self.total_len = 0; |
1001 | 0 | self.buffered_size = 0; |
1002 | 0 | self.nb_stripes_acc = 0; |
1003 | 0 | } |
1004 | | |
1005 | | #[inline(always)] |
1006 | 0 | fn buffered_input(&self) -> &[u8] { |
1007 | 0 | let ptr = self.buffer.0.as_ptr(); |
1008 | | unsafe { |
1009 | 0 | slice::from_raw_parts(ptr as *const u8, self.buffered_size as usize) |
1010 | | } |
1011 | 0 | } |
1012 | | |
1013 | | #[inline(always)] |
1014 | 0 | fn processed_buffer(&self) -> &[mem::MaybeUninit<u8>] { |
1015 | 0 | let ptr = self.buffer.0.as_ptr(); |
1016 | | unsafe { |
1017 | 0 | slice::from_raw_parts(ptr.add(self.buffered_size as usize), self.buffer.0.len() - self.buffered_size as usize) |
1018 | | } |
1019 | 0 | } |
1020 | | |
1021 | | #[inline(always)] |
1022 | | ///Hashes provided chunk |
1023 | 0 | pub fn update(&mut self, input: &[u8]) { |
1024 | 0 | xxh3_stateful_update(input, &mut self.total_len, &mut self.acc, &mut self.buffer, &mut self.buffered_size, &mut self.nb_stripes_acc, &Self::DEFAULT_SECRET); |
1025 | 0 | } |
1026 | | |
1027 | | #[inline(never)] |
1028 | 0 | fn digest_mid_sized(&self) -> u64 { |
1029 | 0 | let mut acc = self.acc.clone(); |
1030 | 0 | xxh3_stateful_digest_internal(&mut acc, self.nb_stripes_acc, self.buffered_input(), self.processed_buffer(), &Self::DEFAULT_SECRET); |
1031 | | |
1032 | 0 | merge_accs(&mut acc, get_aligned_chunk_ref(&Self::DEFAULT_SECRET.0, SECRET_MERGEACCS_START), |
1033 | 0 | self.total_len.wrapping_mul(xxh64::PRIME_1)) |
1034 | 0 | } |
1035 | | |
1036 | | #[inline(never)] |
1037 | 0 | fn digest_mid_sized_128(&self) -> u128 { |
1038 | 0 | let mut acc = self.acc.clone(); |
1039 | 0 | xxh3_stateful_digest_internal(&mut acc, self.nb_stripes_acc, self.buffered_input(), self.processed_buffer(), &Self::DEFAULT_SECRET); |
1040 | | |
1041 | 0 | let low = merge_accs(&mut acc, get_aligned_chunk_ref(&Self::DEFAULT_SECRET.0, SECRET_MERGEACCS_START), |
1042 | 0 | self.total_len.wrapping_mul(xxh64::PRIME_1)); |
1043 | 0 | let high = merge_accs(&mut acc, get_aligned_chunk_ref(&Self::DEFAULT_SECRET.0, |
1044 | 0 | DEFAULT_SECRET_SIZE - mem::size_of_val(&self.acc) - SECRET_MERGEACCS_START), |
1045 | 0 | !self.total_len.wrapping_mul(xxh64::PRIME_2)); |
1046 | 0 | ((high as u128) << 64) | (low as u128) |
1047 | 0 | } |
1048 | | |
1049 | | ///Computes hash. |
1050 | 0 | pub fn digest(&self) -> u64 { |
1051 | | //Separating digest mid sized allows us to inline this function, which benefits |
1052 | | //code generation when hashing fixed size types and/or if the seed is known. |
1053 | 0 | if self.total_len > MID_SIZE_MAX as u64 { |
1054 | 0 | self.digest_mid_sized() |
1055 | | } else { |
1056 | 0 | xxh3_64_internal(self.buffered_input(), 0, &Self::DEFAULT_SECRET.0, xxh3_64_long_default) |
1057 | | } |
1058 | 0 | } |
1059 | | |
1060 | | ///Computes hash as 128bit integer. |
1061 | 0 | pub fn digest128(&self) -> u128 { |
1062 | | //Separating digest mid sized allows us to inline this function, which benefits |
1063 | | //code generation when hashing fixed size types and/or if the seed is known. |
1064 | 0 | if self.total_len > MID_SIZE_MAX as u64 { |
1065 | 0 | self.digest_mid_sized_128() |
1066 | | } else { |
1067 | 0 | xxh3_128_internal(self.buffered_input(), 0, &Self::DEFAULT_SECRET.0, xxh3_128_long_default) |
1068 | | } |
1069 | 0 | } |
1070 | | } |
1071 | | |
1072 | | impl Default for Xxh3Default { |
1073 | | #[inline(always)] |
1074 | 0 | fn default() -> Self { |
1075 | 0 | Self::new() |
1076 | 0 | } |
1077 | | } |
1078 | | |
1079 | | |
1080 | | impl hash::Hasher for Xxh3Default { |
1081 | | #[inline(always)] |
1082 | 0 | fn finish(&self) -> u64 { |
1083 | 0 | self.digest() |
1084 | 0 | } |
1085 | | |
1086 | | #[inline(always)] |
1087 | 0 | fn write(&mut self, input: &[u8]) { |
1088 | 0 | self.update(input) |
1089 | 0 | } |
1090 | | } |
1091 | | |
1092 | | #[cfg(feature = "std")] |
1093 | | impl std::io::Write for Xxh3Default { |
1094 | | #[inline] |
1095 | | fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { |
1096 | | self.update(buf); |
1097 | | Ok(buf.len()) |
1098 | | } |
1099 | | |
1100 | | #[inline] |
1101 | | fn flush(&mut self) -> std::io::Result<()> { |
1102 | | Ok(()) |
1103 | | } |
1104 | | } |
1105 | | |
1106 | | #[derive(Clone)] |
1107 | | ///XXH3 Streaming algorithm |
1108 | | /// |
1109 | | ///Internal state uses rather large buffers, therefore it might be beneficial |
1110 | | ///to store hasher on heap rather than stack. |
1111 | | ///Implementation makes no attempts at that, leaving choice entirely to user. |
1112 | | /// |
1113 | | ///Note that it is better to use [Xxh3Default](struct.Xxh3Default.html) in hash maps |
1114 | | ///due to Rust hash interface which requires to create new instance of hasher every time. |
1115 | | pub struct Xxh3 { |
1116 | | acc: Acc, |
1117 | | custom_secret: Aligned64<[u8; DEFAULT_SECRET_SIZE]>, |
1118 | | buffer: Aligned64<[mem::MaybeUninit<u8>; INTERNAL_BUFFER_SIZE]>, |
1119 | | buffered_size: u16, |
1120 | | nb_stripes_acc: usize, |
1121 | | total_len: u64, |
1122 | | seed: u64, |
1123 | | } |
1124 | | |
1125 | | impl Xxh3 { |
1126 | | #[inline(always)] |
1127 | | ///Creates new hasher with default settings |
1128 | 0 | pub const fn new() -> Self { |
1129 | 0 | Self::with_custom_ops(0, DEFAULT_SECRET) |
1130 | 0 | } |
1131 | | |
1132 | | #[inline] |
1133 | | ///Creates new hasher with all options. |
1134 | 0 | const fn with_custom_ops(seed: u64, secret: [u8; DEFAULT_SECRET_SIZE]) -> Self { |
1135 | 0 | Self { |
1136 | 0 | acc: INITIAL_ACC, |
1137 | 0 | custom_secret: Aligned64(secret), |
1138 | 0 | buffer: Aligned64([mem::MaybeUninit::uninit(); INTERNAL_BUFFER_SIZE]), |
1139 | 0 | buffered_size: 0, |
1140 | 0 | nb_stripes_acc: 0, |
1141 | 0 | total_len: 0, |
1142 | 0 | seed, |
1143 | 0 | } |
1144 | 0 | } |
1145 | | |
1146 | | #[inline(always)] |
1147 | | ///Creates new hasher with custom seed. |
1148 | 0 | pub const fn with_secret(secret: [u8; DEFAULT_SECRET_SIZE]) -> Self { |
1149 | 0 | Self::with_custom_ops(0, secret) |
1150 | 0 | } |
1151 | | |
1152 | | #[inline(always)] |
1153 | | ///Creates new hasher with custom seed. |
1154 | 0 | pub fn with_seed(seed: u64) -> Self { |
1155 | 0 | Self::with_custom_ops(seed, custom_default_secret(seed)) |
1156 | 0 | } |
1157 | | |
1158 | | #[inline(always)] |
1159 | | ///Resets state |
1160 | 0 | pub fn reset(&mut self) { |
1161 | 0 | self.acc = INITIAL_ACC; |
1162 | 0 | self.total_len = 0; |
1163 | 0 | self.buffered_size = 0; |
1164 | 0 | self.nb_stripes_acc = 0; |
1165 | 0 | } |
1166 | | |
1167 | | #[inline(always)] |
1168 | 0 | fn buffered_input(&self) -> &[u8] { |
1169 | 0 | let ptr = self.buffer.0.as_ptr(); |
1170 | | unsafe { |
1171 | 0 | slice::from_raw_parts(ptr as *const u8, self.buffered_size as usize) |
1172 | | } |
1173 | 0 | } |
1174 | | |
1175 | | #[inline(always)] |
1176 | 0 | fn processed_buffer(&self) -> &[mem::MaybeUninit<u8>] { |
1177 | 0 | let ptr = self.buffer.0.as_ptr(); |
1178 | | unsafe { |
1179 | 0 | slice::from_raw_parts(ptr.add(self.buffered_size as usize), self.buffer.0.len() - self.buffered_size as usize) |
1180 | | } |
1181 | 0 | } |
1182 | | |
1183 | | #[inline] |
1184 | | ///Hashes provided chunk |
1185 | 0 | pub fn update(&mut self, input: &[u8]) { |
1186 | 0 | xxh3_stateful_update(input, &mut self.total_len, &mut self.acc, &mut self.buffer, &mut self.buffered_size, &mut self.nb_stripes_acc, &self.custom_secret); |
1187 | 0 | } |
1188 | | |
1189 | | #[inline(never)] |
1190 | 0 | fn digest_mid_sized(&self) -> u64 { |
1191 | 0 | let mut acc = self.acc.clone(); |
1192 | 0 | xxh3_stateful_digest_internal(&mut acc, self.nb_stripes_acc, self.buffered_input(), self.processed_buffer(), &self.custom_secret); |
1193 | | |
1194 | 0 | merge_accs(&mut acc, get_aligned_chunk_ref(&self.custom_secret.0, SECRET_MERGEACCS_START), |
1195 | 0 | self.total_len.wrapping_mul(xxh64::PRIME_1)) |
1196 | 0 | } |
1197 | | |
1198 | | #[inline(never)] |
1199 | 0 | fn digest_mid_sized_128(&self) -> u128 { |
1200 | 0 | let mut acc = self.acc.clone(); |
1201 | 0 | xxh3_stateful_digest_internal(&mut acc, self.nb_stripes_acc, self.buffered_input(), self.processed_buffer(), &self.custom_secret); |
1202 | | |
1203 | 0 | let low = merge_accs(&mut acc, get_aligned_chunk_ref(&self.custom_secret.0, SECRET_MERGEACCS_START), self.total_len.wrapping_mul(xxh64::PRIME_1)); |
1204 | 0 | let high = merge_accs(&mut acc, get_aligned_chunk_ref(&self.custom_secret.0, self.custom_secret.0.len() - mem::size_of_val(&self.acc) - SECRET_MERGEACCS_START), !self.total_len.wrapping_mul(xxh64::PRIME_2)); |
1205 | 0 | ((high as u128) << 64) | (low as u128) |
1206 | 0 | } |
1207 | | |
1208 | | ///Computes hash. |
1209 | 0 | pub fn digest(&self) -> u64 { |
1210 | | //Separating digest mid sized allows us to inline this function, which benefits |
1211 | | //code generation when hashing fixed size types and/or if the seed is known. |
1212 | 0 | if self.total_len > MID_SIZE_MAX as u64 { |
1213 | 0 | self.digest_mid_sized() |
1214 | 0 | } else if self.seed > 0 { |
1215 | | //Technically we should not need to use it. |
1216 | | //But in all actuality original xxh3 implementation uses default secret for input with size less or equal to MID_SIZE_MAX |
1217 | 0 | xxh3_64_internal(self.buffered_input(), self.seed, &DEFAULT_SECRET, xxh3_64_long_with_seed) |
1218 | | } else { |
1219 | 0 | xxh3_64_internal(self.buffered_input(), self.seed, &self.custom_secret.0, xxh3_64_long_with_secret) |
1220 | | } |
1221 | 0 | } |
1222 | | |
1223 | | ///Computes hash as 128bit integer. |
1224 | 0 | pub fn digest128(&self) -> u128 { |
1225 | | //Separating digest mid sized allows us to inline this function, which benefits |
1226 | | //code generation when hashing fixed size types and/or if the seed is known. |
1227 | 0 | if self.total_len > MID_SIZE_MAX as u64 { |
1228 | 0 | self.digest_mid_sized_128() |
1229 | 0 | } else if self.seed > 0 { |
1230 | | //Technically we should not need to use it. |
1231 | | //But in all actuality original xxh3 implementation uses default secret for input with size less or equal to MID_SIZE_MAX |
1232 | 0 | xxh3_128_internal(self.buffered_input(), self.seed, &DEFAULT_SECRET, xxh3_128_long_with_seed) |
1233 | | } else { |
1234 | 0 | xxh3_128_internal(self.buffered_input(), self.seed, &self.custom_secret.0, xxh3_128_long_with_secret) |
1235 | | } |
1236 | 0 | } |
1237 | | } |
1238 | | |
1239 | | impl Default for Xxh3 { |
1240 | | #[inline(always)] |
1241 | 0 | fn default() -> Self { |
1242 | 0 | Self::new() |
1243 | 0 | } |
1244 | | } |
1245 | | |
1246 | | impl core::hash::Hasher for Xxh3 { |
1247 | | #[inline(always)] |
1248 | 0 | fn finish(&self) -> u64 { |
1249 | 0 | self.digest() |
1250 | 0 | } |
1251 | | |
1252 | | #[inline(always)] |
1253 | 0 | fn write(&mut self, input: &[u8]) { |
1254 | 0 | self.update(input) |
1255 | 0 | } |
1256 | | } |
1257 | | |
1258 | | #[cfg(feature = "std")] |
1259 | | impl std::io::Write for Xxh3 { |
1260 | | #[inline] |
1261 | | fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { |
1262 | | self.update(buf); |
1263 | | Ok(buf.len()) |
1264 | | } |
1265 | | |
1266 | | #[inline] |
1267 | | fn flush(&mut self) -> std::io::Result<()> { |
1268 | | Ok(()) |
1269 | | } |
1270 | | } |
1271 | | |
1272 | | #[derive(Clone, Copy)] |
1273 | | ///Hash builder for `Xxh3` |
1274 | | pub struct Xxh3Builder { |
1275 | | seed: u64, |
1276 | | secret: [u8; DEFAULT_SECRET_SIZE], |
1277 | | } |
1278 | | |
1279 | | impl Xxh3Builder { |
1280 | | #[inline(always)] |
1281 | | ///Creates new instance with default params. |
1282 | 0 | pub const fn new() -> Self { |
1283 | 0 | Self { |
1284 | 0 | seed: 0, |
1285 | 0 | secret: DEFAULT_SECRET, |
1286 | 0 | } |
1287 | 0 | } |
1288 | | |
1289 | | #[inline(always)] |
1290 | | ///Sets `seed` for `xxh3` algorithm |
1291 | | /// |
1292 | | ///This method overrides default secret with custom based on seed. |
1293 | | /// |
1294 | | ///To counter it, override secret using [Xxh3Builder::with_secret] |
1295 | 0 | pub const fn with_seed(mut self, seed: u64) -> Self { |
1296 | 0 | self.seed = seed; |
1297 | 0 | self.secret = const_custom_default_secret(seed); |
1298 | 0 | self |
1299 | 0 | } |
1300 | | |
1301 | | #[inline(always)] |
1302 | | ///Sets custom `secret` for `xxh3` algorithm |
1303 | 0 | pub const fn with_secret(mut self, secret: [u8; DEFAULT_SECRET_SIZE]) -> Self { |
1304 | 0 | self.secret = secret; |
1305 | 0 | self |
1306 | 0 | } |
1307 | | |
1308 | | #[inline(always)] |
1309 | | ///Creates `Xxh3` instance |
1310 | 0 | pub const fn build(self) -> Xxh3 { |
1311 | 0 | Xxh3::with_custom_ops(self.seed, self.secret) |
1312 | 0 | } |
1313 | | } |
1314 | | |
1315 | | impl core::hash::BuildHasher for Xxh3Builder { |
1316 | | type Hasher = Xxh3; |
1317 | | |
1318 | | #[inline(always)] |
1319 | 0 | fn build_hasher(&self) -> Self::Hasher { |
1320 | 0 | self.build() |
1321 | 0 | } |
1322 | | } |
1323 | | |
1324 | | impl Default for Xxh3Builder { |
1325 | | #[inline(always)] |
1326 | 0 | fn default() -> Self { |
1327 | 0 | Self::new() |
1328 | 0 | } |
1329 | | } |
1330 | | |
1331 | | #[derive(Clone, Copy)] |
1332 | | ///Hash builder for `Xxh3Default` |
1333 | | pub struct Xxh3DefaultBuilder; |
1334 | | |
1335 | | impl Xxh3DefaultBuilder { |
1336 | | #[inline(always)] |
1337 | | ///Creates new instance with default params. |
1338 | 0 | pub const fn new() -> Self { |
1339 | 0 | Self |
1340 | 0 | } |
1341 | | |
1342 | | #[inline(always)] |
1343 | | ///Creates `Xxh3` instance |
1344 | 0 | pub const fn build(self) -> Xxh3Default { |
1345 | 0 | Xxh3Default::new() |
1346 | 0 | } |
1347 | | } |
1348 | | |
1349 | | impl core::hash::BuildHasher for Xxh3DefaultBuilder { |
1350 | | type Hasher = Xxh3Default; |
1351 | | |
1352 | | #[inline(always)] |
1353 | 0 | fn build_hasher(&self) -> Self::Hasher { |
1354 | 0 | self.build() |
1355 | 0 | } |
1356 | | } |
1357 | | |
1358 | | impl Default for Xxh3DefaultBuilder { |
1359 | | #[inline(always)] |
1360 | 0 | fn default() -> Self { |
1361 | 0 | Self::new() |
1362 | 0 | } |
1363 | | } |
1364 | | |
1365 | | // |
1366 | | //128bit |
1367 | | // |
1368 | | |
1369 | | #[inline] |
1370 | 0 | fn xxh3_128_long_impl(input: &[u8], secret: &[u8]) -> u128 { |
1371 | 0 | let mut acc = INITIAL_ACC; |
1372 | | |
1373 | 0 | hash_long_internal_loop(&mut acc, input, secret); |
1374 | | |
1375 | 0 | debug_assert!(secret.len() >= mem::size_of::<Acc>() + SECRET_MERGEACCS_START); |
1376 | 0 | let lo = merge_accs(&mut acc, get_aligned_chunk_ref(secret, SECRET_MERGEACCS_START), (input.len() as u64).wrapping_mul(xxh64::PRIME_1)); |
1377 | 0 | let hi = merge_accs(&mut acc, |
1378 | 0 | get_aligned_chunk_ref(secret, secret.len() - mem::size_of::<Acc>() - SECRET_MERGEACCS_START), |
1379 | 0 | !(input.len() as u64).wrapping_mul(xxh64::PRIME_2)); |
1380 | | |
1381 | 0 | lo as u128 | (hi as u128) << 64 |
1382 | 0 | } |
1383 | | |
1384 | | #[inline(always)] |
1385 | 0 | fn xxh3_128_9to16(input: &[u8], seed: u64, secret: &[u8]) -> u128 { |
1386 | 0 | let flip_lo = (read_64le_unaligned(secret, 32) ^ read_64le_unaligned(secret, 40)).wrapping_sub(seed); |
1387 | 0 | let flip_hi = (read_64le_unaligned(secret, 48) ^ read_64le_unaligned(secret, 56)).wrapping_add(seed); |
1388 | 0 | let input_lo = read_64le_unaligned(input, 0); |
1389 | 0 | let mut input_hi = read_64le_unaligned(input, input.len() - 8); |
1390 | | |
1391 | 0 | let (mut mul_low, mut mul_high) = mul64_to128(input_lo ^ input_hi ^ flip_lo, xxh64::PRIME_1); |
1392 | | |
1393 | 0 | mul_low = mul_low.wrapping_add((input.len() as u64 - 1) << 54); |
1394 | 0 | input_hi ^= flip_hi; |
1395 | 0 | mul_high = mul_high.wrapping_add( |
1396 | 0 | input_hi.wrapping_add(mult32_to64(input_hi as u32, xxh32::PRIME_2 - 1)) |
1397 | 0 | ); |
1398 | | |
1399 | 0 | mul_low ^= mul_high.swap_bytes(); |
1400 | | |
1401 | 0 | let (result_low, mut result_hi) = mul64_to128(mul_low, xxh64::PRIME_2); |
1402 | 0 | result_hi = result_hi.wrapping_add( |
1403 | 0 | mul_high.wrapping_mul(xxh64::PRIME_2) |
1404 | 0 | ); |
1405 | | |
1406 | 0 | to_u128!(avalanche(result_low), avalanche(result_hi)) |
1407 | 0 | } |
1408 | | |
1409 | | #[inline(always)] |
1410 | 0 | fn xxh3_128_4to8(input: &[u8], mut seed: u64, secret: &[u8]) -> u128 { |
1411 | 0 | seed ^= ((seed as u32).swap_bytes() as u64) << 32; |
1412 | | |
1413 | 0 | let lo = read_32le_unaligned(input, 0); |
1414 | 0 | let hi = read_32le_unaligned(input, input.len() - 4); |
1415 | 0 | let input_64 = (lo as u64).wrapping_add((hi as u64) << 32); |
1416 | | |
1417 | 0 | let flip = (read_64le_unaligned(secret, 16) ^ read_64le_unaligned(secret, 24)).wrapping_add(seed); |
1418 | 0 | let keyed = input_64 ^ flip; |
1419 | | |
1420 | 0 | let (mut lo, mut hi) = mul64_to128(keyed, xxh64::PRIME_1.wrapping_add((input.len() as u64) << 2)); |
1421 | | |
1422 | 0 | hi = hi.wrapping_add(lo << 1); |
1423 | 0 | lo ^= hi >> 3; |
1424 | | |
1425 | 0 | lo = xorshift64(lo, 35).wrapping_mul(0x9FB21C651E98DF25); |
1426 | 0 | lo = xorshift64(lo, 28); |
1427 | 0 | hi = avalanche(hi); |
1428 | | |
1429 | 0 | lo as u128 | (hi as u128) << 64 |
1430 | 0 | } |
1431 | | |
1432 | | #[inline(always)] |
1433 | 0 | fn xxh3_128_1to3(input: &[u8], seed: u64, secret: &[u8]) -> u128 { |
1434 | | let c1; let c2; let c3; |
1435 | 0 | unsafe { |
1436 | 0 | c1 = *input.get_unchecked(0); |
1437 | 0 | c2 = *input.get_unchecked(input.len() >> 1); |
1438 | 0 | c3 = *input.get_unchecked(input.len() - 1); |
1439 | 0 | }; |
1440 | 0 | let input_lo = (c1 as u32) << 16 | (c2 as u32) << 24 | (c3 as u32) << 0 | (input.len() as u32) << 8; |
1441 | 0 | let input_hi = input_lo.swap_bytes().rotate_left(13); |
1442 | | |
1443 | 0 | let flip_lo = (read_32le_unaligned(secret, 0) as u64 ^ read_32le_unaligned(secret, 4) as u64).wrapping_add(seed); |
1444 | 0 | let flip_hi = (read_32le_unaligned(secret, 8) as u64 ^ read_32le_unaligned(secret, 12) as u64).wrapping_sub(seed); |
1445 | 0 | let keyed_lo = input_lo as u64 ^ flip_lo; |
1446 | 0 | let keyed_hi = input_hi as u64 ^ flip_hi; |
1447 | | |
1448 | 0 | xxh64::avalanche(keyed_lo) as u128 | (xxh64::avalanche(keyed_hi) as u128) << 64 |
1449 | 0 | } |
1450 | | |
1451 | | #[inline(always)] |
1452 | 0 | fn xxh3_128_0to16(input: &[u8], seed: u64, secret: &[u8]) -> u128 { |
1453 | 0 | if input.len() > 8 { |
1454 | 0 | xxh3_128_9to16(input, seed, secret) |
1455 | 0 | } else if input.len() >= 4 { |
1456 | 0 | xxh3_128_4to8(input, seed, secret) |
1457 | 0 | } else if input.len() > 0 { |
1458 | 0 | xxh3_128_1to3(input, seed, secret) |
1459 | | } else { |
1460 | 0 | let flip_lo = read_64le_unaligned(secret, 64) ^ read_64le_unaligned(secret, 72); |
1461 | 0 | let flip_hi = read_64le_unaligned(secret, 80) ^ read_64le_unaligned(secret, 88); |
1462 | 0 | xxh64::avalanche(seed ^ flip_lo) as u128 | (xxh64::avalanche(seed ^ flip_hi) as u128) << 64 |
1463 | | } |
1464 | 0 | } |
1465 | | |
1466 | | #[inline(always)] |
1467 | 0 | fn xxh3_128_7to128(input: &[u8], seed: u64, secret: &[u8]) -> u128 { |
1468 | 0 | let mut lo = (input.len() as u64).wrapping_mul(xxh64::PRIME_1); |
1469 | 0 | let mut hi = 0; |
1470 | | |
1471 | 0 | if input.len() > 32 { |
1472 | 0 | if input.len() > 64 { |
1473 | 0 | if input.len() > 96 { |
1474 | 0 |
|
1475 | 0 | mix32_b(&mut lo, &mut hi, |
1476 | 0 | get_aligned_chunk_ref(input, 48), |
1477 | 0 | get_aligned_chunk_ref(input, input.len() - 64), |
1478 | 0 | get_aligned_chunk_ref(secret, 96), |
1479 | 0 | seed |
1480 | 0 | ); |
1481 | 0 | } |
1482 | | |
1483 | 0 | mix32_b(&mut lo, &mut hi, |
1484 | 0 | get_aligned_chunk_ref(input, 32), |
1485 | 0 | get_aligned_chunk_ref(input, input.len() - 48), |
1486 | 0 | get_aligned_chunk_ref(secret, 64), |
1487 | 0 | seed |
1488 | | ); |
1489 | 0 | } |
1490 | | |
1491 | 0 | mix32_b(&mut lo, &mut hi, |
1492 | 0 | get_aligned_chunk_ref(input, 16), |
1493 | 0 | get_aligned_chunk_ref(input, input.len() - 32), |
1494 | 0 | get_aligned_chunk_ref(secret, 32), |
1495 | 0 | seed |
1496 | | ); |
1497 | 0 | } |
1498 | | |
1499 | 0 | mix32_b(&mut lo, &mut hi, |
1500 | 0 | get_aligned_chunk_ref(input, 0), |
1501 | 0 | get_aligned_chunk_ref(input, input.len() - 16), |
1502 | 0 | get_aligned_chunk_ref(secret, 0), |
1503 | 0 | seed |
1504 | | ); |
1505 | | |
1506 | 0 | to_u128!( |
1507 | 0 | avalanche( |
1508 | 0 | lo.wrapping_add(hi) |
1509 | | ), |
1510 | 0 | 0u64.wrapping_sub( |
1511 | 0 | avalanche( |
1512 | 0 | lo.wrapping_mul(xxh64::PRIME_1) |
1513 | 0 | .wrapping_add(hi.wrapping_mul(xxh64::PRIME_4)) |
1514 | 0 | .wrapping_add((input.len() as u64).wrapping_sub(seed).wrapping_mul(xxh64::PRIME_2)) |
1515 | | ) |
1516 | | ) |
1517 | | ) |
1518 | 0 | } |
1519 | | |
1520 | | #[inline(never)] |
1521 | 0 | fn xxh3_128_129to240(input: &[u8], seed: u64, secret: &[u8]) -> u128 { |
1522 | | const START_OFFSET: usize = 3; |
1523 | | const LAST_OFFSET: usize = 17; |
1524 | 0 | let nb_rounds = input.len() / 32; |
1525 | 0 | debug_assert!(nb_rounds >= 4); |
1526 | | |
1527 | 0 | let mut lo = (input.len() as u64).wrapping_mul(xxh64::PRIME_1); |
1528 | 0 | let mut hi = 0; |
1529 | | |
1530 | 0 | let mut idx = 0; |
1531 | 0 | while idx < 4 { |
1532 | 0 | let offset_idx = 32 * idx; |
1533 | 0 | mix32_b(&mut lo, &mut hi, |
1534 | 0 | get_aligned_chunk_ref(input, offset_idx), |
1535 | 0 | get_aligned_chunk_ref(input, offset_idx + 16), |
1536 | 0 | get_aligned_chunk_ref(secret, offset_idx), |
1537 | 0 | seed |
1538 | 0 | ); |
1539 | 0 | idx = idx.wrapping_add(1); |
1540 | 0 | } |
1541 | | |
1542 | 0 | lo = avalanche(lo); |
1543 | 0 | hi = avalanche(hi); |
1544 | | |
1545 | 0 | while idx < nb_rounds { |
1546 | 0 | mix32_b(&mut lo, &mut hi, |
1547 | 0 | get_aligned_chunk_ref(input, 32 * idx), |
1548 | 0 | get_aligned_chunk_ref(input, (32 * idx) + 16), |
1549 | 0 | get_aligned_chunk_ref(secret, START_OFFSET.wrapping_add(32 * (idx - 4))), |
1550 | 0 | seed |
1551 | 0 | ); |
1552 | 0 | idx = idx.wrapping_add(1); |
1553 | 0 | } |
1554 | | |
1555 | 0 | mix32_b(&mut lo, &mut hi, |
1556 | 0 | get_aligned_chunk_ref(input, input.len() - 16), |
1557 | 0 | get_aligned_chunk_ref(input, input.len() - 32), |
1558 | 0 | get_aligned_chunk_ref(secret, SECRET_SIZE_MIN - LAST_OFFSET - 16), |
1559 | 0 | 0u64.wrapping_sub(seed) |
1560 | | ); |
1561 | | |
1562 | 0 | to_u128!( |
1563 | 0 | avalanche( |
1564 | 0 | lo.wrapping_add(hi) |
1565 | | ), |
1566 | 0 | 0u64.wrapping_sub( |
1567 | 0 | avalanche( |
1568 | 0 | lo.wrapping_mul(xxh64::PRIME_1) |
1569 | 0 | .wrapping_add(hi.wrapping_mul(xxh64::PRIME_4)) |
1570 | 0 | .wrapping_add((input.len() as u64).wrapping_sub(seed).wrapping_mul(xxh64::PRIME_2)) |
1571 | | ) |
1572 | | ) |
1573 | | ) |
1574 | 0 | } |
1575 | | |
1576 | | #[inline(always)] |
1577 | 0 | fn xxh3_128_internal(input: &[u8], seed: u64, secret: &[u8], long_hash_fn: LongHashFn128) -> u128 { |
1578 | 0 | debug_assert!(secret.len() >= SECRET_SIZE_MIN); |
1579 | | |
1580 | 0 | if input.len() <= 16 { |
1581 | 0 | xxh3_128_0to16(input, seed, secret) |
1582 | 0 | } else if input.len() <= 128 { |
1583 | 0 | xxh3_128_7to128(input, seed, secret) |
1584 | 0 | } else if input.len() <= MID_SIZE_MAX { |
1585 | 0 | xxh3_128_129to240(input, seed, secret) |
1586 | | } else { |
1587 | 0 | long_hash_fn(input, seed, secret) |
1588 | | } |
1589 | 0 | } |
1590 | | |
1591 | | #[inline(never)] |
1592 | 0 | fn xxh3_128_long_default(input: &[u8], _seed: u64, _secret: &[u8]) -> u128 { |
1593 | 0 | xxh3_128_long_impl(input, &DEFAULT_SECRET) |
1594 | 0 | } |
1595 | | |
1596 | | #[inline(never)] |
1597 | 0 | fn xxh3_128_long_with_seed(input: &[u8], seed: u64, _secret: &[u8]) -> u128 { |
1598 | 0 | match seed { |
1599 | 0 | 0 => xxh3_128_long_impl(input, &DEFAULT_SECRET), |
1600 | 0 | seed => xxh3_128_long_impl(input, &custom_default_secret(seed)), |
1601 | | } |
1602 | 0 | } |
1603 | | |
1604 | | #[inline(never)] |
1605 | 0 | fn xxh3_128_long_with_secret(input: &[u8], _seed: u64, secret: &[u8]) -> u128 { |
1606 | 0 | xxh3_128_long_impl(input, secret) |
1607 | 0 | } |
1608 | | |
1609 | | #[inline] |
1610 | | ///Returns 128bit hash for provided input. |
1611 | 0 | pub fn xxh3_128(input: &[u8]) -> u128 { |
1612 | 0 | xxh3_128_internal(input, 0, &DEFAULT_SECRET, xxh3_128_long_default) |
1613 | 0 | } |
1614 | | |
1615 | | #[inline] |
1616 | | ///Returns 128 hash for provided input using seed. |
1617 | | /// |
1618 | | ///Note: While overhead of deriving new secret from provided seed is low, |
1619 | | ///it would more efficient to generate secret at compile time using special function |
1620 | | ///`const_custom_default_secret` from `const_xxh3` |
1621 | 0 | pub fn xxh3_128_with_seed(input: &[u8], seed: u64) -> u128 { |
1622 | 0 | xxh3_128_internal(input, seed, &DEFAULT_SECRET, xxh3_128_long_with_seed) |
1623 | 0 | } |
1624 | | |
1625 | | #[inline] |
1626 | | ///Returns 128 hash for provided input using custom secret. |
1627 | | /// |
1628 | | ///This function panics if `secret` doesn't fit minimum required secret size. |
1629 | | /// |
1630 | | ///Prefer to use [SecretInput] with [xxh3_128_with_secret_input] to avoid assert |
1631 | 0 | pub fn xxh3_128_with_secret(input: &[u8], secret: &[u8]) -> u128 { |
1632 | 0 | assert!(secret.len() >= SECRET_SIZE_MIN); |
1633 | 0 | xxh3_128_internal(input, 0, secret, xxh3_128_long_with_secret) |
1634 | 0 | } |
1635 | | |
1636 | | #[inline] |
1637 | | ///Returns 128 hash for provided input using custom secret. |
1638 | 0 | pub fn xxh3_128_with_secret_input(input: &[u8], secret: &SecretInput<impl AsRef<[u8]>>) -> u128 { |
1639 | 0 | xxh3_128_internal(input, 0, secret.0.as_ref(), xxh3_128_long_with_secret) |
1640 | 0 | } |