1use core::{ptr, mem, slice, hash};
7
8use crate::xxh32_common as xxh32;
9use crate::xxh64_common as xxh64;
10use crate::xxh3_common::*;
11use crate::utils::{Buffer, get_unaligned_chunk, get_aligned_chunk_ref};
12
13#[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)]
19struct Acc([u64; ACC_NB]);
20#[cfg(all(target_feature = "avx2", not(target_feature = "avx512f")))]
21#[repr(align(32))]
22#[derive(Clone)]
23struct Acc([u64; ACC_NB]);
24#[cfg(target_feature = "avx512f")]
25#[repr(align(64))]
26#[derive(Clone)]
27struct 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)]
31struct Acc([u64; ACC_NB]);
32
33const 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
38type LongHashFn = fn(&[u8], u64, &[u8]) -> u64;
39type LongHashFn128 = fn(&[u8], u64, &[u8]) -> u128;
40
41#[cfg(all(target_family = "wasm", target_feature = "simd128"))]
42type 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"))]
44type 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"))]
46type 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")))]
48type 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")))]
50type 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"))))]
52type 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"))))]
54type 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")]
56type StripeLanes = [[u8; mem::size_of::<core::arch::aarch64::uint8x16_t>()]; STRIPE_LEN / mem::size_of::<core::arch::aarch64::uint8x16_t>()];
57
58pub struct SecretInput<T>(T);
60
61impl<T: AsRef<[u8]>> SecretInput<T> {
62 #[inline(always)]
63 pub fn try_new(input: T) -> Option<Self> {
65 if input.as_ref().len() >= SECRET_SIZE_MIN {
66 Some(Self(input))
67 } else {
68 None
69 }
70 }
71}
72
73impl<const N: usize> SecretInput<[u8; N]> {
74 pub const fn new(input: [u8; N]) -> Self {
80 assert!(N >= SECRET_SIZE_MIN, "input length must be equal or greater than SECRET_SIZE_MIN=136");
81
82 Self(input)
83 }
84}
85
86#[cfg(any(target_feature = "sse2", target_feature = "avx2", target_feature = "avx512f"))]
89#[inline]
90const fn _mm_shuffle(z: u32, y: u32, x: u32, w: u32) -> i32 {
91 ((z << 6) | (y << 4) | (x << 2) | w) as i32
92}
93
94#[inline(always)]
95const fn mult32_to64(left: u32, right: u32) -> u64 {
96 (left as u64).wrapping_mul(right as u64)
97}
98
99macro_rules! to_u128 {
113 ($lo:expr, $hi:expr) => {
114 ($lo) as u128 | ((($hi) as u128) << 64)
115 };
116}
117
118macro_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)]
132fn read_32le_unaligned(data: &[u8], offset: usize) -> u32 {
133 u32::from_ne_bytes(*get_aligned_chunk_ref(data, offset)).to_le()
134}
135
136#[inline(always)]
137fn read_64le_unaligned(data: &[u8], offset: usize) -> u64 {
138 u64::from_ne_bytes(*get_aligned_chunk_ref(data, offset)).to_le()
139}
140
141#[inline(always)]
142fn mix_two_accs(acc: &mut Acc, offset: usize, secret: &[[u8; 8]; 2]) -> u64 {
143 mul128_fold64(acc.0[offset] ^ u64::from_ne_bytes(secret[0]).to_le(),
144 acc.0[offset + 1] ^ u64::from_ne_bytes(secret[1]).to_le())
145}
146
147#[inline]
148fn 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 mix_two_accs!(0);
156 mix_two_accs!(1);
157 mix_two_accs!(2);
158 mix_two_accs!(3);
159
160 avalanche(result)
161}
162
163#[inline(always)]
164fn mix16_b(input: &[[u8; 8]; 2], secret: &[[u8; 8]; 2], seed: u64) -> u64 {
165 let mut input_lo = u64::from_ne_bytes(input[0]).to_le();
166 let mut input_hi = u64::from_ne_bytes(input[1]).to_le();
167
168 input_lo ^= u64::from_ne_bytes(secret[0]).to_le().wrapping_add(seed);
169 input_hi ^= u64::from_ne_bytes(secret[1]).to_le().wrapping_sub(seed);
170
171 mul128_fold64(input_lo, input_hi)
172}
173
174#[inline(always)]
175fn 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 *lo = lo.wrapping_add(mix16_b(input_1, &secret[0], seed));
179 *lo ^= u64::from_ne_bytes(input_2[0]).to_le().wrapping_add(u64::from_ne_bytes(input_2[1]).to_le());
180
181 *hi = hi.wrapping_add(mix16_b(input_2, &secret[1], seed));
182 *hi ^= u64::from_ne_bytes(input_1[0]).to_le().wrapping_add(u64::from_ne_bytes(input_1[1]).to_le());
183}
184
185#[inline(always)]
186fn custom_default_secret(seed: u64) -> [u8; DEFAULT_SECRET_SIZE] {
187 let mut result = mem::MaybeUninit::<[u8; DEFAULT_SECRET_SIZE]>::uninit();
188
189 let nb_rounds = DEFAULT_SECRET_SIZE / 16;
190
191 for idx in 0..nb_rounds {
192 let low = get_unaligned_chunk::<u64>(&DEFAULT_SECRET, idx * 16).to_le().wrapping_add(seed);
193 let hi = get_unaligned_chunk::<u64>(&DEFAULT_SECRET, idx * 16 + 8).to_le().wrapping_sub(seed);
194
195 Buffer {
196 ptr: result.as_mut_ptr() as *mut u8,
197 len: DEFAULT_SECRET_SIZE,
198 offset: idx * 16,
199 }.copy_from_slice(&low.to_le_bytes());
200 Buffer {
201 ptr: result.as_mut_ptr() as *mut u8,
202 len: DEFAULT_SECRET_SIZE,
203 offset: idx * 16 + 8,
204 }.copy_from_slice(&hi.to_le_bytes());
205 }
206
207 unsafe {
208 result.assume_init()
209 }
210}
211
212#[cfg(all(target_family = "wasm", target_feature = "simd128"))]
213fn 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"))]
253macro_rules! vld1q_u8 {
254 ($ptr:expr) => {
255 core::arch::aarch64::vld1q_u8($ptr)
256
257 }
258}
259
260#[cfg(all(target_arch = "arm", target_feature = "neon"))]
262macro_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")]
269fn accumulate_512_neon(acc: &mut Acc, input: &StripeLanes, secret: &StripeLanes) {
270 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 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 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 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 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 let data_key_lo = unzipped.0;
302 let data_key_hi = unzipped.1;
304
305 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"))))]
321fn 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 let xacc = acc.0.as_mut_ptr() as *mut __m128i;
329
330 for idx in 0..secret.len() {
331 let data_vec = _mm_loadu_si128(input[idx].as_ptr() as _);
332 let key_vec = _mm_loadu_si128(secret[idx].as_ptr() as _);
333 let data_key = _mm_xor_si128(data_vec, key_vec);
334
335 let data_key_lo = _mm_shuffle_epi32(data_key, _mm_shuffle(0, 3, 0, 1));
336 let product = _mm_mul_epu32(data_key, data_key_lo);
337
338 let data_swap = _mm_shuffle_epi32(data_vec, _mm_shuffle(1,0,3,2));
339 let sum = _mm_add_epi64(*xacc.add(idx), data_swap);
340 xacc.add(idx).write(_mm_add_epi64(product, sum));
341 }
342 }
343}
344
345#[cfg(all(target_feature = "avx2", not(target_feature = "avx512f")))]
346fn 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")]
371fn 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"))))]
396fn 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"))]
407use accumulate_512_wasm as accumulate_512;
408#[cfg(target_feature = "neon")]
409use accumulate_512_neon as accumulate_512;
410#[cfg(all(target_feature = "sse2", not(any(target_feature = "avx2", target_feature = "avx512f"))))]
411use accumulate_512_sse2 as accumulate_512;
412#[cfg(all(target_feature = "avx2", not(target_feature = "avx512f")))]
413use accumulate_512_avx2 as accumulate_512;
414#[cfg(target_feature = "avx512f")]
415use 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"))))]
417use accumulate_512_scalar as accumulate_512;
418
419#[cfg(all(target_family = "wasm", target_feature = "simd128"))]
420fn 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")]
439fn scramble_acc_neon(acc: &mut Acc, secret: &StripeLanes) {
440 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 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 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"))))]
472fn 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 let xacc = acc.0.as_mut_ptr() as *mut __m128i;
480 let prime32 = _mm_set1_epi32(xxh32::PRIME_1 as i32);
481
482 for idx in 0..secret.len() {
483 let acc_vec = *xacc.add(idx);
484 let shifted = _mm_srli_epi64(acc_vec, 47);
485 let data_vec = _mm_xor_si128(acc_vec, shifted);
486
487 let key_vec = _mm_loadu_si128(secret[idx].as_ptr() as _);
488 let data_key = _mm_xor_si128(data_vec, key_vec);
489
490 let data_key_hi = _mm_shuffle_epi32(data_key, _mm_shuffle(0, 3, 0, 1));
491 let prod_lo = _mm_mul_epu32(data_key, prime32);
492 let prod_hi = _mm_mul_epu32(data_key_hi, prime32);
493 xacc.add(idx).write(_mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32)));
494 }
495 }
496}
497
498#[cfg(all(target_feature = "avx2", not(target_feature = "avx512f")))]
499fn 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")]
526fn 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"))))]
552fn 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"))]
562use scramble_acc_wasm as scramble_acc;
563
564#[cfg(target_feature = "neon")]
565use scramble_acc_neon as scramble_acc;
566
567#[cfg(all(target_feature = "sse2", not(any(target_feature = "avx2", target_feature = "avx512f"))))]
568use scramble_acc_sse2 as scramble_acc;
569
570#[cfg(all(target_feature = "avx2", not(target_feature = "avx512f")))]
571use scramble_acc_avx2 as scramble_acc;
572
573#[cfg(target_feature = "avx512f")]
574use 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"))))]
577use scramble_acc_scalar as scramble_acc;
578
579#[inline(always)]
580fn accumulate_loop(acc: &mut Acc, input: *const u8, secret: *const u8, nb_stripes: usize) {
581 for idx in 0..nb_stripes {
582 unsafe {
583 let input = input.add(idx * STRIPE_LEN);
584 accumulate_512(acc,
588 &*(input as *const _),
589 &*(secret.add(idx * SECRET_CONSUME_RATE) as *const _)
590 );
591 }
592 }
593}
594
595#[inline]
596fn hash_long_internal_loop(acc: &mut Acc, input: &[u8], secret: &[u8]) {
597 let nb_stripes = (secret.len() - STRIPE_LEN) / SECRET_CONSUME_RATE;
598 let block_len = STRIPE_LEN * nb_stripes;
599 let nb_blocks = (input.len() - 1) / block_len;
600
601 for idx in 0..nb_blocks {
602 accumulate_loop(acc, slice_offset_ptr!(input, idx * block_len), secret.as_ptr(), nb_stripes);
603 scramble_acc(acc, get_aligned_chunk_ref(secret, secret.len() - STRIPE_LEN));
604 }
605
606 debug_assert!(input.len() > STRIPE_LEN);
608
609 let nb_stripes = ((input.len() - 1) - (block_len * nb_blocks)) / STRIPE_LEN;
610 debug_assert!(nb_stripes <= (secret.len() / SECRET_CONSUME_RATE));
611 accumulate_loop(acc, slice_offset_ptr!(input, nb_blocks * block_len), secret.as_ptr(), nb_stripes);
612
613 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}
616
617#[inline(always)]
618fn xxh3_64_1to3(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
619 let c1; let c2; let c3;
620 unsafe {
621 c1 = *input.get_unchecked(0);
622 c2 = *input.get_unchecked(input.len() >> 1);
623 c3 = *input.get_unchecked(input.len() - 1);
624 };
625
626 let combo = (c1 as u32) << 16 | (c2 as u32) << 24 | (c3 as u32) << 0 | (input.len() as u32) << 8;
627 let flip = ((read_32le_unaligned(secret, 0) ^ read_32le_unaligned(secret, 4)) as u64).wrapping_add(seed);
628 xxh64::avalanche((combo as u64) ^ flip)
629}
630
631#[inline(always)]
632fn xxh3_64_4to8(input: &[u8], mut seed: u64, secret: &[u8]) -> u64 {
633 debug_assert!(input.len() >= 4 && input.len() <= 8);
634
635 seed ^= ((seed as u32).swap_bytes() as u64) << 32;
636
637 let input1 = read_32le_unaligned(input, 0);
638 let input2 = read_32le_unaligned(input, input.len() - 4);
639
640 let flip = (read_64le_unaligned(secret, 8) ^ read_64le_unaligned(secret, 16)).wrapping_sub(seed);
641 let input64 = (input2 as u64).wrapping_add((input1 as u64) << 32);
642 let keyed = input64 ^ flip;
643
644 strong_avalanche(keyed, input.len() as u64)
645}
646
647#[inline(always)]
648fn xxh3_64_9to16(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
649 debug_assert!(input.len() >= 9 && input.len() <= 16);
650
651 let flip1 = (read_64le_unaligned(secret, 24) ^ read_64le_unaligned(secret, 32)).wrapping_add(seed);
652 let flip2 = (read_64le_unaligned(secret, 40) ^ read_64le_unaligned(secret, 48)).wrapping_sub(seed);
653
654 let input_lo = read_64le_unaligned(input, 0) ^ flip1;
655 let input_hi = read_64le_unaligned(input, input.len() - 8) ^ flip2;
656
657 let acc = (input.len() as u64).wrapping_add(input_lo.swap_bytes())
658 .wrapping_add(input_hi)
659 .wrapping_add(mul128_fold64(input_lo, input_hi));
660
661 avalanche(acc)
662}
663
664#[inline(always)]
665fn xxh3_64_0to16(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
666 if input.len() > 8 {
667 xxh3_64_9to16(input, seed, secret)
668 } else if input.len() >= 4 {
669 xxh3_64_4to8(input, seed, secret)
670 } else if input.len() > 0 {
671 xxh3_64_1to3(input, seed, secret)
672 } else {
673 xxh64::avalanche(seed ^ (read_64le_unaligned(secret, 56) ^ read_64le_unaligned(secret, 64)))
674 }
675}
676
677#[inline(always)]
678fn xxh3_64_7to128(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
679 let mut acc = (input.len() as u64).wrapping_mul(xxh64::PRIME_1);
680
681 if input.len() > 32 {
682 if input.len() > 64 {
683 if input.len() > 96 {
684 acc = acc.wrapping_add(mix16_b(
685 get_aligned_chunk_ref(input, 48),
686 get_aligned_chunk_ref(secret, 96),
687 seed
688 ));
689 acc = acc.wrapping_add(mix16_b(
690 get_aligned_chunk_ref(input, input.len() - 64),
691 get_aligned_chunk_ref(secret, 112),
692 seed
693 ));
694 }
695
696 acc = acc.wrapping_add(mix16_b(
697 get_aligned_chunk_ref(input, 32),
698 get_aligned_chunk_ref(secret, 64),
699 seed
700 ));
701 acc = acc.wrapping_add(mix16_b(
702 get_aligned_chunk_ref(input, input.len() - 48),
703 get_aligned_chunk_ref(secret, 80),
704 seed
705 ));
706 }
707
708 acc = acc.wrapping_add(mix16_b(
709 get_aligned_chunk_ref(input, 16),
710 get_aligned_chunk_ref(secret, 32),
711 seed
712 ));
713 acc = acc.wrapping_add(mix16_b(
714 get_aligned_chunk_ref(input, input.len() - 32),
715 get_aligned_chunk_ref(secret, 48),
716 seed
717 ));
718 }
719
720 acc = acc.wrapping_add(mix16_b(
721 get_aligned_chunk_ref(input, 0),
722 get_aligned_chunk_ref(secret, 0),
723 seed
724 ));
725 acc = acc.wrapping_add(mix16_b(
726 get_aligned_chunk_ref(input, input.len() - 16),
727 get_aligned_chunk_ref(secret, 16),
728 seed
729 ));
730
731 avalanche(acc)
732}
733
734#[inline(never)]
735fn xxh3_64_129to240(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
736 const START_OFFSET: usize = 3;
737 const LAST_OFFSET: usize = 17;
738
739 let mut acc = (input.len() as u64).wrapping_mul(xxh64::PRIME_1);
740 let nb_rounds = input.len() / 16;
741 debug_assert!(nb_rounds >= 8);
742
743 let mut idx = 0;
744 while idx < 8 {
745 acc = acc.wrapping_add(
746 mix16_b(
747 get_aligned_chunk_ref(input, 16*idx),
748 get_aligned_chunk_ref(secret, 16*idx),
749 seed
750 )
751 );
752 idx = idx.wrapping_add(1);
753 }
754 acc = avalanche(acc);
755
756 while idx < nb_rounds {
757 acc = acc.wrapping_add(
758 mix16_b(
759 get_aligned_chunk_ref(input, 16*idx),
760 get_aligned_chunk_ref(secret, 16*(idx-8) + START_OFFSET),
761 seed
762 )
763 );
764 idx = idx.wrapping_add(1);
765 }
766
767 acc = acc.wrapping_add(
768 mix16_b(
769 get_aligned_chunk_ref(input, input.len()-16),
770 get_aligned_chunk_ref(secret, SECRET_SIZE_MIN-LAST_OFFSET),
771 seed
772 )
773 );
774
775 avalanche(acc)
776}
777
778#[inline(always)]
779fn xxh3_64_internal(input: &[u8], seed: u64, secret: &[u8], long_hash_fn: LongHashFn) -> u64 {
780 debug_assert!(secret.len() >= SECRET_SIZE_MIN);
781
782 if input.len() <= 16 {
783 xxh3_64_0to16(input, seed, secret)
784 } else if input.len() <= 128 {
785 xxh3_64_7to128(input, seed, secret)
786 } else if input.len() <= MID_SIZE_MAX {
787 xxh3_64_129to240(input, seed, secret)
788 } else {
789 long_hash_fn(input, seed, secret)
790 }
791}
792
793#[inline(always)]
794fn xxh3_64_long_impl(input: &[u8], secret: &[u8]) -> u64 {
795 let mut acc = INITIAL_ACC;
796
797 hash_long_internal_loop(&mut acc, input, secret);
798
799 merge_accs(&mut acc, get_aligned_chunk_ref(secret, SECRET_MERGEACCS_START), (input.len() as u64).wrapping_mul(xxh64::PRIME_1))
800}
801
802#[inline(never)]
803fn xxh3_64_long_with_seed(input: &[u8], seed: u64, _secret: &[u8]) -> u64 {
804 match seed {
805 0 => xxh3_64_long_impl(input, &DEFAULT_SECRET),
806 seed => xxh3_64_long_impl(input, &custom_default_secret(seed)),
807 }
808}
809
810#[inline(never)]
811fn xxh3_64_long_default(input: &[u8], _seed: u64, _secret: &[u8]) -> u64 {
812 xxh3_64_long_impl(input, &DEFAULT_SECRET)
813}
814
815#[inline(never)]
816fn xxh3_64_long_with_secret(input: &[u8], _seed: u64, secret: &[u8]) -> u64 {
817 xxh3_64_long_impl(input, secret)
818}
819
820#[inline]
821pub fn xxh3_64(input: &[u8]) -> u64 {
823 xxh3_64_internal(input, 0, &DEFAULT_SECRET, xxh3_64_long_default)
824}
825
826#[inline]
827pub fn xxh3_64_with_seed(input: &[u8], seed: u64) -> u64 {
833 xxh3_64_internal(input, seed, &DEFAULT_SECRET, xxh3_64_long_with_seed)
834}
835
836#[inline]
837pub fn xxh3_64_with_secret(input: &[u8], secret: &[u8]) -> u64 {
843 assert!(secret.len() >= SECRET_SIZE_MIN);
844 xxh3_64_internal(input, 0, secret, xxh3_64_long_with_secret)
845}
846
847#[inline]
848pub fn xxh3_64_with_secret_input(input: &[u8], secret: &SecretInput<impl AsRef<[u8]>>) -> u64 {
850 xxh3_64_internal(input, 0, secret.0.as_ref(), xxh3_64_long_with_secret)
851}
852
853const INTERNAL_BUFFER_SIZE: usize = 256;
854const STRIPES_PER_BLOCK: usize = (DEFAULT_SECRET_SIZE - STRIPE_LEN) / SECRET_CONSUME_RATE;
855
856#[derive(Clone)]
857#[repr(align(64))]
858struct Aligned64<T>(T);
859
860#[inline]
861fn xxh3_stateful_consume_stripes(acc: &mut Acc, nb_stripes: usize, nb_stripes_acc: usize, input: *const u8, secret: &[u8; DEFAULT_SECRET_SIZE]) -> usize {
863 if (STRIPES_PER_BLOCK - nb_stripes_acc) <= nb_stripes {
864 let stripes_to_end = STRIPES_PER_BLOCK - nb_stripes_acc;
865 let stripes_after_end = nb_stripes - stripes_to_end;
866
867 accumulate_loop(acc, input, slice_offset_ptr!(secret, nb_stripes_acc * SECRET_CONSUME_RATE), stripes_to_end);
868 scramble_acc(acc, get_aligned_chunk_ref(secret, DEFAULT_SECRET_SIZE - STRIPE_LEN));
869 accumulate_loop(acc, unsafe { input.add(stripes_to_end * STRIPE_LEN) }, secret.as_ptr(), stripes_after_end);
870 stripes_after_end
871 } else {
872 accumulate_loop(acc, input, slice_offset_ptr!(secret, nb_stripes_acc * SECRET_CONSUME_RATE), nb_stripes);
873 nb_stripes_acc.wrapping_add(nb_stripes)
874 }
875}
876
877fn xxh3_stateful_update(
879 input: &[u8],
880 total_len: &mut u64,
881 acc: &mut Acc,
882 buffer: &mut Aligned64<[mem::MaybeUninit<u8>; INTERNAL_BUFFER_SIZE]>, buffered_size: &mut u16,
883 nb_stripes_acc: &mut usize,
884 secret: &Aligned64<[u8; DEFAULT_SECRET_SIZE]>
885) {
886 const INTERNAL_BUFFER_STRIPES: usize = INTERNAL_BUFFER_SIZE / STRIPE_LEN;
887
888 let mut input_ptr = input.as_ptr();
889 let mut input_len = input.len();
890 *total_len = total_len.wrapping_add(input_len as u64);
891
892 if (input_len + *buffered_size as usize) <= INTERNAL_BUFFER_SIZE {
893 unsafe {
894 ptr::copy_nonoverlapping(input_ptr, (buffer.0.as_mut_ptr() as *mut u8).offset(*buffered_size as isize), input_len)
895 }
896 *buffered_size += input_len as u16;
897 return;
898 }
899
900 if *buffered_size > 0 {
901 let fill_len = INTERNAL_BUFFER_SIZE - *buffered_size as usize;
902
903 unsafe {
904 ptr::copy_nonoverlapping(input_ptr, (buffer.0.as_mut_ptr() as *mut u8).offset(*buffered_size as isize), fill_len);
905 input_ptr = input_ptr.add(fill_len);
906 input_len -= fill_len;
907 }
908
909 *nb_stripes_acc = xxh3_stateful_consume_stripes(acc, INTERNAL_BUFFER_STRIPES, *nb_stripes_acc, buffer.0.as_ptr() as *const u8, &secret.0);
910
911 *buffered_size = 0;
912 }
913
914 debug_assert_ne!(input_len, 0);
915 if input_len > INTERNAL_BUFFER_SIZE {
916 loop {
917 *nb_stripes_acc = xxh3_stateful_consume_stripes(acc, INTERNAL_BUFFER_STRIPES, *nb_stripes_acc, input_ptr, &secret.0);
918 input_ptr = unsafe {
919 input_ptr.add(INTERNAL_BUFFER_SIZE)
920 };
921 input_len = input_len - INTERNAL_BUFFER_SIZE;
922
923 if input_len <= INTERNAL_BUFFER_SIZE {
924 break;
925 }
926 }
927
928 unsafe {
929 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 }
932
933 debug_assert_ne!(input_len, 0);
934 debug_assert_eq!(*buffered_size, 0);
935 unsafe {
936 ptr::copy_nonoverlapping(input_ptr, buffer.0.as_mut_ptr() as *mut u8, input_len)
937 }
938 *buffered_size = input_len as u16;
939}
940
941#[inline(always)]
942fn 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 if buffer.len() >= STRIPE_LEN {
945 let nb_stripes = (buffer.len() - 1) / STRIPE_LEN;
946 xxh3_stateful_consume_stripes(acc, nb_stripes, nb_stripes_acc, buffer.as_ptr(), &secret.0);
947
948 accumulate_512(acc,
949 get_aligned_chunk_ref(buffer, buffer.len() - STRIPE_LEN),
950 get_aligned_chunk_ref(&secret.0, DEFAULT_SECRET_SIZE - STRIPE_LEN - SECRET_LASTACC_START)
951 );
952 } else {
953 let mut last_stripe = mem::MaybeUninit::<[u8; STRIPE_LEN]>::uninit();
954 let catchup_size = STRIPE_LEN - buffer.len();
955 debug_assert!(buffer.len() > 0);
956
957 let last_stripe = unsafe {
958 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 ptr::copy_nonoverlapping(buffer.as_ptr(), (last_stripe.as_mut_ptr() as *mut u8).add(catchup_size), buffer.len());
960 slice::from_raw_parts(last_stripe.as_ptr() as *const u8, buffer.len() + catchup_size)
961 };
962
963 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}
966
967#[derive(Clone)]
968pub 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
981impl Xxh3Default {
982 const DEFAULT_SECRET: Aligned64<[u8; DEFAULT_SECRET_SIZE]> = Aligned64(DEFAULT_SECRET);
983
984 #[inline(always)]
985 pub const fn new() -> Self {
987 Self {
988 acc: INITIAL_ACC,
989 buffer: Aligned64([mem::MaybeUninit::uninit(); INTERNAL_BUFFER_SIZE]),
990 buffered_size: 0,
991 nb_stripes_acc: 0,
992 total_len: 0,
993 }
994 }
995
996 #[inline(always)]
997 pub fn reset(&mut self) {
999 self.acc = INITIAL_ACC;
1000 self.total_len = 0;
1001 self.buffered_size = 0;
1002 self.nb_stripes_acc = 0;
1003 }
1004
1005 #[inline(always)]
1006 fn buffered_input(&self) -> &[u8] {
1007 let ptr = self.buffer.0.as_ptr();
1008 unsafe {
1009 slice::from_raw_parts(ptr as *const u8, self.buffered_size as usize)
1010 }
1011 }
1012
1013 #[inline(always)]
1014 fn processed_buffer(&self) -> &[mem::MaybeUninit<u8>] {
1015 let ptr = self.buffer.0.as_ptr();
1016 unsafe {
1017 slice::from_raw_parts(ptr.add(self.buffered_size as usize), self.buffer.0.len() - self.buffered_size as usize)
1018 }
1019 }
1020
1021 #[inline(always)]
1022 pub fn update(&mut self, input: &[u8]) {
1024 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 }
1026
1027 #[inline(never)]
1028 fn digest_mid_sized(&self) -> u64 {
1029 let mut acc = self.acc.clone();
1030 xxh3_stateful_digest_internal(&mut acc, self.nb_stripes_acc, self.buffered_input(), self.processed_buffer(), &Self::DEFAULT_SECRET);
1031
1032 merge_accs(&mut acc, get_aligned_chunk_ref(&Self::DEFAULT_SECRET.0, SECRET_MERGEACCS_START),
1033 self.total_len.wrapping_mul(xxh64::PRIME_1))
1034 }
1035
1036 #[inline(never)]
1037 fn digest_mid_sized_128(&self) -> u128 {
1038 let mut acc = self.acc.clone();
1039 xxh3_stateful_digest_internal(&mut acc, self.nb_stripes_acc, self.buffered_input(), self.processed_buffer(), &Self::DEFAULT_SECRET);
1040
1041 let low = merge_accs(&mut acc, get_aligned_chunk_ref(&Self::DEFAULT_SECRET.0, SECRET_MERGEACCS_START),
1042 self.total_len.wrapping_mul(xxh64::PRIME_1));
1043 let high = merge_accs(&mut acc, get_aligned_chunk_ref(&Self::DEFAULT_SECRET.0,
1044 DEFAULT_SECRET_SIZE - mem::size_of_val(&self.acc) - SECRET_MERGEACCS_START),
1045 !self.total_len.wrapping_mul(xxh64::PRIME_2));
1046 ((high as u128) << 64) | (low as u128)
1047 }
1048
1049 #[inline]
1050 pub fn digest(&self) -> u64 {
1052 if self.total_len > MID_SIZE_MAX as u64 {
1055 self.digest_mid_sized()
1056 } else {
1057 xxh3_64_internal(self.buffered_input(), 0, &Self::DEFAULT_SECRET.0, xxh3_64_long_default)
1058 }
1059 }
1060
1061 #[inline]
1062 pub fn digest128(&self) -> u128 {
1064 if self.total_len > MID_SIZE_MAX as u64 {
1067 self.digest_mid_sized_128()
1068 } else {
1069 xxh3_128_internal(self.buffered_input(), 0, &Self::DEFAULT_SECRET.0, xxh3_128_long_default)
1070 }
1071 }
1072}
1073
1074impl Default for Xxh3Default {
1075 #[inline(always)]
1076 fn default() -> Self {
1077 Self::new()
1078 }
1079}
1080
1081
1082impl hash::Hasher for Xxh3Default {
1083 #[inline(always)]
1084 fn finish(&self) -> u64 {
1085 self.digest()
1086 }
1087
1088 #[inline(always)]
1089 fn write(&mut self, input: &[u8]) {
1090 self.update(input)
1091 }
1092}
1093
1094#[cfg(feature = "std")]
1095impl std::io::Write for Xxh3Default {
1096 #[inline]
1097 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
1098 self.update(buf);
1099 Ok(buf.len())
1100 }
1101
1102 #[inline]
1103 fn flush(&mut self) -> std::io::Result<()> {
1104 Ok(())
1105 }
1106}
1107
1108#[derive(Clone)]
1109pub struct Xxh3 {
1118 acc: Acc,
1119 custom_secret: Aligned64<[u8; DEFAULT_SECRET_SIZE]>,
1120 buffer: Aligned64<[mem::MaybeUninit<u8>; INTERNAL_BUFFER_SIZE]>,
1121 buffered_size: u16,
1122 nb_stripes_acc: usize,
1123 total_len: u64,
1124 seed: u64,
1125}
1126
1127impl Xxh3 {
1128 #[inline(always)]
1129 pub const fn new() -> Self {
1131 Self::with_custom_ops(0, DEFAULT_SECRET)
1132 }
1133
1134 #[inline]
1135 const fn with_custom_ops(seed: u64, secret: [u8; DEFAULT_SECRET_SIZE]) -> Self {
1137 Self {
1138 acc: INITIAL_ACC,
1139 custom_secret: Aligned64(secret),
1140 buffer: Aligned64([mem::MaybeUninit::uninit(); INTERNAL_BUFFER_SIZE]),
1141 buffered_size: 0,
1142 nb_stripes_acc: 0,
1143 total_len: 0,
1144 seed,
1145 }
1146 }
1147
1148 #[inline(always)]
1149 pub const fn with_secret(secret: [u8; DEFAULT_SECRET_SIZE]) -> Self {
1151 Self::with_custom_ops(0, secret)
1152 }
1153
1154 #[inline(always)]
1155 pub fn with_seed(seed: u64) -> Self {
1157 Self::with_custom_ops(seed, custom_default_secret(seed))
1158 }
1159
1160 #[inline(always)]
1161 pub fn reset(&mut self) {
1163 self.acc = INITIAL_ACC;
1164 self.total_len = 0;
1165 self.buffered_size = 0;
1166 self.nb_stripes_acc = 0;
1167 }
1168
1169 #[inline(always)]
1170 fn buffered_input(&self) -> &[u8] {
1171 let ptr = self.buffer.0.as_ptr();
1172 unsafe {
1173 slice::from_raw_parts(ptr as *const u8, self.buffered_size as usize)
1174 }
1175 }
1176
1177 #[inline(always)]
1178 fn processed_buffer(&self) -> &[mem::MaybeUninit<u8>] {
1179 let ptr = self.buffer.0.as_ptr();
1180 unsafe {
1181 slice::from_raw_parts(ptr.add(self.buffered_size as usize), self.buffer.0.len() - self.buffered_size as usize)
1182 }
1183 }
1184
1185 #[inline]
1186 pub fn update(&mut self, input: &[u8]) {
1188 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);
1189 }
1190
1191 #[inline(never)]
1192 fn digest_mid_sized(&self) -> u64 {
1193 let mut acc = self.acc.clone();
1194 xxh3_stateful_digest_internal(&mut acc, self.nb_stripes_acc, self.buffered_input(), self.processed_buffer(), &self.custom_secret);
1195
1196 merge_accs(&mut acc, get_aligned_chunk_ref(&self.custom_secret.0, SECRET_MERGEACCS_START),
1197 self.total_len.wrapping_mul(xxh64::PRIME_1))
1198 }
1199
1200 #[inline(never)]
1201 fn digest_mid_sized_128(&self) -> u128 {
1202 let mut acc = self.acc.clone();
1203 xxh3_stateful_digest_internal(&mut acc, self.nb_stripes_acc, self.buffered_input(), self.processed_buffer(), &self.custom_secret);
1204
1205 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));
1206 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));
1207 ((high as u128) << 64) | (low as u128)
1208 }
1209
1210 #[inline]
1211 pub fn digest(&self) -> u64 {
1213 if self.total_len > MID_SIZE_MAX as u64 {
1216 self.digest_mid_sized()
1217 } else if self.seed > 0 {
1218 xxh3_64_internal(self.buffered_input(), self.seed, &DEFAULT_SECRET, xxh3_64_long_with_seed)
1221 } else {
1222 xxh3_64_internal(self.buffered_input(), self.seed, &self.custom_secret.0, xxh3_64_long_with_secret)
1223 }
1224 }
1225
1226 #[inline]
1227 pub fn digest128(&self) -> u128 {
1229 if self.total_len > MID_SIZE_MAX as u64 {
1232 self.digest_mid_sized_128()
1233 } else if self.seed > 0 {
1234 xxh3_128_internal(self.buffered_input(), self.seed, &DEFAULT_SECRET, xxh3_128_long_with_seed)
1237 } else {
1238 xxh3_128_internal(self.buffered_input(), self.seed, &self.custom_secret.0, xxh3_128_long_with_secret)
1239 }
1240 }
1241}
1242
1243impl Default for Xxh3 {
1244 #[inline(always)]
1245 fn default() -> Self {
1246 Self::new()
1247 }
1248}
1249
1250impl core::hash::Hasher for Xxh3 {
1251 #[inline(always)]
1252 fn finish(&self) -> u64 {
1253 self.digest()
1254 }
1255
1256 #[inline(always)]
1257 fn write(&mut self, input: &[u8]) {
1258 self.update(input)
1259 }
1260}
1261
1262#[cfg(feature = "std")]
1263impl std::io::Write for Xxh3 {
1264 #[inline]
1265 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
1266 self.update(buf);
1267 Ok(buf.len())
1268 }
1269
1270 #[inline]
1271 fn flush(&mut self) -> std::io::Result<()> {
1272 Ok(())
1273 }
1274}
1275
1276#[derive(Clone, Copy)]
1277pub struct Xxh3Builder {
1279 seed: Option<u64>,
1280 secret: Option<[u8; DEFAULT_SECRET_SIZE]>,
1281}
1282
1283impl Xxh3Builder {
1284 #[inline(always)]
1285 pub const fn new() -> Self {
1287 Self {
1288 seed: None,
1289 secret: None,
1290 }
1291 }
1292
1293 #[inline(always)]
1294 pub const fn with_seed(mut self, seed: u64) -> Self {
1300 self.seed = Some(seed);
1301 self
1302 }
1303
1304 #[inline(always)]
1305 pub const fn with_secret(mut self, secret: [u8; DEFAULT_SECRET_SIZE]) -> Self {
1307 self.secret = Some(secret);
1308 self
1309 }
1310
1311 #[inline(always)]
1312 pub const fn build(self) -> Xxh3 {
1314 let (seed, secret) = match (self.seed, self.secret) {
1315 (Some(seed), Some(secret)) => (seed, secret),
1316 (Some(seed), None) => (seed, const_custom_default_secret(seed)),
1317 (None, Some(secret)) => (0, secret),
1318 (None, None) => (0, DEFAULT_SECRET),
1319 };
1320 Xxh3::with_custom_ops(seed, secret)
1321 }
1322}
1323
1324impl core::hash::BuildHasher for Xxh3Builder {
1325 type Hasher = Xxh3;
1326
1327 #[inline(always)]
1328 fn build_hasher(&self) -> Self::Hasher {
1329 self.build()
1330 }
1331}
1332
1333impl Default for Xxh3Builder {
1334 #[inline(always)]
1335 fn default() -> Self {
1336 Self::new()
1337 }
1338}
1339
1340#[derive(Clone, Copy)]
1341pub struct Xxh3DefaultBuilder;
1343
1344impl Xxh3DefaultBuilder {
1345 #[inline(always)]
1346 pub const fn new() -> Self {
1348 Self
1349 }
1350
1351 #[inline(always)]
1352 pub const fn build(self) -> Xxh3Default {
1354 Xxh3Default::new()
1355 }
1356}
1357
1358impl core::hash::BuildHasher for Xxh3DefaultBuilder {
1359 type Hasher = Xxh3Default;
1360
1361 #[inline(always)]
1362 fn build_hasher(&self) -> Self::Hasher {
1363 self.build()
1364 }
1365}
1366
1367impl Default for Xxh3DefaultBuilder {
1368 #[inline(always)]
1369 fn default() -> Self {
1370 Self::new()
1371 }
1372}
1373
1374#[inline]
1379fn xxh3_128_long_impl(input: &[u8], secret: &[u8]) -> u128 {
1380 let mut acc = INITIAL_ACC;
1381
1382 hash_long_internal_loop(&mut acc, input, secret);
1383
1384 debug_assert!(secret.len() >= mem::size_of::<Acc>() + SECRET_MERGEACCS_START);
1385 let lo = merge_accs(&mut acc, get_aligned_chunk_ref(secret, SECRET_MERGEACCS_START), (input.len() as u64).wrapping_mul(xxh64::PRIME_1));
1386 let hi = merge_accs(&mut acc,
1387 get_aligned_chunk_ref(secret, secret.len() - mem::size_of::<Acc>() - SECRET_MERGEACCS_START),
1388 !(input.len() as u64).wrapping_mul(xxh64::PRIME_2));
1389
1390 lo as u128 | (hi as u128) << 64
1391}
1392
1393#[inline(always)]
1394fn xxh3_128_9to16(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
1395 let flip_lo = (read_64le_unaligned(secret, 32) ^ read_64le_unaligned(secret, 40)).wrapping_sub(seed);
1396 let flip_hi = (read_64le_unaligned(secret, 48) ^ read_64le_unaligned(secret, 56)).wrapping_add(seed);
1397 let input_lo = read_64le_unaligned(input, 0);
1398 let mut input_hi = read_64le_unaligned(input, input.len() - 8);
1399
1400 let (mut mul_low, mut mul_high) = mul64_to128(input_lo ^ input_hi ^ flip_lo, xxh64::PRIME_1);
1401
1402 mul_low = mul_low.wrapping_add((input.len() as u64 - 1) << 54);
1403 input_hi ^= flip_hi;
1404 mul_high = mul_high.wrapping_add(
1405 input_hi.wrapping_add(mult32_to64(input_hi as u32, xxh32::PRIME_2 - 1))
1406 );
1407
1408 mul_low ^= mul_high.swap_bytes();
1409
1410 let (result_low, mut result_hi) = mul64_to128(mul_low, xxh64::PRIME_2);
1411 result_hi = result_hi.wrapping_add(
1412 mul_high.wrapping_mul(xxh64::PRIME_2)
1413 );
1414
1415 to_u128!(avalanche(result_low), avalanche(result_hi))
1416}
1417
1418#[inline(always)]
1419fn xxh3_128_4to8(input: &[u8], mut seed: u64, secret: &[u8]) -> u128 {
1420 seed ^= ((seed as u32).swap_bytes() as u64) << 32;
1421
1422 let lo = read_32le_unaligned(input, 0);
1423 let hi = read_32le_unaligned(input, input.len() - 4);
1424 let input_64 = (lo as u64).wrapping_add((hi as u64) << 32);
1425
1426 let flip = (read_64le_unaligned(secret, 16) ^ read_64le_unaligned(secret, 24)).wrapping_add(seed);
1427 let keyed = input_64 ^ flip;
1428
1429 let (mut lo, mut hi) = mul64_to128(keyed, xxh64::PRIME_1.wrapping_add((input.len() as u64) << 2));
1430
1431 hi = hi.wrapping_add(lo << 1);
1432 lo ^= hi >> 3;
1433
1434 lo = xorshift64(lo, 35).wrapping_mul(0x9FB21C651E98DF25);
1435 lo = xorshift64(lo, 28);
1436 hi = avalanche(hi);
1437
1438 lo as u128 | (hi as u128) << 64
1439}
1440
1441#[inline(always)]
1442fn xxh3_128_1to3(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
1443 let c1; let c2; let c3;
1444 unsafe {
1445 c1 = *input.get_unchecked(0);
1446 c2 = *input.get_unchecked(input.len() >> 1);
1447 c3 = *input.get_unchecked(input.len() - 1);
1448 };
1449 let input_lo = (c1 as u32) << 16 | (c2 as u32) << 24 | (c3 as u32) << 0 | (input.len() as u32) << 8;
1450 let input_hi = input_lo.swap_bytes().rotate_left(13);
1451
1452 let flip_lo = (read_32le_unaligned(secret, 0) as u64 ^ read_32le_unaligned(secret, 4) as u64).wrapping_add(seed);
1453 let flip_hi = (read_32le_unaligned(secret, 8) as u64 ^ read_32le_unaligned(secret, 12) as u64).wrapping_sub(seed);
1454 let keyed_lo = input_lo as u64 ^ flip_lo;
1455 let keyed_hi = input_hi as u64 ^ flip_hi;
1456
1457 xxh64::avalanche(keyed_lo) as u128 | (xxh64::avalanche(keyed_hi) as u128) << 64
1458}
1459
1460#[inline(always)]
1461fn xxh3_128_0to16(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
1462 if input.len() > 8 {
1463 xxh3_128_9to16(input, seed, secret)
1464 } else if input.len() >= 4 {
1465 xxh3_128_4to8(input, seed, secret)
1466 } else if input.len() > 0 {
1467 xxh3_128_1to3(input, seed, secret)
1468 } else {
1469 let flip_lo = read_64le_unaligned(secret, 64) ^ read_64le_unaligned(secret, 72);
1470 let flip_hi = read_64le_unaligned(secret, 80) ^ read_64le_unaligned(secret, 88);
1471 xxh64::avalanche(seed ^ flip_lo) as u128 | (xxh64::avalanche(seed ^ flip_hi) as u128) << 64
1472 }
1473}
1474
1475#[inline(always)]
1476fn xxh3_128_7to128(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
1477 let mut lo = (input.len() as u64).wrapping_mul(xxh64::PRIME_1);
1478 let mut hi = 0;
1479
1480 if input.len() > 32 {
1481 if input.len() > 64 {
1482 if input.len() > 96 {
1483
1484 mix32_b(&mut lo, &mut hi,
1485 get_aligned_chunk_ref(input, 48),
1486 get_aligned_chunk_ref(input, input.len() - 64),
1487 get_aligned_chunk_ref(secret, 96),
1488 seed
1489 );
1490 }
1491
1492 mix32_b(&mut lo, &mut hi,
1493 get_aligned_chunk_ref(input, 32),
1494 get_aligned_chunk_ref(input, input.len() - 48),
1495 get_aligned_chunk_ref(secret, 64),
1496 seed
1497 );
1498 }
1499
1500 mix32_b(&mut lo, &mut hi,
1501 get_aligned_chunk_ref(input, 16),
1502 get_aligned_chunk_ref(input, input.len() - 32),
1503 get_aligned_chunk_ref(secret, 32),
1504 seed
1505 );
1506 }
1507
1508 mix32_b(&mut lo, &mut hi,
1509 get_aligned_chunk_ref(input, 0),
1510 get_aligned_chunk_ref(input, input.len() - 16),
1511 get_aligned_chunk_ref(secret, 0),
1512 seed
1513 );
1514
1515 to_u128!(
1516 avalanche(
1517 lo.wrapping_add(hi)
1518 ),
1519 0u64.wrapping_sub(
1520 avalanche(
1521 lo.wrapping_mul(xxh64::PRIME_1)
1522 .wrapping_add(hi.wrapping_mul(xxh64::PRIME_4))
1523 .wrapping_add((input.len() as u64).wrapping_sub(seed).wrapping_mul(xxh64::PRIME_2))
1524 )
1525 )
1526 )
1527}
1528
1529#[inline(never)]
1530fn xxh3_128_129to240(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
1531 const START_OFFSET: usize = 3;
1532 const LAST_OFFSET: usize = 17;
1533 let nb_rounds = input.len() / 32;
1534 debug_assert!(nb_rounds >= 4);
1535
1536 let mut lo = (input.len() as u64).wrapping_mul(xxh64::PRIME_1);
1537 let mut hi = 0;
1538
1539 let mut idx = 0;
1540 while idx < 4 {
1541 let offset_idx = 32 * idx;
1542 mix32_b(&mut lo, &mut hi,
1543 get_aligned_chunk_ref(input, offset_idx),
1544 get_aligned_chunk_ref(input, offset_idx + 16),
1545 get_aligned_chunk_ref(secret, offset_idx),
1546 seed
1547 );
1548 idx = idx.wrapping_add(1);
1549 }
1550
1551 lo = avalanche(lo);
1552 hi = avalanche(hi);
1553
1554 while idx < nb_rounds {
1555 mix32_b(&mut lo, &mut hi,
1556 get_aligned_chunk_ref(input, 32 * idx),
1557 get_aligned_chunk_ref(input, (32 * idx) + 16),
1558 get_aligned_chunk_ref(secret, START_OFFSET.wrapping_add(32 * (idx - 4))),
1559 seed
1560 );
1561 idx = idx.wrapping_add(1);
1562 }
1563
1564 mix32_b(&mut lo, &mut hi,
1565 get_aligned_chunk_ref(input, input.len() - 16),
1566 get_aligned_chunk_ref(input, input.len() - 32),
1567 get_aligned_chunk_ref(secret, SECRET_SIZE_MIN - LAST_OFFSET - 16),
1568 0u64.wrapping_sub(seed)
1569 );
1570
1571 to_u128!(
1572 avalanche(
1573 lo.wrapping_add(hi)
1574 ),
1575 0u64.wrapping_sub(
1576 avalanche(
1577 lo.wrapping_mul(xxh64::PRIME_1)
1578 .wrapping_add(hi.wrapping_mul(xxh64::PRIME_4))
1579 .wrapping_add((input.len() as u64).wrapping_sub(seed).wrapping_mul(xxh64::PRIME_2))
1580 )
1581 )
1582 )
1583}
1584
1585#[inline(always)]
1586fn xxh3_128_internal(input: &[u8], seed: u64, secret: &[u8], long_hash_fn: LongHashFn128) -> u128 {
1587 debug_assert!(secret.len() >= SECRET_SIZE_MIN);
1588
1589 if input.len() <= 16 {
1590 xxh3_128_0to16(input, seed, secret)
1591 } else if input.len() <= 128 {
1592 xxh3_128_7to128(input, seed, secret)
1593 } else if input.len() <= MID_SIZE_MAX {
1594 xxh3_128_129to240(input, seed, secret)
1595 } else {
1596 long_hash_fn(input, seed, secret)
1597 }
1598}
1599
1600#[inline(never)]
1601fn xxh3_128_long_default(input: &[u8], _seed: u64, _secret: &[u8]) -> u128 {
1602 xxh3_128_long_impl(input, &DEFAULT_SECRET)
1603}
1604
1605#[inline(never)]
1606fn xxh3_128_long_with_seed(input: &[u8], seed: u64, _secret: &[u8]) -> u128 {
1607 match seed {
1608 0 => xxh3_128_long_impl(input, &DEFAULT_SECRET),
1609 seed => xxh3_128_long_impl(input, &custom_default_secret(seed)),
1610 }
1611}
1612
1613#[inline(never)]
1614fn xxh3_128_long_with_secret(input: &[u8], _seed: u64, secret: &[u8]) -> u128 {
1615 xxh3_128_long_impl(input, secret)
1616}
1617
1618#[inline]
1619pub fn xxh3_128(input: &[u8]) -> u128 {
1621 xxh3_128_internal(input, 0, &DEFAULT_SECRET, xxh3_128_long_default)
1622}
1623
1624#[inline]
1625pub fn xxh3_128_with_seed(input: &[u8], seed: u64) -> u128 {
1631 xxh3_128_internal(input, seed, &DEFAULT_SECRET, xxh3_128_long_with_seed)
1632}
1633
1634#[inline]
1635pub fn xxh3_128_with_secret(input: &[u8], secret: &[u8]) -> u128 {
1641 assert!(secret.len() >= SECRET_SIZE_MIN);
1642 xxh3_128_internal(input, 0, secret, xxh3_128_long_with_secret)
1643}
1644
1645#[inline]
1646pub fn xxh3_128_with_secret_input(input: &[u8], secret: &SecretInput<impl AsRef<[u8]>>) -> u128 {
1648 xxh3_128_internal(input, 0, secret.0.as_ref(), xxh3_128_long_with_secret)
1649}