1use core::mem;
7
8use crate::xxh32_common as xxh32;
9use crate::xxh64_common as xxh64;
10use crate::xxh3_common::*;
11
12const INITIAL_ACC: [u64; ACC_NB] = [
13 xxh32::PRIME_3 as u64, xxh64::PRIME_1, xxh64::PRIME_2, xxh64::PRIME_3,
14 xxh64::PRIME_4, xxh32::PRIME_2 as u64, xxh64::PRIME_5, xxh32::PRIME_1 as u64
15];
16
17#[inline(always)]
18const fn read_u32(input: &[u8], cursor: usize) -> u32 {
19 input[cursor] as u32 | (input[cursor + 1] as u32) << 8 | (input[cursor + 2] as u32) << 16 | (input[cursor + 3] as u32) << 24
20}
21
22
23#[inline(always)]
24const fn read_u64(input: &[u8], cursor: usize) -> u64 {
25 input[cursor] as u64
26 | (input[cursor + 1] as u64) << 8
27 | (input[cursor + 2] as u64) << 16
28 | (input[cursor + 3] as u64) << 24
29 | (input[cursor + 4] as u64) << 32
30 | (input[cursor + 5] as u64) << 40
31 | (input[cursor + 6] as u64) << 48
32 | (input[cursor + 7] as u64) << 56
33}
34
35#[inline(always)]
36const fn mult32_to64(left: u32, right: u32) -> u64 {
37 (left as u64).wrapping_mul(right as u64)
38}
39
40#[inline]
41const fn mix16_b(input: &[u8], input_offset: usize, secret: &[u8], secret_offset: usize, seed: u64) -> u64 {
42 let mut input_lo = read_u64(input, input_offset);
43 let mut input_hi = read_u64(input, input_offset + 8);
44
45 input_lo ^= read_u64(secret, secret_offset).wrapping_add(seed);
46 input_hi ^= read_u64(secret, secret_offset + 8).wrapping_sub(seed);
47
48 mul128_fold64(input_lo, input_hi)
49}
50
51#[allow(clippy::too_many_arguments)]
52#[inline]
53const fn mix32_b(mut acc: (u64, u64), input_1: &[u8], input_1_off: usize, input_2: &[u8], input_2_off: usize, secret: &[u8], secret_offset: usize, seed: u64) -> (u64, u64) {
54 acc.0 = acc.0.wrapping_add(mix16_b(input_1, input_1_off, secret, secret_offset, seed));
55 acc.0 ^= read_u64(input_2, input_2_off).wrapping_add(read_u64(input_2, input_2_off + 8));
56
57 acc.1 = acc.1.wrapping_add(mix16_b(input_2, input_2_off, secret, secret_offset + 16, seed));
58 acc.1 ^= read_u64(input_1, input_1_off).wrapping_add(read_u64(input_1, input_1_off + 8));
59
60 acc
61}
62
63#[inline(always)]
64const fn xxh3_64_9to16(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
65 let flip1 = (read_u64(secret, 24) ^ read_u64(secret, 32)).wrapping_add(seed);
66 let flip2 = (read_u64(secret, 40) ^ read_u64(secret, 48)).wrapping_sub(seed);
67
68 let input_lo = read_u64(input, 0) ^ flip1;
69 let input_hi = read_u64(input, input.len() - 8) ^ flip2;
70
71 let acc = (input.len() as u64).wrapping_add(input_lo.swap_bytes())
72 .wrapping_add(input_hi)
73 .wrapping_add(mul128_fold64(input_lo, input_hi));
74
75 avalanche(acc)
76}
77
78#[inline(always)]
79const fn xxh3_64_4to8(input: &[u8], mut seed: u64, secret: &[u8]) -> u64 {
80 seed ^= ((seed as u32).swap_bytes() as u64) << 32;
81
82 let input1 = read_u32(input, 0);
83 let input2 = read_u32(input, input.len() - 4);
84
85 let flip = (read_u64(secret, 8) ^ read_u64(secret, 16)).wrapping_sub(seed);
86 let input64 = (input2 as u64).wrapping_add((input1 as u64) << 32);
87 let keyed = input64 ^ flip;
88
89 strong_avalanche(keyed, input.len() as u64)
90}
91
92#[inline(always)]
93const fn xxh3_64_1to3(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
94 let combo = ((input[0] as u32) << 16)
95 | ((input[input.len() >> 1] as u32) << 24)
96 | (input[input.len() - 1] as u32)
97 | ((input.len() as u32) << 8);
98
99
100 let flip = ((read_u32(secret, 0) ^ read_u32(secret, 4)) as u64).wrapping_add(seed);
101 xxh64::avalanche((combo as u64) ^ flip)
102}
103
104#[inline(always)]
105const fn xxh3_64_0to16(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
106 if input.len() > 8 {
107 xxh3_64_9to16(input, seed, secret)
108 } else if input.len() >= 4 {
109 xxh3_64_4to8(input, seed, secret)
110 } else if input.len() > 0 {
111 xxh3_64_1to3(input, seed, secret)
112 } else {
113 xxh64::avalanche(seed ^ read_u64(secret, 56) ^ read_u64(secret, 64))
114 }
115}
116
117#[inline(always)]
118const fn xxh3_64_7to128(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
119 let mut acc = (input.len() as u64).wrapping_mul(xxh64::PRIME_1);
120
121 if input.len() > 32 {
122 if input.len() > 64 {
123 if input.len() > 96 {
124 acc = acc.wrapping_add(mix16_b(input, 48, secret, 96, seed));
125 acc = acc.wrapping_add(mix16_b(input, input.len()-64, secret, 112, seed));
126 }
127
128 acc = acc.wrapping_add(mix16_b(input, 32, secret, 64, seed));
129 acc = acc.wrapping_add(mix16_b(input, input.len()-48, secret, 80, seed));
130 }
131
132 acc = acc.wrapping_add(mix16_b(input, 16, secret, 32, seed));
133 acc = acc.wrapping_add(mix16_b(input, input.len()-32, secret, 48, seed));
134 }
135
136 acc = acc.wrapping_add(mix16_b(input, 0, secret, 0, seed));
137 acc = acc.wrapping_add(mix16_b(input, input.len()-16, secret, 16, seed));
138
139 avalanche(acc)
140}
141
142const fn xxh3_64_129to240(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
143 const START_OFFSET: usize = 3;
144 const LAST_OFFSET: usize = 17;
145
146 let mut acc = (input.len() as u64).wrapping_mul(xxh64::PRIME_1);
147 let nb_rounds = input.len() / 16;
148
149 let mut idx = 0;
150 while idx < 8 {
151 acc = acc.wrapping_add(mix16_b(input, 16*idx, secret, 16*idx, seed));
152 idx += 1;
153 }
154 acc = avalanche(acc);
155
156 while idx < nb_rounds {
157 acc = acc.wrapping_add(mix16_b(input, 16*idx, secret, 16*(idx-8)+START_OFFSET, seed));
158 idx += 1;
159 }
160
161 acc = acc.wrapping_add(mix16_b(input, input.len()-16, secret, SECRET_SIZE_MIN-LAST_OFFSET, seed));
162
163 avalanche(acc)
164}
165
166#[inline(always)]
167const fn mix_two_accs(acc: &[u64], acc_offset: usize, secret: &[u8], secret_offset: usize) -> u64 {
168 mul128_fold64(acc[acc_offset] ^ read_u64(secret, secret_offset),
169 acc[acc_offset + 1] ^ read_u64(secret, secret_offset + 8))
170}
171
172#[inline]
173const fn merge_accs(acc: &[u64], secret: &[u8], secret_offset: usize, mut result: u64) -> u64 {
174 let mut idx = 0;
175 while idx < 4 {
176 result = result.wrapping_add(mix_two_accs(acc, idx * 2, secret, secret_offset + idx * 16));
177 idx += 1;
178 }
179
180 avalanche(result)
181}
182
183const fn scramble_acc(mut acc: [u64; ACC_NB], secret: &[u8], secret_offset: usize) -> [u64; ACC_NB] {
184 let mut idx = 0;
185
186 while idx < ACC_NB {
187 let key = read_u64(secret, secret_offset + 8 * idx);
188 let mut acc_val = xorshift64(acc[idx], 47);
189 acc_val ^= key;
190 acc[idx] = acc_val.wrapping_mul(xxh32::PRIME_1 as u64);
191
192 idx += 1;
193 }
194
195 acc
196}
197
198const fn accumulate_512(mut acc: [u64; ACC_NB], input: &[u8], input_offset: usize, secret: &[u8], secret_offset: usize) -> [u64; ACC_NB] {
199 let mut idx = 0;
200 while idx < ACC_NB {
201 let data_val = read_u64(input, input_offset + 8 * idx);
202 let data_key = data_val ^ read_u64(secret, secret_offset + 8 * idx);
203
204 acc[idx ^ 1] = acc[idx ^ 1].wrapping_add(data_val);
205 acc[idx] = acc[idx].wrapping_add(mult32_to64((data_key & 0xFFFFFFFF) as u32, (data_key >> 32) as u32));
206
207 idx += 1;
208 }
209
210 acc
211}
212
213#[inline(always)]
214const fn accumulate_loop(mut acc: [u64; ACC_NB], input: &[u8], input_offset: usize, secret: &[u8], secret_offset: usize, nb_stripes: usize) -> [u64; ACC_NB] {
215 let mut idx = 0;
216 while idx < nb_stripes {
217 acc = accumulate_512(acc, input, input_offset + idx * STRIPE_LEN, secret, secret_offset + idx * SECRET_CONSUME_RATE);
218
219 idx += 1;
220 }
221
222 acc
223}
224
225#[inline]
226const fn hash_long_internal_loop(input: &[u8], secret: &[u8]) -> [u64; ACC_NB] {
227 let mut acc = INITIAL_ACC;
228
229 let nb_stripes = (secret.len() - STRIPE_LEN) / SECRET_CONSUME_RATE;
230 let block_len = STRIPE_LEN * nb_stripes;
231 let nb_blocks = (input.len() - 1) / block_len;
232
233 let mut idx = 0;
234 while idx < nb_blocks {
235 acc = accumulate_loop(acc, input, idx * block_len, secret, 0, nb_stripes);
236 acc = scramble_acc(acc, secret, secret.len() - STRIPE_LEN);
237
238 idx += 1;
239 }
240
241 let nb_stripes = ((input.len() - 1) - (block_len * nb_blocks)) / STRIPE_LEN;
242
243 acc = accumulate_loop(acc, input, nb_blocks * block_len, secret, 0, nb_stripes);
244 accumulate_512(acc, input, input.len() - STRIPE_LEN, secret, secret.len() - STRIPE_LEN - SECRET_LASTACC_START)
245}
246
247const fn xxh3_64_long_impl(input: &[u8], secret: &[u8]) -> u64 {
248 let acc = hash_long_internal_loop(input, secret);
249
250 merge_accs(&acc, secret, SECRET_MERGEACCS_START, (input.len() as u64).wrapping_mul(xxh64::PRIME_1))
251}
252
253#[inline(always)]
254pub const fn xxh3_64(input: &[u8]) -> u64 {
256 xxh3_64_with_seed(input, 0)
257}
258
259pub const fn xxh3_64_with_seed(input: &[u8], seed: u64) -> u64 {
261 if input.len() <= 16 {
262 xxh3_64_0to16(input, seed, &DEFAULT_SECRET)
263 } else if input.len() <= 128 {
264 xxh3_64_7to128(input, seed, &DEFAULT_SECRET)
265 } else if input.len() <= MID_SIZE_MAX {
266 xxh3_64_129to240(input, seed, &DEFAULT_SECRET)
267 } else {
268 xxh3_64_long_impl(input, &const_custom_default_secret(seed))
269 }
270}
271
272pub const fn xxh3_64_with_secret(input: &[u8], secret: &[u8; DEFAULT_SECRET_SIZE]) -> u64 {
274 if input.len() <= 16 {
275 xxh3_64_0to16(input, 0, secret)
276 } else if input.len() <= 128 {
277 xxh3_64_7to128(input, 0, secret)
278 } else if input.len() <= MID_SIZE_MAX {
279 xxh3_64_129to240(input, 0, secret)
280 } else {
281 xxh3_64_long_impl(input, secret)
282 }
283}
284
285#[inline(always)]
290const fn xxh3_128_1to3(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
291 let c1 = input[0];
292 let c2 = input[input.len() >> 1];
293 let c3 = input[input.len() - 1];
294 let input_lo = (c1 as u32) << 16 | (c2 as u32) << 24 | c3 as u32 | (input.len() as u32) << 8;
295 let input_hi = input_lo.swap_bytes().rotate_left(13);
296
297 let flip_lo = (read_u32(secret, 0) as u64 ^ read_u32(secret, 4) as u64).wrapping_add(seed);
298 let flip_hi = (read_u32(secret, 8) as u64 ^ read_u32(secret, 12) as u64).wrapping_sub(seed);
299 let keyed_lo = input_lo as u64 ^ flip_lo;
300 let keyed_hi = input_hi as u64 ^ flip_hi;
301
302 xxh64::avalanche(keyed_lo) as u128 | (xxh64::avalanche(keyed_hi) as u128) << 64
303}
304
305#[inline(always)]
306const fn xxh3_128_4to8(input: &[u8], mut seed: u64, secret: &[u8]) -> u128 {
307 seed ^= ((seed as u32).swap_bytes() as u64) << 32;
308
309 let lo = read_u32(input, 0);
310 let hi = read_u32(input, input.len() - 4);
311 let input_64 = (lo as u64).wrapping_add((hi as u64) << 32);
312
313 let flip = (read_u64(secret, 16) ^ read_u64(secret, 24)).wrapping_add(seed);
314 let keyed = input_64 ^ flip;
315
316 let (mut lo, mut hi) = mul64_to128(keyed, xxh64::PRIME_1.wrapping_add((input.len() as u64) << 2));
317
318 hi = hi.wrapping_add(lo << 1);
319 lo ^= hi >> 3;
320
321 lo = xorshift64(lo, 35).wrapping_mul(0x9FB21C651E98DF25);
322 lo = xorshift64(lo, 28);
323 hi = avalanche(hi);
324
325 lo as u128 | (hi as u128) << 64
326}
327
328#[inline(always)]
329const fn xxh3_128_9to16(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
330 let flip_lo = (read_u64(secret, 32) ^ read_u64(secret, 40)).wrapping_sub(seed);
331 let flip_hi = (read_u64(secret, 48) ^ read_u64(secret, 56)).wrapping_add(seed);
332 let input_lo = read_u64(input, 0);
333 let mut input_hi = read_u64(input, input.len() - 8);
334
335 let (mut mul_low, mut mul_high) = mul64_to128(input_lo ^ input_hi ^ flip_lo, xxh64::PRIME_1);
336
337 mul_low = mul_low.wrapping_add((input.len() as u64 - 1) << 54);
338 input_hi ^= flip_hi;
339 mul_high = mul_high.wrapping_add(
340 input_hi.wrapping_add(mult32_to64(input_hi as u32, xxh32::PRIME_2 - 1))
341 );
342
343 mul_low ^= mul_high.swap_bytes();
344
345 let (result_low, mut result_hi) = mul64_to128(mul_low, xxh64::PRIME_2);
346 result_hi = result_hi.wrapping_add(
347 mul_high.wrapping_mul(xxh64::PRIME_2)
348 );
349
350 avalanche(result_low) as u128 | (avalanche(result_hi) as u128) << 64
351}
352
353#[inline(always)]
354const fn xxh3_128_0to16(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
355 if input.len() > 8 {
356 xxh3_128_9to16(input, seed, secret)
357 } else if input.len() >= 4 {
358 xxh3_128_4to8(input, seed, secret)
359 } else if input.len() > 0 {
360 xxh3_128_1to3(input, seed, secret)
361 } else {
362 let flip_lo = read_u64(secret, 64) ^ read_u64(secret, 72);
363 let flip_hi = read_u64(secret, 80) ^ read_u64(secret, 88);
364 xxh64::avalanche(seed ^ flip_lo) as u128 | (xxh64::avalanche(seed ^ flip_hi) as u128) << 64
365 }
366}
367
368#[inline(always)]
369const fn xxh3_128_7to128(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
370 let mut acc = ((input.len() as u64).wrapping_mul(xxh64::PRIME_1), 0u64);
371
372 if input.len() > 32 {
373 if input.len() > 64 {
374 if input.len() > 96 {
375 acc = mix32_b(acc, input, 48, input, input.len() - 64, secret, 96, seed);
376 }
377
378 acc = mix32_b(acc, input, 32, input, input.len() - 48, secret, 64, seed);
379 }
380
381 acc = mix32_b(acc, input, 16, input, input.len() - 32, secret, 32, seed);
382 }
383
384 acc = mix32_b(acc, input, 0, input, input.len() - 16, secret, 0, seed);
385
386 let result_lo = acc.0.wrapping_add(acc.1);
387 let result_hi = acc.0.wrapping_mul(xxh64::PRIME_1)
388 .wrapping_add(acc.1.wrapping_mul(xxh64::PRIME_4))
389 .wrapping_add((input.len() as u64).wrapping_sub(seed).wrapping_mul(xxh64::PRIME_2));
390
391 avalanche(result_lo) as u128 | (0u64.wrapping_sub(avalanche(result_hi)) as u128) << 64
392}
393
394#[inline(never)]
395const fn xxh3_128_129to240(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
396 const START_OFFSET: usize = 3;
397 const LAST_OFFSET: usize = 17;
398 let nb_rounds = input.len() / 32;
399
400 let mut acc = ((input.len() as u64).wrapping_mul(xxh64::PRIME_1), 0u64);
401
402 let mut idx = 0;
403 while idx < 4 {
404 acc = mix32_b(acc, input, 32 * idx, input, (32 * idx) + 16, secret, 32 * idx, seed);
405 idx += 1;
406 }
407
408 acc.0 = avalanche(acc.0);
409 acc.1 = avalanche(acc.1);
410
411 while idx < nb_rounds {
412 acc = mix32_b(acc, input, 32 * idx, input, (32 * idx) + 16, secret, START_OFFSET.wrapping_add(32 * (idx - 4)), seed);
413 idx += 1;
414 }
415
416 acc = mix32_b(acc, input, input.len() - 16, input, input.len() - 32, secret, SECRET_SIZE_MIN - LAST_OFFSET - 16, 0u64.wrapping_sub(seed));
417 let result_lo = acc.0.wrapping_add(acc.1);
418 let result_hi = acc.0.wrapping_mul(xxh64::PRIME_1)
419 .wrapping_add(acc.1.wrapping_mul(xxh64::PRIME_4))
420 .wrapping_add((input.len() as u64).wrapping_sub(seed).wrapping_mul(xxh64::PRIME_2));
421
422 avalanche(result_lo) as u128 | 0u128.wrapping_sub(avalanche(result_hi) as u128) << 64
423}
424
425const fn xxh3_128_long_impl(input: &[u8], secret: &[u8]) -> u128 {
426 let acc = hash_long_internal_loop(input, secret);
427
428 let lo = merge_accs(&acc, secret, SECRET_MERGEACCS_START, (input.len() as u64).wrapping_mul(xxh64::PRIME_1));
429 let hi = merge_accs(&acc,
430 secret, secret.len() - ACC_NB * mem::size_of::<u64>() - SECRET_MERGEACCS_START,
431 !(input.len() as u64).wrapping_mul(xxh64::PRIME_2));
432
433 lo as u128 | (hi as u128) << 64
434}
435
436#[inline(always)]
437pub const fn xxh3_128(input: &[u8]) -> u128 {
439 xxh3_128_with_seed(input, 0)
440}
441
442pub const fn xxh3_128_with_seed(input: &[u8], seed: u64) -> u128 {
444 if input.len() <= 16 {
445 xxh3_128_0to16(input, seed, &DEFAULT_SECRET)
446 } else if input.len() <= 128 {
447 xxh3_128_7to128(input, seed, &DEFAULT_SECRET)
448 } else if input.len() <= MID_SIZE_MAX {
449 xxh3_128_129to240(input, seed, &DEFAULT_SECRET)
450 } else {
451 xxh3_128_long_impl(input, &const_custom_default_secret(seed))
452 }
453}
454
455pub const fn xxh3_128_with_secret(input: &[u8], secret: &[u8; DEFAULT_SECRET_SIZE]) -> u128 {
457 if input.len() <= 16 {
458 xxh3_128_0to16(input, 0, secret)
459 } else if input.len() <= 128 {
460 xxh3_128_7to128(input, 0, secret)
461 } else if input.len() <= MID_SIZE_MAX {
462 xxh3_128_129to240(input, 0, secret)
463 } else {
464 xxh3_128_long_impl(input, secret)
465 }
466}
467
468#[inline(always)]
470pub const fn const_custom_default_secret(seed: u64) -> [u8; DEFAULT_SECRET_SIZE] {
474 if seed == 0 {
475 return DEFAULT_SECRET;
476 }
477
478 #[inline(always)]
479 const fn read_u64(input: &[u8], cursor: usize) -> u64 {
480 input[cursor] as u64
481 | (input[cursor + 1] as u64) << 8
482 | (input[cursor + 2] as u64) << 16
483 | (input[cursor + 3] as u64) << 24
484 | (input[cursor + 4] as u64) << 32
485 | (input[cursor + 5] as u64) << 40
486 | (input[cursor + 6] as u64) << 48
487 | (input[cursor + 7] as u64) << 56
488 }
489
490 let mut idx = 0;
491 let mut result = [0; DEFAULT_SECRET_SIZE];
492 const NB_ROUNDS: usize = DEFAULT_SECRET_SIZE / 16;
493
494 while idx < NB_ROUNDS {
495 let lo = read_u64(&DEFAULT_SECRET, idx * 16).wrapping_add(seed).to_le_bytes();
496 let hi = read_u64(&DEFAULT_SECRET, idx * 16 + 8).wrapping_sub(seed).to_le_bytes();
497
498 result[idx * 16] = lo[0];
499 result[idx * 16 + 1] = lo[1];
500 result[idx * 16 + 2] = lo[2];
501 result[idx * 16 + 3] = lo[3];
502 result[idx * 16 + 4] = lo[4];
503 result[idx * 16 + 5] = lo[5];
504 result[idx * 16 + 6] = lo[6];
505 result[idx * 16 + 7] = lo[7];
506
507 result[idx * 16 + 8] = hi[0];
508 result[idx * 16 + 8 + 1] = hi[1];
509 result[idx * 16 + 8 + 2] = hi[2];
510 result[idx * 16 + 8 + 3] = hi[3];
511 result[idx * 16 + 8 + 4] = hi[4];
512 result[idx * 16 + 8 + 5] = hi[5];
513 result[idx * 16 + 8 + 6] = hi[6];
514 result[idx * 16 + 8 + 7] = hi[7];
515
516 idx += 1;
517 }
518
519 result
520}