Coverage Report

Created: 2026-07-16 07:17

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