1use crate::Checksum;
2
3pub(crate) const PRIME32_1: u64 = 0x9E3779B1;
8pub(crate) const PRIME32_3: u64 = 0xC2B2AE3D;
9
10const PRIME64_1: u64 = 0x9E3779B185EBCA87;
11const PRIME64_2: u64 = 0xC2B2AE3D27D4EB4F;
12const PRIME64_5: u64 = 0x27D4EB2F165667C5;
13
14pub(crate) const STRIPE_LEN: usize = 64;
15pub(crate) const SECRET_CONSUME_RATE: usize = 8;
16pub(crate) const ACC_NB: usize = 8;
17const SECRET_MERGEACCS_START: usize = 11;
18const SECRET_LASTACC_START: usize = 7;
19const MID_SIZE_MAX: usize = 240;
20const SECRET_SIZE_MIN: usize = 136;
21const DEFAULT_SECRET_SIZE: usize = 192;
22const STRIPES_PER_BLOCK: usize = (DEFAULT_SECRET_SIZE - STRIPE_LEN) / SECRET_CONSUME_RATE;
23const INTERNAL_BUFFER_SIZE: usize = 256;
25const INTERNAL_BUFFER_STRIPES: usize = INTERNAL_BUFFER_SIZE / STRIPE_LEN;
27const LAST_STRIPE_SECRET_OFFSET: usize = DEFAULT_SECRET_SIZE - STRIPE_LEN - SECRET_LASTACC_START;
29
30const DEFAULT_SECRET: [u8; 192] = [
32 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, 0xde, 0xd4, 0x6d,
33 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0,
34 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0,
35 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c, 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b,
36 0x1b, 0x53, 0x2e, 0xa3, 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac,
37 0xd8, 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d, 0x8a, 0x51,
38 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, 0xea, 0xc5, 0xac, 0x83, 0x34,
39 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49,
40 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e, 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8,
41 0xd1, 0x7a, 0xd0, 0x31, 0xce, 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b,
42 0x40, 0x7e,
43];
44
45const INITIAL_ACC: [u64; ACC_NB] = [
46 PRIME32_3,
47 PRIME64_1,
48 PRIME64_2,
49 0x165667B19E3779F9, 0x85EBCA77C2B2AE63, 0x85EBCA77, PRIME64_5,
53 PRIME32_1,
54];
55
56#[inline]
60const fn read_64le(data: &[u8], offset: usize) -> u64 {
61 let (_, rest) = data.split_at(offset);
62 let arr = match rest.split_first_chunk::<8>() {
63 Some((arr, _)) => arr,
64 None => panic!("read_64le: out of bounds"),
65 };
66 u64::from_le_bytes(*arr)
67}
68
69#[inline]
70const fn read_32le(data: &[u8], offset: usize) -> u32 {
71 let (_, rest) = data.split_at(offset);
72 let arr = match rest.split_first_chunk::<4>() {
73 Some((arr, _)) => arr,
74 None => panic!("read_32le: out of bounds"),
75 };
76 u32::from_le_bytes(*arr)
77}
78#[inline]
79const fn xorshift64(value: u64, shift: u64) -> u64 {
80 value ^ (value >> shift)
81}
82
83#[inline]
84const fn avalanche(mut h: u64) -> u64 {
85 h = xorshift64(h, 37);
86 h = h.wrapping_mul(0x165667919E3779F9);
87 xorshift64(h, 32)
88}
89
90const fn xxh64_avalanche(mut h: u64) -> u64 {
92 h ^= h >> 33;
93 h = h.wrapping_mul(PRIME64_2);
94 h ^= h >> 29;
95 h = h.wrapping_mul(0x165667B19E3779F9);
96 h ^= h >> 32;
97 h
98}
99
100#[inline]
101const fn strong_avalanche(mut value: u64, len: u64) -> u64 {
102 value ^= value.rotate_left(49) ^ value.rotate_left(24);
103 value = value.wrapping_mul(0x9FB21C651E98DF25);
104 value ^= (value >> 35).wrapping_add(len);
105 value = value.wrapping_mul(0x9FB21C651E98DF25);
106 xorshift64(value, 28)
107}
108
109#[inline]
110const fn mul128_fold64(l: u64, r: u64) -> u64 {
111 let p = (l as u128).wrapping_mul(r as u128);
112 (p as u64) ^ ((p >> 64) as u64)
113}
114
115#[inline]
116const fn mult32_to64(left: u32, right: u32) -> u64 {
117 (left as u64).wrapping_mul(right as u64)
118}
119
120#[inline]
121const fn mix16b(input: &[u8], input_offset: usize, secret: &[u8], secret_offset: usize, seed: u64) -> u64 {
122 let mut input_lo = read_64le(input, input_offset);
123 let mut input_hi = read_64le(input, input_offset + 8);
124
125 input_lo ^= read_64le(secret, secret_offset).wrapping_add(seed);
126 input_hi ^= read_64le(secret, secret_offset + 8).wrapping_sub(seed);
127
128 mul128_fold64(input_lo, input_hi)
129}
130
131#[inline]
132const fn mix_two_accs(acc: &[u64; ACC_NB], offset: usize, secret: &[u8], sec_off: usize) -> u64 {
133 mul128_fold64(
134 acc[offset] ^ read_64le(secret, sec_off),
135 acc[offset + 1] ^ read_64le(secret, sec_off + 8),
136 )
137}
138
139#[inline]
140const fn merge_accs(acc: &[u64; ACC_NB], secret: &[u8], start_offset: usize, mut result: u64) -> u64 {
141 let mut i = 0;
142 while i < 4 {
143 result = result.wrapping_add(mix_two_accs(acc, i * 2, secret, start_offset + i * 16));
144 i += 1;
145 }
146 avalanche(result)
147}
148
149#[inline]
154const fn accumulate_512_scalar(
155 acc: &mut [u64; ACC_NB],
156 input: &[u8],
157 input_off: usize,
158 secret: &[u8],
159 secret_off: usize,
160) {
161 let mut i = 0;
162 while i < ACC_NB {
163 let data_val = read_64le(input, input_off + i * 8);
164 let data_key = data_val ^ read_64le(secret, secret_off + i * 8);
165 acc[i ^ 1] = acc[i ^ 1].wrapping_add(data_val);
166 acc[i] = acc[i].wrapping_add(mult32_to64((data_key & 0xFFFFFFFF) as u32, (data_key >> 32) as u32));
167 i += 1;
168 }
169}
170
171#[inline]
172const fn accumulate_loop_scalar(
173 acc: &mut [u64; ACC_NB],
174 input: &[u8],
175 input_off: usize,
176 secret: &[u8],
177 secret_off: usize,
178 nb_stripes: usize,
179) {
180 let mut i = 0;
181 while i < nb_stripes {
182 accumulate_512_scalar(
183 acc,
184 input,
185 input_off + i * STRIPE_LEN,
186 secret,
187 secret_off + i * SECRET_CONSUME_RATE,
188 );
189 i += 1;
190 }
191}
192
193#[inline]
194const fn scramble_acc_scalar(acc: &mut [u64; ACC_NB], secret: &[u8], secret_off: usize) {
195 let mut i = 0;
196 while i < ACC_NB {
197 let key = read_64le(secret, secret_off + i * 8);
198 let mut val = xorshift64(acc[i], 47);
199 val ^= key;
200 acc[i] = val.wrapping_mul(PRIME32_1);
201 i += 1;
202 }
203}
204
205#[inline]
206const fn hash_long_internal_loop(acc: &mut [u64; ACC_NB], input: &[u8], secret: &[u8]) {
207 let nb_stripes = STRIPES_PER_BLOCK;
208 let block_len = STRIPE_LEN * nb_stripes;
209 let nb_blocks = (input.len() - 1) / block_len;
210
211 let mut i = 0;
212 while i < nb_blocks {
213 accumulate_loop_scalar(acc, input, i * block_len, secret, 0, nb_stripes);
214 scramble_acc_scalar(acc, secret, secret.len() - STRIPE_LEN);
215 i += 1;
216 }
217
218 let nb_stripes = ((input.len() - 1) - (block_len * nb_blocks)) / STRIPE_LEN;
220 accumulate_loop_scalar(acc, input, nb_blocks * block_len, secret, 0, nb_stripes);
221
222 let last_stripe_start = input.len() - STRIPE_LEN;
224 let last_secret_off = secret.len() - STRIPE_LEN - SECRET_LASTACC_START;
225 accumulate_512_scalar(acc, input, last_stripe_start, secret, last_secret_off);
226}
227
228#[inline]
231fn hash_long_internal_loop_dispatch(acc: &mut [u64; ACC_NB], input: &[u8], secret: &[u8]) {
232 let nb_stripes = STRIPES_PER_BLOCK;
233 let block_len = STRIPE_LEN * nb_stripes;
234 let nb_blocks = (input.len() - 1) / block_len;
235
236 let mut i = 0;
237 while i < nb_blocks {
238 accumulate_loop(acc, input, i * block_len, secret, 0, nb_stripes);
239 scramble_acc(acc, secret, secret.len() - STRIPE_LEN);
240 i += 1;
241 }
242
243 let nb_stripes = ((input.len() - 1) - (block_len * nb_blocks)) / STRIPE_LEN;
245 accumulate_loop(acc, input, nb_blocks * block_len, secret, 0, nb_stripes);
246
247 let last_stripe_start = input.len() - STRIPE_LEN;
249 accumulate_512(acc, input, last_stripe_start, secret, LAST_STRIPE_SECRET_OFFSET);
250}
251
252#[inline]
255fn consume_stripes(
256 acc: &mut [u64; ACC_NB],
257 nb_stripes_so_far: &mut usize,
258 input: &[u8],
259 mut nb_stripes: usize,
260 secret: &[u8],
261) {
262 let mut pos = 0;
263 let mut secret_off = *nb_stripes_so_far * SECRET_CONSUME_RATE;
264
265 if nb_stripes >= STRIPES_PER_BLOCK - *nb_stripes_so_far {
267 let mut nb_this = STRIPES_PER_BLOCK - *nb_stripes_so_far;
268 loop {
269 accumulate_loop(acc, input, pos, secret, secret_off, nb_this);
270 scramble_acc(acc, secret, secret.len() - STRIPE_LEN);
271 pos += nb_this * STRIPE_LEN;
272 nb_stripes -= nb_this;
273 nb_this = STRIPES_PER_BLOCK;
274 secret_off = 0;
275 if nb_stripes < STRIPES_PER_BLOCK {
276 break;
277 }
278 }
279 *nb_stripes_so_far = 0;
280 }
281
282 if nb_stripes > 0 {
284 accumulate_loop(acc, input, pos, secret, secret_off, nb_stripes);
285 *nb_stripes_so_far += nb_stripes;
286 }
287}
288
289const fn custom_default_secret_scalar(seed: u64) -> [u8; DEFAULT_SECRET_SIZE] {
294 let mut result = [0u8; DEFAULT_SECRET_SIZE];
295 let nb_rounds = DEFAULT_SECRET_SIZE / 16;
296 let mut i = 0;
297 while i < nb_rounds {
298 let lo = read_64le(&DEFAULT_SECRET, i * 16).wrapping_add(seed);
299 let hi = read_64le(&DEFAULT_SECRET, i * 16 + 8).wrapping_sub(seed);
300 let lo_bytes = lo.to_le_bytes();
301 let hi_bytes = hi.to_le_bytes();
302 let mut j = 0;
303 while j < 8 {
304 result[i * 16 + j] = lo_bytes[j];
305 result[i * 16 + 8 + j] = hi_bytes[j];
306 j += 1;
307 }
308 i += 1;
309 }
310 result
311}
312
313#[inline]
322#[allow(unreachable_code, unused_variables)]
323fn accumulate_512(acc: &mut [u64; ACC_NB], input: &[u8], input_off: usize, secret: &[u8], secret_off: usize) {
324 #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
325 {
326 crate::xxh3_wasm_simd128::accumulate_512(acc, input, input_off, secret, secret_off);
327 return;
328 }
329
330 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
331 {
332 unsafe { crate::xxh3_neon::accumulate_512(acc, input, input_off, secret, secret_off) };
333 return;
334 }
335
336 #[cfg(all(target_arch = "x86_64", feature = "std"))]
337 {
338 match crate::x86_dispatch::level() {
339 crate::x86_dispatch::Level::Avx512 => {
340 unsafe { crate::xxh3_avx512::accumulate_512(acc, input, input_off, secret, secret_off) };
341 return;
342 }
343 crate::x86_dispatch::Level::Avx2 => {
344 unsafe { crate::xxh3_avx2::accumulate_512(acc, input, input_off, secret, secret_off) };
345 return;
346 }
347 crate::x86_dispatch::Level::Scalar => {}
348 }
349 }
350
351 #[cfg(all(target_arch = "x86_64", not(feature = "std"), target_feature = "avx512f"))]
352 {
353 unsafe { crate::xxh3_avx512::accumulate_512(acc, input, input_off, secret, secret_off) };
354 return;
355 }
356
357 #[cfg(all(target_arch = "x86_64", not(feature = "std"), target_feature = "avx2"))]
358 {
359 unsafe { crate::xxh3_avx2::accumulate_512(acc, input, input_off, secret, secret_off) };
360 return;
361 }
362
363 accumulate_512_scalar(acc, input, input_off, secret, secret_off);
364}
365
366#[inline]
367#[allow(unreachable_code, unused_variables)]
368fn accumulate_loop(
369 acc: &mut [u64; ACC_NB],
370 input: &[u8],
371 input_off: usize,
372 secret: &[u8],
373 secret_off: usize,
374 nb_stripes: usize,
375) {
376 #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
377 {
378 crate::xxh3_wasm_simd128::accumulate_loop(acc, input, input_off, secret, secret_off, nb_stripes);
379 return;
380 }
381
382 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
383 {
384 unsafe {
385 crate::xxh3_neon::accumulate_loop(acc, input, input_off, secret, secret_off, nb_stripes);
386 }
387 return;
388 }
389
390 #[cfg(all(target_arch = "x86_64", feature = "std"))]
391 {
392 match crate::x86_dispatch::level() {
393 crate::x86_dispatch::Level::Avx512 => {
394 unsafe {
395 crate::xxh3_avx512::accumulate_loop(acc, input, input_off, secret, secret_off, nb_stripes);
396 }
397 return;
398 }
399 crate::x86_dispatch::Level::Avx2 => {
400 unsafe {
401 crate::xxh3_avx2::accumulate_loop(acc, input, input_off, secret, secret_off, nb_stripes);
402 }
403 return;
404 }
405 crate::x86_dispatch::Level::Scalar => {}
406 }
407 }
408
409 #[cfg(all(target_arch = "x86_64", not(feature = "std"), target_feature = "avx512f"))]
410 {
411 unsafe {
412 crate::xxh3_avx512::accumulate_loop(acc, input, input_off, secret, secret_off, nb_stripes);
413 }
414 return;
415 }
416
417 #[cfg(all(target_arch = "x86_64", not(feature = "std"), target_feature = "avx2"))]
418 {
419 unsafe {
420 crate::xxh3_avx2::accumulate_loop(acc, input, input_off, secret, secret_off, nb_stripes);
421 }
422 return;
423 }
424
425 accumulate_loop_scalar(acc, input, input_off, secret, secret_off, nb_stripes);
426}
427
428#[inline]
429#[allow(unreachable_code, unused_variables)]
430fn scramble_acc(acc: &mut [u64; ACC_NB], secret: &[u8], secret_off: usize) {
431 #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
432 {
433 crate::xxh3_wasm_simd128::scramble_acc(acc, secret, secret_off);
434 return;
435 }
436
437 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
438 {
439 unsafe { crate::xxh3_neon::scramble_acc(acc, secret, secret_off) };
440 return;
441 }
442
443 #[cfg(all(target_arch = "x86_64", feature = "std"))]
444 {
445 match crate::x86_dispatch::level() {
446 crate::x86_dispatch::Level::Avx512 => {
447 unsafe { crate::xxh3_avx512::scramble_acc(acc, secret, secret_off) };
448 return;
449 }
450 crate::x86_dispatch::Level::Avx2 => {
451 unsafe { crate::xxh3_avx2::scramble_acc(acc, secret, secret_off) };
452 return;
453 }
454 crate::x86_dispatch::Level::Scalar => {}
455 }
456 }
457
458 #[cfg(all(target_arch = "x86_64", not(feature = "std"), target_feature = "avx512f"))]
459 {
460 unsafe { crate::xxh3_avx512::scramble_acc(acc, secret, secret_off) };
461 return;
462 }
463
464 #[cfg(all(target_arch = "x86_64", not(feature = "std"), target_feature = "avx2"))]
465 {
466 unsafe { crate::xxh3_avx2::scramble_acc(acc, secret, secret_off) };
467 return;
468 }
469
470 scramble_acc_scalar(acc, secret, secret_off);
471}
472
473const fn xxh3_64_1to3(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
478 let len = input.len();
479 let c1 = input[0] as u32;
480 let c2 = input[len >> 1] as u32;
481 let c3 = input[len - 1] as u32;
482 let combined = (c1 << 16) | (c2 << 24) | (c3) | ((len as u32) << 8);
483 let flip = ((read_32le(secret, 0) ^ read_32le(secret, 4)) as u64).wrapping_add(seed);
484 xxh64_avalanche((combined as u64) ^ flip)
485}
486
487const fn xxh3_64_4to8(input: &[u8], mut seed: u64, secret: &[u8]) -> u64 {
488 seed ^= ((seed as u32).swap_bytes() as u64) << 32;
489 let len = input.len();
490 let input1 = read_32le(input, 0);
491 let input2 = read_32le(input, len - 4);
492 let flip = (read_64le(secret, 8) ^ read_64le(secret, 16)).wrapping_sub(seed);
493 let input64 = (input2 as u64).wrapping_add((input1 as u64) << 32);
494 strong_avalanche(input64 ^ flip, len as u64)
495}
496
497const fn xxh3_64_9to16(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
498 let len = input.len();
499 let flip1 = (read_64le(secret, 24) ^ read_64le(secret, 32)).wrapping_add(seed);
500 let flip2 = (read_64le(secret, 40) ^ read_64le(secret, 48)).wrapping_sub(seed);
501 let input_lo = read_64le(input, 0) ^ flip1;
502 let input_hi = read_64le(input, len - 8) ^ flip2;
503 let acc = (len as u64)
504 .wrapping_add(input_lo.swap_bytes())
505 .wrapping_add(input_hi)
506 .wrapping_add(mul128_fold64(input_lo, input_hi));
507 avalanche(acc)
508}
509
510const fn xxh3_64_0to16(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
511 if input.len() > 8 {
512 xxh3_64_9to16(input, seed, secret)
513 } else if input.len() >= 4 {
514 xxh3_64_4to8(input, seed, secret)
515 } else if !input.is_empty() {
516 xxh3_64_1to3(input, seed, secret)
517 } else {
518 xxh64_avalanche(seed ^ read_64le(secret, 56) ^ read_64le(secret, 64))
519 }
520}
521
522const fn xxh3_64_7to128(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
527 let len = input.len();
528 let mut acc = (len as u64).wrapping_mul(PRIME64_1);
529
530 if len > 32 {
531 if len > 64 {
532 if len > 96 {
533 acc = acc.wrapping_add(mix16b(input, 48, secret, 96, seed));
534 acc = acc.wrapping_add(mix16b(input, len - 64, secret, 112, seed));
535 }
536 acc = acc.wrapping_add(mix16b(input, 32, secret, 64, seed));
537 acc = acc.wrapping_add(mix16b(input, len - 48, secret, 80, seed));
538 }
539 acc = acc.wrapping_add(mix16b(input, 16, secret, 32, seed));
540 acc = acc.wrapping_add(mix16b(input, len - 32, secret, 48, seed));
541 }
542
543 acc = acc.wrapping_add(mix16b(input, 0, secret, 0, seed));
544 acc = acc.wrapping_add(mix16b(input, len - 16, secret, 16, seed));
545
546 avalanche(acc)
547}
548
549const fn xxh3_64_129to240(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
554 let len = input.len();
555 let mut acc = (len as u64).wrapping_mul(PRIME64_1);
556 let nb_rounds = len / 16;
557
558 let mut i = 0;
559 while i < 8 {
560 acc = acc.wrapping_add(mix16b(input, i * 16, secret, i * 16, seed));
561 i += 1;
562 }
563 acc = avalanche(acc);
564
565 i = 8;
566 while i < nb_rounds {
567 acc = acc.wrapping_add(mix16b(input, i * 16, secret, (i - 8) * 16 + 3, seed));
568 i += 1;
569 }
570
571 acc = acc.wrapping_add(mix16b(input, len - 16, secret, SECRET_SIZE_MIN - 17, seed));
572
573 avalanche(acc)
574}
575
576const fn xxh3_64_long_scalar(input: &[u8], secret: &[u8]) -> u64 {
582 let mut acc = INITIAL_ACC;
583 hash_long_internal_loop(&mut acc, input, secret);
584 merge_accs(
585 &acc,
586 secret,
587 SECRET_MERGEACCS_START,
588 (input.len() as u64).wrapping_mul(PRIME64_1),
589 )
590}
591
592fn xxh3_64_long(input: &[u8], secret: &[u8]) -> u64 {
594 let mut acc = INITIAL_ACC;
595 hash_long_internal_loop_dispatch(&mut acc, input, secret);
596 merge_accs(
597 &acc,
598 secret,
599 SECRET_MERGEACCS_START,
600 (input.len() as u64).wrapping_mul(PRIME64_1),
601 )
602}
603
604#[inline]
606const fn xxh3_64_short(input: &[u8], seed: u64, secret: &[u8]) -> u64 {
607 if input.len() <= 16 {
608 xxh3_64_0to16(input, seed, secret)
609 } else if input.len() <= 128 {
610 xxh3_64_7to128(input, seed, secret)
611 } else {
612 xxh3_64_129to240(input, seed, secret)
613 }
614}
615
616#[inline(always)]
621#[allow(clippy::too_many_arguments)]
622const fn mix32_b(
623 lo: &mut u64,
624 hi: &mut u64,
625 input: &[u8],
626 input1_off: usize,
627 input2_off: usize,
628 secret: &[u8],
629 secret_off: usize,
630 seed: u64,
631) {
632 *lo = lo.wrapping_add(mix16b(input, input1_off, secret, secret_off, seed));
633 *lo ^= read_64le(input, input2_off).wrapping_add(read_64le(input, input2_off + 8));
634 *hi = hi.wrapping_add(mix16b(input, input2_off, secret, secret_off + 16, seed));
635 *hi ^= read_64le(input, input1_off).wrapping_add(read_64le(input, input1_off + 8));
636}
637
638const fn xxh3_128_1to3(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
643 let len = input.len();
644 let c1 = input[0] as u32;
645 let c2 = input[len >> 1] as u32;
646 let c3 = input[len - 1] as u32;
647 let combinedl = ((c1) << 16) | (c2 << 24) | (c3) | ((len as u32) << 8);
648 let combinedh = combinedl.swap_bytes().rotate_left(13);
649 let bitflipl = ((read_32le(secret, 0) ^ read_32le(secret, 4)) as u64).wrapping_add(seed);
650 let bitfliph = ((read_32le(secret, 8) ^ read_32le(secret, 12)) as u64).wrapping_sub(seed);
651 let keyed_lo = (combinedl as u64) ^ bitflipl;
652 let keyed_hi = (combinedh as u64) ^ bitfliph;
653 ((xxh64_avalanche(keyed_hi) as u128) << 64) | (xxh64_avalanche(keyed_lo) as u128)
654}
655
656const fn xxh3_128_9to16(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
657 let len = input.len();
658 let bitflipl = (read_64le(secret, 32) ^ read_64le(secret, 40)).wrapping_sub(seed);
659 let bitfliph = (read_64le(secret, 48) ^ read_64le(secret, 56)).wrapping_add(seed);
660 let input_lo = read_64le(input, 0);
661 let mut input_hi = read_64le(input, len - 8);
662
663 let m128_full = (input_lo ^ input_hi ^ bitflipl) as u128 * PRIME64_1 as u128;
664 let mut m128_lo = m128_full as u64;
665 let mut m128_hi = (m128_full >> 64) as u64;
666
667 m128_lo = m128_lo.wrapping_add(((len - 1) as u64) << 54);
668 input_hi ^= bitfliph;
669 m128_hi = m128_hi
670 .wrapping_add(input_hi)
671 .wrapping_add(mult32_to64(input_hi as u32, 0x85EBCA76u32));
672
673 m128_lo ^= m128_hi.swap_bytes();
674
675 let h128_full = (m128_lo as u128).wrapping_mul(PRIME64_2 as u128);
676 let h128_lo = h128_full as u64;
677 let h128_hi = ((h128_full >> 64) as u64).wrapping_add(m128_hi.wrapping_mul(PRIME64_2));
678
679 ((avalanche(h128_hi) as u128) << 64) | (avalanche(h128_lo) as u128)
680}
681
682const fn xxh3_128_4to8_return(input: &[u8], mut seed: u64, secret: &[u8]) -> u128 {
683 seed ^= ((seed as u32).swap_bytes() as u64) << 32;
684 let len = input.len();
685 let input_lo = read_32le(input, 0);
686 let input_hi = read_32le(input, len - 4);
687 let input_64 = (input_lo as u64).wrapping_add((input_hi as u64) << 32);
688 let bitflip = (read_64le(secret, 16) ^ read_64le(secret, 24)).wrapping_add(seed);
689 let keyed = input_64 ^ bitflip;
690 let m128 = (keyed as u128).wrapping_mul(PRIME64_1.wrapping_add((len as u64) << 2) as u128);
691 let mut m128_lo = m128 as u64;
692 let mut m128_hi = (m128 >> 64) as u64;
693 m128_hi = m128_hi.wrapping_add(m128_lo << 1);
694 m128_lo ^= m128_hi >> 3;
695 m128_lo = xorshift64(m128_lo, 35);
696 m128_lo = m128_lo.wrapping_mul(0x9FB21C651E98DF25);
697 m128_lo = xorshift64(m128_lo, 28);
698 m128_hi = avalanche(m128_hi);
699 ((m128_hi as u128) << 64) | (m128_lo as u128)
700}
701
702const fn xxh3_128_0to16(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
703 if input.len() > 8 {
704 xxh3_128_9to16(input, seed, secret)
705 } else if input.len() >= 4 {
706 xxh3_128_4to8_return(input, seed, secret)
707 } else if !input.is_empty() {
708 xxh3_128_1to3(input, seed, secret)
709 } else {
710 let flip_lo = read_64le(secret, 64) ^ read_64le(secret, 72);
711 let flip_hi = read_64le(secret, 80) ^ read_64le(secret, 88);
712 (xxh64_avalanche(seed ^ flip_lo) as u128) | ((xxh64_avalanche(seed ^ flip_hi) as u128) << 64)
713 }
714}
715
716const fn xxh3_128_7to128(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
721 let len = input.len();
722 let mut lo = (len as u64).wrapping_mul(PRIME64_1);
723 let mut hi: u64 = 0;
724
725 if len > 32 {
726 if len > 64 {
727 if len > 96 {
728 mix32_b(&mut lo, &mut hi, input, 48, len - 64, secret, 96, seed);
729 }
730 mix32_b(&mut lo, &mut hi, input, 32, len - 48, secret, 64, seed);
731 }
732 mix32_b(&mut lo, &mut hi, input, 16, len - 32, secret, 32, seed);
733 }
734
735 mix32_b(&mut lo, &mut hi, input, 0, len - 16, secret, 0, seed);
736
737 (avalanche(lo.wrapping_add(hi)) as u128)
738 | ((0u64.wrapping_sub(avalanche(
739 lo.wrapping_mul(PRIME64_1)
740 .wrapping_add(hi.wrapping_mul(0x85EBCA77C2B2AE63))
741 .wrapping_add((len as u64).wrapping_sub(seed).wrapping_mul(PRIME64_2)),
742 )) as u128)
743 << 64)
744}
745
746const fn xxh3_128_129to240(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
747 let len = input.len();
748 let nb_rounds = len / 32;
749 let mut lo = (len as u64).wrapping_mul(PRIME64_1);
750 let mut hi: u64 = 0;
751
752 let mut i = 0;
753 while i < 4 {
754 let offset = 32 * i;
755 mix32_b(&mut lo, &mut hi, input, offset, offset + 16, secret, offset, seed);
756 i += 1;
757 }
758
759 lo = avalanche(lo);
760 hi = avalanche(hi);
761
762 i = 4;
763 while i < nb_rounds {
764 mix32_b(&mut lo, &mut hi, input, 32 * i, 32 * i + 16, secret, 3 + 32 * (i - 4), seed);
765 i += 1;
766 }
767
768 mix32_b(
769 &mut lo,
770 &mut hi,
771 input,
772 len - 16,
773 len - 32,
774 secret,
775 SECRET_SIZE_MIN - 17 - 16,
776 0u64.wrapping_sub(seed),
777 );
778
779 (avalanche(lo.wrapping_add(hi)) as u128)
780 | ((0u64.wrapping_sub(avalanche(
781 lo.wrapping_mul(PRIME64_1)
782 .wrapping_add(hi.wrapping_mul(0x85EBCA77C2B2AE63))
783 .wrapping_add((len as u64).wrapping_sub(seed).wrapping_mul(PRIME64_2)),
784 )) as u128)
785 << 64)
786}
787
788const fn xxh3_128_long_scalar(input: &[u8], secret: &[u8]) -> u128 {
793 let mut acc = INITIAL_ACC;
794 hash_long_internal_loop(&mut acc, input, secret);
795
796 let lo = merge_accs(
797 &acc,
798 secret,
799 SECRET_MERGEACCS_START,
800 (input.len() as u64).wrapping_mul(PRIME64_1),
801 );
802 let hi = merge_accs(
803 &acc,
804 secret,
805 secret.len() - ACC_NB * 8 - SECRET_MERGEACCS_START,
806 !(input.len() as u64).wrapping_mul(PRIME64_2),
807 );
808
809 (lo as u128) | ((hi as u128) << 64)
810}
811
812fn xxh3_128_long(input: &[u8], secret: &[u8]) -> u128 {
814 let mut acc = INITIAL_ACC;
815 hash_long_internal_loop_dispatch(&mut acc, input, secret);
816
817 let lo = merge_accs(
818 &acc,
819 secret,
820 SECRET_MERGEACCS_START,
821 (input.len() as u64).wrapping_mul(PRIME64_1),
822 );
823 let hi = merge_accs(
824 &acc,
825 secret,
826 secret.len() - ACC_NB * 8 - SECRET_MERGEACCS_START,
827 !(input.len() as u64).wrapping_mul(PRIME64_2),
828 );
829
830 (lo as u128) | ((hi as u128) << 64)
831}
832
833#[inline]
835const fn xxh3_128_short(input: &[u8], seed: u64, secret: &[u8]) -> u128 {
836 if input.len() <= 16 {
837 xxh3_128_0to16(input, seed, secret)
838 } else if input.len() <= 128 {
839 xxh3_128_7to128(input, seed, secret)
840 } else {
841 xxh3_128_129to240(input, seed, secret)
842 }
843}
844
845#[derive(Clone)]
860struct Xxh3State {
861 acc: [u64; ACC_NB],
862 secret: [u8; DEFAULT_SECRET_SIZE],
863 buffer: [u8; INTERNAL_BUFFER_SIZE],
864 buffered_size: usize,
865 nb_stripes_so_far: usize,
866 total_len: u64,
867 seed: u64,
868 use_seed: bool,
872}
873
874impl Xxh3State {
875 #[inline]
876 const fn new(secret: [u8; DEFAULT_SECRET_SIZE], seed: u64, use_seed: bool) -> Self {
877 Xxh3State {
878 acc: INITIAL_ACC,
879 secret,
880 buffer: [0u8; INTERNAL_BUFFER_SIZE],
881 buffered_size: 0,
882 nb_stripes_so_far: 0,
883 total_len: 0,
884 seed,
885 use_seed,
886 }
887 }
888
889 fn update(&mut self, input: &[u8]) {
894 self.total_len += input.len() as u64;
895
896 if input.len() <= INTERNAL_BUFFER_SIZE - self.buffered_size {
898 let end = self.buffered_size + input.len();
899 self.buffer[self.buffered_size..end].copy_from_slice(input);
900 self.buffered_size = end;
901 return;
902 }
903
904 let mut data = input;
905
906 if self.buffered_size > 0 {
908 let load_size = INTERNAL_BUFFER_SIZE - self.buffered_size;
909 self.buffer[self.buffered_size..INTERNAL_BUFFER_SIZE].copy_from_slice(&data[..load_size]);
910 data = &data[load_size..];
911 consume_stripes(
912 &mut self.acc,
913 &mut self.nb_stripes_so_far,
914 &self.buffer,
915 INTERNAL_BUFFER_STRIPES,
916 &self.secret,
917 );
918 self.buffered_size = 0;
919 }
920
921 if data.len() > INTERNAL_BUFFER_SIZE {
923 let nb_stripes = (data.len() - 1) / STRIPE_LEN;
924 consume_stripes(&mut self.acc, &mut self.nb_stripes_so_far, data, nb_stripes, &self.secret);
925 let consumed = nb_stripes * STRIPE_LEN;
926 self.buffer[INTERNAL_BUFFER_SIZE - STRIPE_LEN..].copy_from_slice(&data[consumed - STRIPE_LEN..consumed]);
929 data = &data[consumed..];
930 }
931
932 self.buffer[..data.len()].copy_from_slice(data);
934 self.buffered_size = data.len();
935 }
936
937 fn digest_short_64(&self) -> u64 {
939 let data = &self.buffer[..self.buffered_size];
940 if self.use_seed {
941 xxh3_64_short(data, self.seed, &DEFAULT_SECRET)
942 } else {
943 xxh3_64_short(data, 0, &self.secret)
944 }
945 }
946
947 fn digest_short_128(&self) -> u128 {
949 let data = &self.buffer[..self.buffered_size];
950 if self.use_seed {
951 xxh3_128_short(data, self.seed, &DEFAULT_SECRET)
952 } else {
953 xxh3_128_short(data, 0, &self.secret)
954 }
955 }
956
957 fn digest_long_acc(&mut self) {
961 if self.buffered_size >= STRIPE_LEN {
962 let nb_stripes = (self.buffered_size - 1) / STRIPE_LEN;
963 consume_stripes(
964 &mut self.acc,
965 &mut self.nb_stripes_so_far,
966 &self.buffer,
967 nb_stripes,
968 &self.secret,
969 );
970 let last_off = self.buffered_size - STRIPE_LEN;
971 accumulate_512(&mut self.acc, &self.buffer, last_off, &self.secret, LAST_STRIPE_SECRET_OFFSET);
972 } else {
973 let catchup = STRIPE_LEN - self.buffered_size;
976 let mut last_stripe = [0u8; STRIPE_LEN];
977 last_stripe[..catchup].copy_from_slice(&self.buffer[INTERNAL_BUFFER_SIZE - catchup..]);
978 last_stripe[catchup..].copy_from_slice(&self.buffer[..self.buffered_size]);
979 accumulate_512(&mut self.acc, &last_stripe, 0, &self.secret, LAST_STRIPE_SECRET_OFFSET);
980 }
981 }
982
983 fn digest_long_64(mut self) -> u64 {
984 self.digest_long_acc();
985 merge_accs(
986 &self.acc,
987 &self.secret,
988 SECRET_MERGEACCS_START,
989 self.total_len.wrapping_mul(PRIME64_1),
990 )
991 }
992
993 fn digest_long_128(mut self) -> u128 {
994 self.digest_long_acc();
995 let lo = merge_accs(
996 &self.acc,
997 &self.secret,
998 SECRET_MERGEACCS_START,
999 self.total_len.wrapping_mul(PRIME64_1),
1000 );
1001 let hi = merge_accs(
1002 &self.acc,
1003 &self.secret,
1004 DEFAULT_SECRET_SIZE - ACC_NB * 8 - SECRET_MERGEACCS_START,
1005 !self.total_len.wrapping_mul(PRIME64_2),
1006 );
1007 (lo as u128) | ((hi as u128) << 64)
1008 }
1009
1010 fn digest_64(self) -> u64 {
1011 if self.total_len <= MID_SIZE_MAX as u64 {
1012 self.digest_short_64()
1013 } else {
1014 self.digest_long_64()
1015 }
1016 }
1017
1018 fn digest_128(self) -> u128 {
1019 if self.total_len <= MID_SIZE_MAX as u64 {
1020 self.digest_short_128()
1021 } else {
1022 self.digest_long_128()
1023 }
1024 }
1025}
1026
1027#[derive(Clone)]
1062pub struct Xxh3_64 {
1063 state: Xxh3State,
1064}
1065
1066impl Xxh3_64 {
1067 #[inline]
1071 pub const fn with_seed(seed: u64) -> Self {
1072 if seed == 0 {
1073 Xxh3_64 {
1074 state: Xxh3State::new(DEFAULT_SECRET, 0, false),
1075 }
1076 } else {
1077 Xxh3_64 {
1078 state: Xxh3State::new(custom_default_secret_scalar(seed), seed, true),
1079 }
1080 }
1081 }
1082
1083 #[inline]
1085 pub const fn with_secret(secret: [u8; DEFAULT_SECRET_SIZE]) -> Self {
1086 Xxh3_64 {
1087 state: Xxh3State::new(secret, 0, false),
1088 }
1089 }
1090
1091 #[inline]
1098 pub const fn with_seed_and_secret(seed: u64, secret: [u8; DEFAULT_SECRET_SIZE]) -> Self {
1099 Xxh3_64 {
1100 state: Xxh3State::new(secret, seed, true),
1101 }
1102 }
1103}
1104
1105impl Checksum for Xxh3_64 {
1106 type Output = u64;
1107
1108 fn new() -> Self {
1109 Self::with_seed(0)
1110 }
1111
1112 #[inline]
1113 fn checksum(data: &[u8]) -> u64 {
1114 if data.len() <= MID_SIZE_MAX {
1115 xxh3_64_short(data, 0, &DEFAULT_SECRET)
1116 } else {
1117 xxh3_64_long(data, &DEFAULT_SECRET)
1118 }
1119 }
1120
1121 #[inline]
1122 fn update(&mut self, data: &[u8]) {
1123 self.state.update(data);
1124 }
1125
1126 #[inline]
1127 fn sum(self) -> u64 {
1128 self.state.digest_64()
1129 }
1130}
1131
1132impl core::fmt::Debug for Xxh3_64 {
1133 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1134 f.debug_struct("Xxh3_64").finish()
1135 }
1136}
1137
1138impl Default for Xxh3_64 {
1139 #[inline]
1140 fn default() -> Self {
1141 Self::with_seed(0)
1142 }
1143}
1144
1145#[derive(Clone)]
1180pub struct Xxh3_128 {
1181 state: Xxh3State,
1182}
1183
1184impl Xxh3_128 {
1185 #[inline]
1189 pub const fn with_seed(seed: u64) -> Self {
1190 if seed == 0 {
1191 Xxh3_128 {
1192 state: Xxh3State::new(DEFAULT_SECRET, 0, false),
1193 }
1194 } else {
1195 Xxh3_128 {
1196 state: Xxh3State::new(custom_default_secret_scalar(seed), seed, true),
1197 }
1198 }
1199 }
1200
1201 #[inline]
1203 pub const fn with_secret(secret: [u8; DEFAULT_SECRET_SIZE]) -> Self {
1204 Xxh3_128 {
1205 state: Xxh3State::new(secret, 0, false),
1206 }
1207 }
1208
1209 #[inline]
1216 pub const fn with_seed_and_secret(seed: u64, secret: [u8; DEFAULT_SECRET_SIZE]) -> Self {
1217 Xxh3_128 {
1218 state: Xxh3State::new(secret, seed, true),
1219 }
1220 }
1221}
1222
1223impl Checksum for Xxh3_128 {
1224 type Output = u128;
1225
1226 fn new() -> Self {
1227 Self::with_seed(0)
1228 }
1229
1230 #[inline]
1231 fn checksum(data: &[u8]) -> u128 {
1232 if data.len() <= MID_SIZE_MAX {
1233 xxh3_128_short(data, 0, &DEFAULT_SECRET)
1234 } else {
1235 xxh3_128_long(data, &DEFAULT_SECRET)
1236 }
1237 }
1238
1239 #[inline]
1240 fn update(&mut self, data: &[u8]) {
1241 self.state.update(data);
1242 }
1243
1244 #[inline]
1245 fn sum(self) -> u128 {
1246 self.state.digest_128()
1247 }
1248}
1249
1250impl core::fmt::Debug for Xxh3_128 {
1251 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1252 f.debug_struct("Xxh3_128").finish()
1253 }
1254}
1255
1256impl Default for Xxh3_128 {
1257 #[inline]
1258 fn default() -> Self {
1259 Self::with_seed(0)
1260 }
1261}
1262
1263#[inline]
1283pub const fn xxh3_64(data: &[u8]) -> u64 {
1284 if data.len() <= MID_SIZE_MAX {
1285 xxh3_64_short(data, 0, &DEFAULT_SECRET)
1286 } else {
1287 xxh3_64_long_scalar(data, &DEFAULT_SECRET)
1288 }
1289}
1290
1291#[inline]
1307pub const fn xxh3_128(data: &[u8]) -> u128 {
1308 if data.len() <= MID_SIZE_MAX {
1309 xxh3_128_short(data, 0, &DEFAULT_SECRET)
1310 } else {
1311 xxh3_128_long_scalar(data, &DEFAULT_SECRET)
1312 }
1313}
1314
1315#[cfg(test)]
1320mod tests {
1321 use super::*;
1322 use crate::{Checksum, test_helpers::fill_test_buffer};
1323
1324 extern crate alloc;
1325
1326 const TEST_SEED: u64 = 0x9E3779B185EBCA8D;
1328 const TEST_SEED32: u64 = 0x9E3779B1;
1330
1331 #[test]
1334 fn test_xh3_64_empty() {
1335 assert_eq!(Xxh3_64::checksum(b""), 0x2D06800538D394C2);
1336 }
1337
1338 #[test]
1339 fn test_xh3_64_hello() {
1340 assert_eq!(Xxh3_64::checksum(b"hello"), 0x9555E8555C62DCFD);
1341 }
1342
1343 #[test]
1344 fn test_xh3_64_fox() {
1345 assert_eq!(
1346 Xxh3_64::checksum(b"The quick brown fox jumps over the lazy dog"),
1347 0xCE7D19A5418FB365
1348 );
1349 }
1350
1351 #[test]
1355 fn test_xh3_64_official_vectors() {
1356 let cases: &[(usize, u64, u64)] = &[
1357 (0, 0, 0x2D06800538D394C2),
1358 (0, TEST_SEED, 0xA8A6B918B2F0364A),
1359 (1, 0, 0xC44BDFF4074EECDB),
1360 (1, TEST_SEED, 0x032BE332DD766EF8),
1361 (6, 0, 0x27B56A84CD2D7325),
1362 (6, TEST_SEED, 0x84589C116AB59AB9),
1363 (12, 0, 0xA713DAF0DFBB77E7),
1364 (12, TEST_SEED, 0xE7303E1B2336DE0E),
1365 (24, 0, 0xA3FE70BF9D3510EB),
1366 (24, TEST_SEED, 0x850E80FC35BDD690),
1367 (48, 0, 0x397DA259ECBA1F11),
1368 (48, TEST_SEED, 0xADC2CBAA44ACC616),
1369 (80, 0, 0xBCDEFBBB2C47C90A),
1370 (80, TEST_SEED, 0xC6DD0CB699532E73),
1371 (195, 0, 0xCD94217EE362EC3A),
1372 (195, TEST_SEED, 0xBA68003D370CB3D9),
1373 ];
1374
1375 for &(len, seed, expected) in cases {
1376 let buf = fill_test_buffer(len);
1377 let mut h = Xxh3_64::with_seed(seed);
1378 h.update(&buf);
1379 assert_eq!(h.sum(), expected, "XXH3-64 length {len} seed {seed:#x}");
1380 }
1381 }
1382
1383 #[test]
1385 fn test_xh3_64_byte_at_a_time() {
1386 let cases: &[(usize, u64, u64)] = &[
1387 (0, 0, 0x2D06800538D394C2),
1388 (1, TEST_SEED, 0x032BE332DD766EF8),
1389 (12, 0, 0xA713DAF0DFBB77E7),
1390 (80, TEST_SEED, 0xC6DD0CB699532E73),
1391 ];
1392
1393 for &(len, seed, expected) in cases {
1394 let buf = fill_test_buffer(len);
1395 let mut h = Xxh3_64::with_seed(seed);
1396 for b in &buf {
1397 h.update(&[*b]);
1398 }
1399 assert_eq!(h.sum(), expected, "XXH3-64 byte-at-a-time length {len} seed {seed:#x}");
1400 }
1401 }
1402
1403 #[test]
1405 fn test_xh3_64_boundaries() {
1406 let sizes: &[(usize, u64)] = &[
1407 (0, 0x2D06800538D394C2),
1409 (1, 0xC44BDFF4074EECDB),
1410 (3, 0x54247382A8D6B94D),
1411 (4, 0xE5DC74BC51848A51),
1412 (8, 0x24CCC9ACAA9F65E4),
1413 (16, 0x981B17D36C7498C9),
1414 (17, 0x796F5ACD3A60F862),
1416 (32, 0x9FEADDBDBF57EED3),
1417 (64, 0x9CB48487720EC49D),
1418 (128, 0xFCFF24126754D861),
1419 (129, 0x98F1B0A679A2CA29),
1421 (240, 0x81C3C2B67F568CCF),
1422 ];
1423
1424 for &(len, expected) in sizes {
1425 let buf = fill_test_buffer(len);
1426 let mut h = Xxh3_64::new();
1427 h.update(&buf);
1428 assert_eq!(h.sum(), expected, "XXH3-64 boundary length {len}");
1429 }
1430 }
1431
1432 #[test]
1433 fn test_xh3_64_incremental() {
1434 let mut h = Xxh3_64::new();
1435 h.update(b"The quick brown ");
1436 h.update(b"fox jumps over ");
1437 h.update(b"the lazy dog");
1438 assert_eq!(h.sum(), 0xCE7D19A5418FB365);
1439 }
1440
1441 #[test]
1442 fn test_xh3_64_seeded() {
1443 let mut h = Xxh3_64::with_seed(42);
1444 h.update(b"hello");
1445 assert_eq!(h.sum(), 0xBAFA072F07DB7937);
1446 }
1447
1448 #[test]
1449 fn test_xh3_64_long_incremental() {
1450 let mut h = Xxh3_64::new();
1451 let data = [0x55u8; 512];
1452 h.update(&data[..200]);
1453 h.update(&data[200..400]);
1454 h.update(&data[400..]);
1455 assert_eq!(h.sum(), 0x4C1155EA5825B659);
1456 }
1457
1458 #[test]
1461 fn test_xh3_128_empty() {
1462 assert_eq!(Xxh3_128::checksum(b""), 0x99AA06D3014798D86001C324468D497F);
1463 }
1464
1465 #[test]
1466 fn test_xh3_128_hello() {
1467 assert_eq!(Xxh3_128::checksum(b"hello"), 0xB5E9C1AD071B3E7FC779CFAA5E523818);
1468 }
1469
1470 #[test]
1471 fn test_xh3_128_fox() {
1472 assert_eq!(
1473 Xxh3_128::checksum(b"The quick brown fox jumps over the lazy dog"),
1474 0xDDD650205CA3E7FA24A1CC2E3A8A7651
1475 );
1476 }
1477
1478 #[test]
1481 fn test_xh3_128_official_vectors() {
1482 let cases: &[(usize, u64, u128)] = &[
1483 (0, 0, 0x99AA06D3014798D86001C324468D497F),
1484 (0, TEST_SEED32, 0x92220AE55E14AB505444F7869C671AB0),
1485 (1, 0, 0xA6CD5E9392000F6AC44BDFF4074EECDB),
1486 (1, TEST_SEED32, 0x89B99554BA22467CB53D5557E7F76F8D),
1487 (6, 0, 0x082AFE0B8162D12A3E7039BDDA43CFC6),
1488 (6, TEST_SEED32, 0x5A865B5389ABD2B1269D8F70BE98856E),
1489 (12, 0, 0x6E3EFD8FC7802B18061A192713F69AD9),
1490 (12, TEST_SEED32, 0xD7E09D518A3405D39BE9F9A67F3C7DFB),
1491 (24, 0, 0x0CE966E4678D37611E7044D28B1B901D),
1492 (24, TEST_SEED32, 0x3162026714A6A243D7304C54EBAD40A9),
1493 (48, 0, 0xA002AC4E5478227EF942219AED80F67B),
1494 (48, TEST_SEED32, 0x163ADDE36C0722957BA3C3E453A1934E),
1495 (81, 0, 0x4952F58181AB00425E8BAFB9F95FB803),
1496 (81, TEST_SEED32, 0x2724EC7ADC750FB6703FBB3D7A5F755C),
1497 (222, 0, 0x337E09641B948717F1AEBD597CEC6B3A),
1498 (222, TEST_SEED32, 0x91820016621E97F1AE995BB8AF917A8D),
1499 ];
1500
1501 for &(len, seed, expected) in cases {
1502 let buf = fill_test_buffer(len);
1503 let mut h = Xxh3_128::with_seed(seed);
1504 h.update(&buf);
1505 assert_eq!(h.sum(), expected, "XXH128 length {len} seed {seed:#x}");
1506 }
1507 }
1508
1509 #[test]
1511 fn test_xh3_128_byte_at_a_time() {
1512 let cases: &[(usize, u64, u128)] = &[
1513 (0, 0, 0x99AA06D3014798D86001C324468D497F),
1514 (1, TEST_SEED32, 0x89B99554BA22467CB53D5557E7F76F8D),
1515 (12, 0, 0x6E3EFD8FC7802B18061A192713F69AD9),
1516 ];
1517
1518 for &(len, seed, expected) in cases {
1519 let buf = fill_test_buffer(len);
1520 let mut h = Xxh3_128::with_seed(seed);
1521 for b in &buf {
1522 h.update(&[*b]);
1523 }
1524 assert_eq!(h.sum(), expected, "XXH128 byte-at-a-time length {len} seed {seed:#x}");
1525 }
1526 }
1527
1528 #[test]
1530 fn test_xh3_128_boundaries() {
1531 let sizes: &[(usize, u128)] = &[
1532 (0, 0x99AA06D3014798D86001C324468D497F),
1533 (1, 0xA6CD5E9392000F6AC44BDFF4074EECDB),
1534 (3, 0x20EFC49FF02422EA54247382A8D6B94D),
1535 (4, 0x970D585AC632BF8E2E7D8D6876A39FE9),
1536 (8, 0x47A7F080D82BB45664C69CAB4BB21DC5),
1537 (16, 0xC68C368ECF8A9C05562980258A998629),
1538 (17, 0x955FA78643ED3669ABBC12D11973D7DB),
1539 (32, 0x98FC6458710DC2E8278410A17595E3F9),
1540 (240, 0xAA4202DAA2769DC85C9AAE94C8EBE5A0),
1541 ];
1542
1543 for &(len, expected) in sizes {
1544 let buf = fill_test_buffer(len);
1545 let mut h = Xxh3_128::new();
1546 h.update(&buf);
1547 assert_eq!(h.sum(), expected, "XXH128 boundary length {len}");
1548 }
1549 }
1550
1551 #[test]
1552 fn test_xh3_128_incremental() {
1553 let mut h = Xxh3_128::new();
1554 h.update(b"The quick brown ");
1555 h.update(b"fox jumps over ");
1556 h.update(b"the lazy dog");
1557 assert_eq!(h.sum(), 0xDDD650205CA3E7FA24A1CC2E3A8A7651);
1558 }
1559
1560 #[test]
1561 fn test_xh3_128_long_incremental() {
1562 let mut h = Xxh3_128::new();
1563 let data = [0x55u8; 512];
1564 h.update(&data[..200]);
1565 h.update(&data[200..400]);
1566 h.update(&data[400..]);
1567 assert_eq!(h.sum(), 0x0DD2485B0318DEF24C1155EA5825B659);
1568 }
1569
1570 #[test]
1573 fn test_const_fn_xh3_64() {
1574 assert_eq!(xxh3_64(b""), Xxh3_64::checksum(b""));
1575 assert_eq!(xxh3_64(b"hello"), Xxh3_64::checksum(b"hello"));
1576 assert_eq!(
1577 xxh3_64(b"The quick brown fox jumps over the lazy dog"),
1578 Xxh3_64::checksum(b"The quick brown fox jumps over the lazy dog")
1579 );
1580 let buf = &[0x42u8; 200];
1583 assert_eq!(xxh3_64(buf), Xxh3_64::checksum(buf));
1584 }
1585
1586 #[test]
1589 fn test_const_fn_xh3_128() {
1590 assert_eq!(xxh3_128(b""), Xxh3_128::checksum(b""));
1591 assert_eq!(xxh3_128(b"hello"), Xxh3_128::checksum(b"hello"));
1592 assert_eq!(
1593 xxh3_128(b"The quick brown fox jumps over the lazy dog"),
1594 Xxh3_128::checksum(b"The quick brown fox jumps over the lazy dog")
1595 );
1596 let buf = &[0x42u8; 200];
1597 assert_eq!(xxh3_128(buf), Xxh3_128::checksum(buf));
1598 }
1599
1600 fn test_secret() -> [u8; 192] {
1606 let mut secret = [0u8; 192];
1607 secret.copy_from_slice(&fill_test_buffer(192));
1608 secret
1609 }
1610
1611 #[test]
1614 fn test_reference_vectors_with_seed() {
1615 for &(len, seed, h64, h128) in crate::test_vectors::XXH3_VECTORS {
1616 let buf = fill_test_buffer(len);
1617
1618 let mut hasher = Xxh3_64::with_seed(seed);
1619 hasher.update(&buf);
1620 assert_eq!(hasher.sum(), h64, "XXH3-64 len={len} seed={seed:#018x}");
1621
1622 let mut hasher = Xxh3_128::with_seed(seed);
1623 hasher.update(&buf);
1624 assert_eq!(hasher.sum(), h128, "XXH3-128 len={len} seed={seed:#018x}");
1625
1626 if seed == 0 {
1627 assert_eq!(xxh3_64(&buf), h64, "xxh3_64 const len={len}");
1630 assert_eq!(xxh3_128(&buf), h128, "xxh3_128 const len={len}");
1631 assert_eq!(Xxh3_64::checksum(&buf), h64, "checksum 64 len={len}");
1632 assert_eq!(Xxh3_128::checksum(&buf), h128, "checksum 128 len={len}");
1633 }
1634 }
1635 }
1636
1637 #[test]
1638 fn test_reference_vectors_with_secret() {
1639 let secret = test_secret();
1640 for &(len, h64, h128) in crate::test_vectors::XXH3_SECRET_VECTORS {
1641 let buf = fill_test_buffer(len);
1642
1643 let mut hasher = Xxh3_64::with_secret(secret);
1644 hasher.update(&buf);
1645 assert_eq!(hasher.sum(), h64, "XXH3-64 secret len={len}");
1646
1647 let mut hasher = Xxh3_128::with_secret(secret);
1648 hasher.update(&buf);
1649 assert_eq!(hasher.sum(), h128, "XXH3-128 secret len={len}");
1650 }
1651 }
1652
1653 #[test]
1654 fn test_reference_vectors_with_secret_and_seed() {
1655 let secret = test_secret();
1656 for &(len, seed, h64, h128) in crate::test_vectors::XXH3_SECRET_SEED_VECTORS {
1657 let buf = fill_test_buffer(len);
1658
1659 let mut hasher = Xxh3_64::with_seed_and_secret(seed, secret);
1660 hasher.update(&buf);
1661 assert_eq!(hasher.sum(), h64, "XXH3-64 secret+seed len={len} seed={seed:#018x}");
1662
1663 let mut hasher = Xxh3_128::with_seed_and_secret(seed, secret);
1664 hasher.update(&buf);
1665 assert_eq!(hasher.sum(), h128, "XXH3-128 secret+seed len={len} seed={seed:#018x}");
1666 }
1667 }
1668
1669 struct Rng(u64);
1676
1677 impl Rng {
1678 fn next(&mut self) -> u64 {
1679 self.0 ^= self.0 << 13;
1680 self.0 ^= self.0 >> 7;
1681 self.0 ^= self.0 << 17;
1682 self.0
1683 }
1684
1685 fn below(&mut self, n: u64) -> u64 {
1686 self.next() % n
1687 }
1688 }
1689
1690 fn random_data(rng: &mut Rng, len: usize) -> alloc::vec::Vec<u8> {
1691 (0..len).map(|_| rng.next() as u8).collect()
1692 }
1693
1694 #[test]
1697 fn test_streaming_matches_one_shot_randomized() {
1698 let mut rng = Rng(0x243F6A8885A308D3);
1699 for _ in 0..4000 {
1700 let len = rng.below(3000) as usize;
1701 let data = random_data(&mut rng, len);
1702 let seed = rng.next();
1703
1704 let mut chunks = alloc::vec::Vec::new();
1706 let mut left = len;
1707 let max_chunk = [1_u64, 3, 64, 65, 240, 241, 1024][rng.below(7) as usize];
1708 while left > 0 {
1709 let n = ((rng.below(max_chunk) + 1) as usize).min(left);
1710 chunks.push(n);
1711 left -= n;
1712 }
1713
1714 let mut hasher = Xxh3_64::with_seed(seed);
1715 let mut hasher128 = Xxh3_128::with_seed(seed);
1716 let mut offset = 0;
1717 for &n in &chunks {
1718 hasher.update(&data[offset..offset + n]);
1719 hasher128.update(&data[offset..offset + n]);
1720 offset += n;
1721 }
1722 let streamed64 = hasher.sum();
1723 let streamed128 = hasher128.sum();
1724
1725 let mut one_shot = Xxh3_64::with_seed(seed);
1726 one_shot.update(&data);
1727 assert_eq!(streamed64, one_shot.sum(), "XXH3-64 len={len} chunks={chunks:?}");
1728
1729 let mut one_shot = Xxh3_128::with_seed(seed);
1730 one_shot.update(&data);
1731 assert_eq!(streamed128, one_shot.sum(), "XXH3-128 len={len} chunks={chunks:?}");
1732 }
1733 }
1734
1735 #[test]
1737 fn test_byte_at_a_time_matches_one_shot() {
1738 for len in 0..=600 {
1739 let buf = fill_test_buffer(len);
1740
1741 let mut hasher = Xxh3_64::new();
1742 for byte in &buf {
1743 hasher.update(core::slice::from_ref(byte));
1744 }
1745 assert_eq!(hasher.sum(), xxh3_64(&buf), "XXH3-64 len={len}");
1746
1747 let mut hasher = Xxh3_128::new();
1748 for byte in &buf {
1749 hasher.update(core::slice::from_ref(byte));
1750 }
1751 assert_eq!(hasher.sum(), xxh3_128(&buf), "XXH3-128 len={len}");
1752 }
1753 }
1754
1755 #[test]
1760 fn test_regression_flush_at_block_boundary() {
1761 for total in [241_usize, 256, 257, 320, 512, 1024, 1025, 1088, 1089, 1152, 2048, 2049] {
1762 let buf = fill_test_buffer(total);
1763 for split in [1_usize, 63, 64, 65, 240, 241, 256] {
1764 if split >= total {
1765 continue;
1766 }
1767
1768 let mut hasher = Xxh3_64::new();
1769 hasher.update(&buf[..split]);
1770 hasher.update(&buf[split..]);
1771 assert_eq!(hasher.sum(), Xxh3_64::checksum(&buf), "total={total} split={split}");
1772
1773 let mut hasher = Xxh3_128::new();
1774 hasher.update(&buf[..split]);
1775 hasher.update(&buf[split..]);
1776 assert_eq!(hasher.sum(), Xxh3_128::checksum(&buf), "total={total} split={split}");
1777 }
1778 }
1779 }
1780
1781 #[test]
1785 fn test_many_small_updates_after_long_prefix() {
1786 let sql = fill_test_buffer(300);
1787 let mut hasher = Xxh3_128::new();
1788 hasher.update(&sql);
1789 let mut scratch = sql.clone();
1790 for i in 0..500u32 {
1791 let oid = (2500 + i).to_be_bytes();
1792 hasher.update(&oid);
1793 scratch.extend_from_slice(&oid);
1794 }
1795 assert_eq!(hasher.sum(), xxh3_128(&scratch));
1796 }
1797
1798 #[test]
1800 fn test_empty_updates_are_noop() {
1801 let buf = fill_test_buffer(500);
1802 let mut hasher = Xxh3_64::new();
1803 hasher.update(&[]);
1804 hasher.update(&buf);
1805 hasher.update(&[]);
1806 assert_eq!(hasher.sum(), Xxh3_64::checksum(&buf));
1807 }
1808
1809 #[test]
1812 fn test_dispatched_matches_scalar() {
1813 for len in [
1814 241_usize, 255, 256, 257, 300, 512, 1023, 1024, 1025, 2048, 2049, 4096, 5000, 8192, 10000,
1815 ] {
1816 let buf = fill_test_buffer(len);
1817 assert_eq!(Xxh3_64::checksum(&buf), xxh3_64(&buf), "XXH3-64 len={len}");
1818 assert_eq!(Xxh3_128::checksum(&buf), xxh3_128(&buf), "XXH3-128 len={len}");
1819 }
1820 }
1821
1822 #[cfg(not(target_arch = "wasm32"))]
1824 #[test]
1825 fn test_hashing_does_not_allocate() {
1826 let small = fill_test_buffer(240);
1827 let large = fill_test_buffer(4096);
1828 let secret = test_secret();
1829
1830 let ((), counters) = alloc_counter::measure(|| {
1831 let _ = core::hint::black_box(xxh3_64(core::hint::black_box(&small)));
1832 let _ = core::hint::black_box(xxh3_128(core::hint::black_box(&large)));
1833
1834 let mut hasher = Xxh3_64::with_seed(42);
1835 hasher.update(core::hint::black_box(&large));
1836 let _ = core::hint::black_box(hasher.sum());
1837
1838 let mut hasher = Xxh3_128::with_secret(secret);
1839 for chunk in large.chunks(7) {
1840 hasher.update(core::hint::black_box(chunk));
1841 }
1842 let _ = core::hint::black_box(hasher.sum());
1843
1844 let mut hasher = Xxh3_64::new();
1845 hasher.update(core::hint::black_box(&large));
1846 let _ = core::hint::black_box(hasher.sum());
1847 });
1848
1849 assert_eq!(counters.calls, 0, "hashing must not allocate");
1850 assert_eq!(counters.live_allocations, 0, "hashing must not leak");
1851 }
1852}