Skip to main content

xxhash_rust/
xxh3.rs

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
6use 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// 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)]
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
58///Secret validation wrapper
59pub struct SecretInput<T>(T);
60
61impl<T: AsRef<[u8]>> SecretInput<T> {
62    #[inline(always)]
63    ///Creates secret input validating its length is sufficient
64    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    ///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    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// 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]
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
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
112macro_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)]
175//Inputs are two chunks of unaligned u64
176//Secret are two chunks of unaligned (u64, u64)
177fn 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//For some dumb reasons vld1q_u8 is unstable for arm
261#[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    //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"))))]
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    //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"))))]
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            //Miri complains about it for dumb reason so for not turn off prefetch
585            //_mm_prefetch(input as _, 320);
586
587            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    //last partial block
607    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    //last stripe
614    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]
821///Returns 64bit hash for provided input.
822pub fn xxh3_64(input: &[u8]) -> u64 {
823    xxh3_64_internal(input, 0, &DEFAULT_SECRET, xxh3_64_long_default)
824}
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`
832pub 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]
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
842pub 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]
848///Returns 64bit hash for provided input using custom secret.
849pub 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]
861//Internal function shared between Xxh3 and Xxh3Default
862fn 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
877//Internal function shared between Xxh3 and Xxh3Default
878fn 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)]
942//Internal function shared between Xxh3 and Xxh3Default
943fn 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)]
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
973pub 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    ///Creates new hasher with default settings
986    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    ///Resets state
998    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    ///Hashes provided chunk
1023    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    ///Computes hash.
1051    pub fn digest(&self) -> u64 {
1052        //Separating digest mid sized allows us to inline this function, which benefits
1053        //code generation when hashing fixed size types and/or if the seed is known.
1054        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    ///Computes hash as 128bit integer.
1063    pub fn digest128(&self) -> u128 {
1064        //Separating digest mid sized allows us to inline this function, which benefits
1065        //code generation when hashing fixed size types and/or if the seed is known.
1066        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)]
1109///XXH3 Streaming algorithm
1110///
1111///Internal state uses rather large buffers, therefore it might be beneficial
1112///to store hasher on heap rather than stack.
1113///Implementation makes no attempts at that, leaving choice entirely to user.
1114///
1115///Note that it is better to use [Xxh3Default](struct.Xxh3Default.html) in hash maps
1116///due to Rust hash interface which requires to create new instance of hasher every time.
1117pub 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    ///Creates new hasher with default settings
1130    pub const fn new() -> Self {
1131        Self::with_custom_ops(0, DEFAULT_SECRET)
1132    }
1133
1134    #[inline]
1135    ///Creates new hasher with all options.
1136    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    ///Creates new hasher with custom seed.
1150    pub const fn with_secret(secret: [u8; DEFAULT_SECRET_SIZE]) -> Self {
1151        Self::with_custom_ops(0, secret)
1152    }
1153
1154    #[inline(always)]
1155    ///Creates new hasher with custom seed.
1156    pub fn with_seed(seed: u64) -> Self {
1157        Self::with_custom_ops(seed, custom_default_secret(seed))
1158    }
1159
1160    #[inline(always)]
1161    ///Resets state
1162    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    ///Hashes provided chunk
1187    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    ///Computes hash.
1212    pub fn digest(&self) -> u64 {
1213        //Separating digest mid sized allows us to inline this function, which benefits
1214        //code generation when hashing fixed size types and/or if the seed is known.
1215        if self.total_len > MID_SIZE_MAX as u64 {
1216            self.digest_mid_sized()
1217        } else if self.seed > 0 {
1218            //Technically we should not need to use it.
1219            //But in all actuality original xxh3 implementation uses default secret for input with size less or equal to MID_SIZE_MAX
1220            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    ///Computes hash as 128bit integer.
1228    pub fn digest128(&self) -> u128 {
1229        //Separating digest mid sized allows us to inline this function, which benefits
1230        //code generation when hashing fixed size types and/or if the seed is known.
1231        if self.total_len > MID_SIZE_MAX as u64 {
1232            self.digest_mid_sized_128()
1233        } else if self.seed > 0 {
1234            //Technically we should not need to use it.
1235            //But in all actuality original xxh3 implementation uses default secret for input with size less or equal to MID_SIZE_MAX
1236            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)]
1277///Hash builder for `Xxh3`
1278pub struct Xxh3Builder {
1279    seed: Option<u64>,
1280    secret: Option<[u8; DEFAULT_SECRET_SIZE]>,
1281}
1282
1283impl Xxh3Builder {
1284    #[inline(always)]
1285    ///Creates new instance with default params.
1286    pub const fn new() -> Self {
1287        Self {
1288            seed: None,
1289            secret: None,
1290        }
1291    }
1292
1293    #[inline(always)]
1294    ///Sets `seed` for `xxh3` algorithm
1295    ///
1296    ///This method overrides default secret with custom based on seed.
1297    ///
1298    ///To counter it, override secret using [Xxh3Builder::with_secret]
1299    pub const fn with_seed(mut self, seed: u64) -> Self {
1300        self.seed = Some(seed);
1301        self
1302    }
1303
1304    #[inline(always)]
1305    ///Sets custom `secret` for `xxh3` algorithm
1306    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    ///Creates `Xxh3` instance
1313    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)]
1341///Hash builder for `Xxh3Default`
1342pub struct Xxh3DefaultBuilder;
1343
1344impl Xxh3DefaultBuilder {
1345    #[inline(always)]
1346    ///Creates new instance with default params.
1347    pub const fn new() -> Self {
1348        Self
1349    }
1350
1351    #[inline(always)]
1352    ///Creates `Xxh3` instance
1353    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//
1375//128bit
1376//
1377
1378#[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]
1619///Returns 128bit hash for provided input.
1620pub fn xxh3_128(input: &[u8]) -> u128 {
1621    xxh3_128_internal(input, 0, &DEFAULT_SECRET, xxh3_128_long_default)
1622}
1623
1624#[inline]
1625///Returns 128 hash for provided input using seed.
1626///
1627///Note: While overhead of deriving new secret from provided seed is low,
1628///it would more efficient to generate secret at compile time using special function
1629///`const_custom_default_secret` from `const_xxh3`
1630pub 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]
1635///Returns 128 hash for provided input using custom secret.
1636///
1637///This function panics if `secret` doesn't fit minimum required secret size.
1638///
1639///Prefer to use [SecretInput] with [xxh3_128_with_secret_input] to avoid assert
1640pub 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]
1646///Returns 128 hash for provided input using custom secret.
1647pub 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}