1#![deny(
4 clippy::missing_safety_doc,
5 clippy::undocumented_unsafe_blocks,
6 unsafe_op_in_unsafe_fn
7)]
8
9use core::{hash, hint};
10
11use crate::{
12 xxhash3::{primes::*, *},
13 IntoU128 as _, IntoU64 as _,
14};
15
16pub use crate::xxhash3::{
17 FixedBuffer, FixedMutBuffer, OneshotWithSecretError, SecretBuffer, SecretTooShortError,
18 SecretWithSeedError, DEFAULT_SECRET_LENGTH, SECRET_MINIMUM_LENGTH,
19};
20
21#[derive(Clone)]
23pub struct Hasher {
24 #[cfg(feature = "alloc")]
25 inner: AllocRawHasher,
26 _private: (),
27}
28
29impl Hasher {
30 #[must_use]
33 #[inline]
34 pub fn oneshot(input: &[u8]) -> u64 {
35 fn optimize_for_latency(input: &[u8]) -> bool {
39 input.len() <= CUTOFF
40 }
41
42 if optimize_for_latency(input) {
43 impl_oneshot(DEFAULT_SECRET, DEFAULT_SEED, input)
44 } else {
45 #[inline(never)]
46 fn outline(input: &[u8]) -> u64 {
47 unsafe {
53 hint::assert_unchecked(!optimize_for_latency(input));
54 }
55 impl_oneshot(opaque_default_secret!(), DEFAULT_SEED, input)
56 }
57
58 outline(input)
59 }
60 }
61
62 #[must_use]
66 #[inline]
67 pub fn oneshot_with_seed(seed: u64, input: &[u8]) -> u64 {
68 fn simple_case(seed: u64, input: &[u8]) -> bool {
73 input.len() <= CUTOFF || seed == DEFAULT_SEED
74 }
75
76 if simple_case(seed, input) {
77 impl_oneshot(opaque_default_secret!(), seed, input)
78 } else {
79 #[inline(never)]
83 fn outline(seed: u64, input: &[u8]) -> u64 {
84 unsafe {
90 hint::assert_unchecked(!simple_case(seed, input));
91 }
92
93 let mut derived_secret = DEFAULT_SECRET_RAW;
94 derive_secret(seed, &mut derived_secret);
95 let secret =
96 Secret::new(&derived_secret).expect("The default secret length is invalid");
97
98 impl_oneshot(secret, seed, input)
99 }
100
101 outline(seed, input)
102 }
103 }
104
105 #[inline]
109 pub fn oneshot_with_secret(secret: &[u8], input: &[u8]) -> Result<u64, OneshotWithSecretError> {
110 let secret = Secret::new(secret).map_err(OneshotWithSecretError)?;
111 Ok(impl_oneshot(secret, DEFAULT_SEED, input))
112 }
113
114 #[inline]
118 pub fn oneshot_with_seed_and_secret(
119 seed: u64,
120 secret: &[u8],
121 input: &[u8],
122 ) -> Result<u64, OneshotWithSecretError> {
123 let secret = if input.len() > CUTOFF {
124 Secret::new(secret).map_err(OneshotWithSecretError)?
125 } else {
126 DEFAULT_SECRET
127 };
128
129 Ok(impl_oneshot(secret, seed, input))
130 }
131}
132
133#[cfg(feature = "alloc")]
134#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
135mod with_alloc {
136 use ::alloc::boxed::Box;
137
138 use super::*;
139
140 impl Hasher {
141 pub fn new() -> Self {
143 Self {
144 inner: RawHasherCore::allocate_default(),
145 _private: (),
146 }
147 }
148
149 pub fn with_seed(seed: u64) -> Self {
152 Self {
153 inner: RawHasherCore::allocate_with_seed(seed),
154 _private: (),
155 }
156 }
157
158 pub fn with_seed_and_secret(
160 seed: u64,
161 secret: impl Into<Box<[u8]>>,
162 ) -> Result<Self, SecretTooShortError<Box<[u8]>>> {
163 Ok(Self {
164 inner: RawHasherCore::allocate_with_seed_and_secret(seed, secret)?,
165 _private: (),
166 })
167 }
168
169 pub fn into_secret(self) -> Box<[u8]> {
171 self.inner.into_secret()
172 }
173 }
174
175 impl Default for Hasher {
176 fn default() -> Self {
177 Self::new()
178 }
179 }
180
181 impl hash::Hasher for Hasher {
182 #[inline]
183 fn write(&mut self, input: &[u8]) {
184 self.inner.write(input);
185 }
186
187 #[inline]
188 fn finish(&self) -> u64 {
189 self.inner.finish(Finalize64)
190 }
191 }
192}
193
194#[derive(Clone)]
195pub struct RawHasher<S>(RawHasherCore<S>);
204
205impl<S> RawHasher<S> {
206 pub fn new(secret_buffer: SecretBuffer<S>) -> Self {
209 Self(RawHasherCore::new(secret_buffer))
210 }
211
212 pub fn into_secret(self) -> S {
214 self.0.into_secret()
215 }
216}
217
218impl<S> hash::Hasher for RawHasher<S>
219where
220 S: FixedBuffer,
221{
222 #[inline]
223 fn write(&mut self, input: &[u8]) {
224 self.0.write(input);
225 }
226
227 #[inline]
228 fn finish(&self) -> u64 {
229 self.0.finish(Finalize64)
230 }
231}
232
233struct Finalize64;
234
235impl Finalize for Finalize64 {
236 type Output = u64;
237
238 #[inline(always)]
239 fn small(&self, secret: &Secret, seed: u64, input: &[u8]) -> Self::Output {
240 impl_oneshot(secret, seed, input)
241 }
242
243 #[inline(always)]
244 fn large(
245 &self,
246 vector: impl Vector,
247 acc: [u64; 8],
248 last_block: &[u8],
249 last_stripe: &[u8; 64],
250 secret: &Secret,
251 len: usize,
252 ) -> Self::Output {
253 Algorithm(vector).finalize_64(acc, last_block, last_stripe, secret, len)
254 }
255}
256
257#[inline(always)]
258fn impl_oneshot(secret: &Secret, seed: u64, input: &[u8]) -> u64 {
259 match input.len() {
260 241.. => impl_241_plus_bytes(secret, input),
261
262 129..=240 => impl_129_to_240_bytes(secret, seed, input),
263
264 17..=128 => impl_17_to_128_bytes(secret, seed, input),
265
266 9..=16 => impl_9_to_16_bytes(secret, seed, input),
267
268 4..=8 => impl_4_to_8_bytes(secret, seed, input),
269
270 1..=3 => impl_1_to_3_bytes(secret, seed, input),
271
272 0 => impl_0_bytes(secret, seed),
273 }
274}
275
276#[inline(always)]
277fn impl_0_bytes(secret: &Secret, seed: u64) -> u64 {
278 let secret_words = secret.for_64().words_for_0();
279 avalanche_xxh64(seed ^ secret_words[0] ^ secret_words[1])
280}
281
282#[inline(always)]
283fn impl_1_to_3_bytes(secret: &Secret, seed: u64, input: &[u8]) -> u64 {
284 assert_input_range!(1..=3, input.len());
285 let combined = impl_1_to_3_bytes_combined(input);
286
287 let secret_words = secret.for_64().words_for_1_to_3();
288
289 let value = {
290 let secret = (secret_words[0] ^ secret_words[1]).into_u64();
291 secret.wrapping_add(seed) ^ combined.into_u64()
292 };
293
294 avalanche_xxh64(value)
296}
297
298#[inline(always)]
299fn impl_4_to_8_bytes(secret: &Secret, seed: u64, input: &[u8]) -> u64 {
300 assert_input_range!(4..=8, input.len());
301 let input_first = input.first_u32().unwrap();
302 let input_last = input.last_u32().unwrap();
303
304 let modified_seed = seed ^ (seed.lower_half().swap_bytes().into_u64() << 32);
305 let secret_words = secret.for_64().words_for_4_to_8();
306
307 let combined = input_last.into_u64() | (input_first.into_u64() << 32);
308
309 let mut value = {
310 let a = secret_words[0] ^ secret_words[1];
311 let b = a.wrapping_sub(modified_seed);
312 b ^ combined
313 };
314 value ^= value.rotate_left(49) ^ value.rotate_left(24);
315 value = value.wrapping_mul(PRIME_MX2);
316 value ^= (value >> 35).wrapping_add(input.len().into_u64());
317 value = value.wrapping_mul(PRIME_MX2);
318 value ^= value >> 28;
319 value
320}
321
322#[inline(always)]
323fn impl_9_to_16_bytes(secret: &Secret, seed: u64, input: &[u8]) -> u64 {
324 assert_input_range!(9..=16, input.len());
325 let input_first = input.first_u64().unwrap();
326 let input_last = input.last_u64().unwrap();
327
328 let secret_words = secret.for_64().words_for_9_to_16();
329 let low = ((secret_words[0] ^ secret_words[1]).wrapping_add(seed)) ^ input_first;
330 let high = ((secret_words[2] ^ secret_words[3]).wrapping_sub(seed)) ^ input_last;
331 let mul_result = low.into_u128().wrapping_mul(high.into_u128());
332 let value = input
333 .len()
334 .into_u64()
335 .wrapping_add(low.swap_bytes())
336 .wrapping_add(high)
337 .wrapping_add(mul_result.lower_half() ^ mul_result.upper_half());
338
339 avalanche(value)
340}
341
342#[inline]
343fn impl_17_to_128_bytes(secret: &Secret, seed: u64, input: &[u8]) -> u64 {
344 assert_input_range!(17..=128, input.len());
345 let mut acc = input.len().into_u64().wrapping_mul(PRIME64_1);
346
347 impl_17_to_128_bytes_iter(secret, input, |fwd, bwd, secret| {
348 acc = acc.wrapping_add(mix_step(fwd, &secret[0], seed));
349 acc = acc.wrapping_add(mix_step(bwd, &secret[1], seed));
350 });
351
352 avalanche(acc)
353}
354
355#[inline(always)]
358fn prevent_autovectorization(acc: u64) -> u64 {
359 #[cfg(all(target_arch = "x86_64", not(miri)))]
360 {
361 let mut acc = acc;
362 unsafe {
369 core::arch::asm!(
370 "/* {0} */",
371 inout(reg) acc,
372 options(nomem, nostack, preserves_flags)
373 );
374 }
375 acc
376 }
377
378 #[cfg(not(all(target_arch = "x86_64", not(miri))))]
379 acc
380}
381
382#[inline]
383fn impl_129_to_240_bytes(secret: &Secret, seed: u64, input: &[u8]) -> u64 {
384 assert_input_range!(129..=240, input.len());
385 let mut acc = input.len().into_u64().wrapping_mul(PRIME64_1);
386
387 let (head, tail) = input.split_first_chunk::<128>().unwrap();
388 assert_input_range!(1..=112, tail.len());
389
390 let (head, _) = head.bp_as_chunks();
391 let (tail, _) = tail.bp_as_chunks();
392
393 let ss = secret.for_64().words_for_129_to_240_part1();
394 for (chunk, secret) in head.iter().zip(ss) {
395 acc = acc.wrapping_add(mix_step(chunk, secret, seed));
396 acc = prevent_autovectorization(acc);
397 }
398
399 acc = avalanche(acc);
400
401 let ss = secret.for_64().words_for_129_to_240_part2();
402 for (chunk, secret) in tail.iter().zip(ss) {
403 acc = acc.wrapping_add(mix_step(chunk, secret, seed));
404 acc = prevent_autovectorization(acc);
405 }
406
407 let last_chunk = input.last_chunk().unwrap();
408 let ss = secret.for_64().words_for_129_to_240_part3();
409 acc = acc.wrapping_add(mix_step(last_chunk, ss, seed));
410
411 avalanche(acc)
412}
413
414#[inline]
415fn impl_241_plus_bytes(secret: &Secret, input: &[u8]) -> u64 {
416 assert_input_range!(241.., input.len());
417 dispatch! {
418 fn oneshot_impl<>(secret: &Secret, input: &[u8]) -> u64
419 []
420 }
421}
422
423#[inline]
424fn oneshot_impl(vector: impl Vector, secret: &Secret, input: &[u8]) -> u64 {
425 Algorithm(vector).oneshot(secret, input, Finalize64)
426}
427
428#[cfg(test)]
429mod test {
430 use std::hash::Hasher as _;
431
432 use crate::xxhash3::test::bytes;
433
434 use super::*;
435
436 const _: () = {
437 const fn is_clone<T: Clone>() {}
438 is_clone::<Hasher>();
439 };
440
441 const EMPTY_BYTES: [u8; 0] = [];
442
443 fn hash_byte_by_byte(input: &[u8]) -> u64 {
444 let mut hasher = Hasher::new();
445 for byte in input.chunks(1) {
446 hasher.write(byte);
447 }
448 hasher.finish()
449 }
450
451 fn hash_byte_by_byte_with_seed(seed: u64, input: &[u8]) -> u64 {
452 let mut hasher = Hasher::with_seed(seed);
453 for byte in input.chunks(1) {
454 hasher.write(byte);
455 }
456 hasher.finish()
457 }
458
459 #[test]
460 fn oneshot_empty() {
461 let hash = Hasher::oneshot(&EMPTY_BYTES);
462 assert_eq!(hash, 0x2d06_8005_38d3_94c2);
463 }
464
465 #[test]
466 fn streaming_empty() {
467 let hash = hash_byte_by_byte(&EMPTY_BYTES);
468 assert_eq!(hash, 0x2d06_8005_38d3_94c2);
469 }
470
471 #[test]
472 fn oneshot_1_to_3_bytes() {
473 test_1_to_3_bytes(Hasher::oneshot);
474 }
475
476 #[test]
477 fn streaming_1_to_3_bytes() {
478 test_1_to_3_bytes(hash_byte_by_byte);
479 }
480
481 #[track_caller]
482 fn test_1_to_3_bytes(mut f: impl FnMut(&[u8]) -> u64) {
483 let inputs = bytes![1, 2, 3];
484
485 let expected = [
486 0xc44b_dff4_074e_ecdb,
487 0xd664_5fc3_051a_9457,
488 0x5f42_99fc_161c_9cbb,
489 ];
490
491 for (input, expected) in inputs.iter().zip(expected) {
492 let hash = f(input);
493 assert_eq!(hash, expected, "input was {} bytes", input.len());
494 }
495 }
496
497 #[test]
498 fn oneshot_4_to_8_bytes() {
499 test_4_to_8_bytes(Hasher::oneshot);
500 }
501
502 #[test]
503 fn streaming_4_to_8_bytes() {
504 test_4_to_8_bytes(hash_byte_by_byte);
505 }
506
507 #[track_caller]
508 fn test_4_to_8_bytes(mut f: impl FnMut(&[u8]) -> u64) {
509 let inputs = bytes![4, 5, 6, 7, 8];
510
511 let expected = [
512 0x60da_b036_a582_11f2,
513 0xb075_753a_84ca_0fbe,
514 0xa658_4d1d_9a6a_e704,
515 0x0cd2_084a_6240_6b69,
516 0x3a1c_2d7c_85af_88f8,
517 ];
518
519 for (input, expected) in inputs.iter().zip(expected) {
520 let hash = f(input);
521 assert_eq!(hash, expected, "input was {} bytes", input.len());
522 }
523 }
524
525 #[test]
526 fn oneshot_9_to_16_bytes() {
527 test_9_to_16_bytes(Hasher::oneshot);
528 }
529
530 #[test]
531 fn streaming_9_to_16_bytes() {
532 test_9_to_16_bytes(hash_byte_by_byte);
533 }
534
535 #[track_caller]
536 fn test_9_to_16_bytes(mut f: impl FnMut(&[u8]) -> u64) {
537 let inputs = bytes![9, 10, 11, 12, 13, 14, 15, 16];
538
539 let expected = [
540 0xe961_2598_145b_b9dc,
541 0xab69_a08e_f83d_8f77,
542 0x1cf3_96aa_4de6_198d,
543 0x5ace_6a51_1c10_894b,
544 0xb7a5_d8a8_309a_2cb9,
545 0x4cf4_5c94_4a9a_2237,
546 0x55ec_edc2_b87b_b042,
547 0x8355_e3a6_f617_70db,
548 ];
549
550 for (input, expected) in inputs.iter().zip(expected) {
551 let hash = f(input);
552 assert_eq!(hash, expected, "input was {} bytes", input.len());
553 }
554 }
555
556 #[test]
557 fn oneshot_17_to_128_bytes() {
558 test_17_to_128_bytes(Hasher::oneshot);
559 }
560
561 #[test]
562 fn streaming_17_to_128_bytes() {
563 test_17_to_128_bytes(hash_byte_by_byte);
564 }
565
566 #[track_caller]
567 fn test_17_to_128_bytes(mut f: impl FnMut(&[u8]) -> u64) {
568 let lower_boundary = bytes![17, 18, 19];
569 let chunk_boundary = bytes![31, 32, 33];
570 let upper_boundary = bytes![126, 127, 128];
571
572 let inputs = lower_boundary
573 .iter()
574 .chain(chunk_boundary)
575 .chain(upper_boundary);
576
577 let expected = [
578 0x9ef3_41a9_9de3_7328,
580 0xf691_2490_d4c0_eed5,
581 0x60e7_2614_3cf5_0312,
582 0x4f36_db8e_4df3_78fd,
584 0x3523_581f_e96e_4c05,
585 0xe68c_56ba_8899_1e58,
586 0x6c2a_9eb7_459c_dc61,
588 0x120b_9787_f842_5f2f,
589 0x85c6_174c_7ff4_c46b,
590 ];
591
592 for (input, expected) in inputs.zip(expected) {
593 let hash = f(input);
594 assert_eq!(hash, expected, "input was {} bytes", input.len());
595 }
596 }
597
598 #[test]
599 fn oneshot_129_to_240_bytes() {
600 test_129_to_240_bytes(Hasher::oneshot);
601 }
602
603 #[test]
604 fn streaming_129_to_240_bytes() {
605 test_129_to_240_bytes(hash_byte_by_byte);
606 }
607
608 #[track_caller]
609 fn test_129_to_240_bytes(mut f: impl FnMut(&[u8]) -> u64) {
610 let lower_boundary = bytes![129, 130, 131];
611 let upper_boundary = bytes![238, 239, 240];
612
613 let inputs = lower_boundary.iter().chain(upper_boundary);
614
615 let expected = [
616 0xec76_42b4_31ba_3e5a,
618 0x4d32_24b1_0090_8a87,
619 0xe57f_7ea6_741f_e3a0,
620 0x3044_9a0b_4899_dee9,
622 0x972b_14e3_c46f_214b,
623 0x375a_384d_957f_e865,
624 ];
625
626 for (input, expected) in inputs.zip(expected) {
627 let hash = f(input);
628 assert_eq!(hash, expected, "input was {} bytes", input.len());
629 }
630 }
631
632 #[test]
633 fn oneshot_241_plus_bytes() {
634 test_241_plus_bytes(Hasher::oneshot);
635 }
636
637 #[test]
638 fn streaming_241_plus_bytes() {
639 test_241_plus_bytes(hash_byte_by_byte);
640 }
641
642 #[track_caller]
643 fn test_241_plus_bytes(mut f: impl FnMut(&[u8]) -> u64) {
644 let inputs = bytes![241, 242, 243, 244, 1024, 10240];
645
646 let expected = [
647 0x02e8_cd95_421c_6d02,
648 0xddcb_33c4_9405_1832,
649 0x8835_f952_9193_e3dc,
650 0xbc17_c91e_c3cf_8d7f,
651 0xe5d7_8baf_a45b_2aa5,
652 0xbcd6_3266_df6e_2244,
653 ];
654
655 for (input, expected) in inputs.iter().zip(expected) {
656 let hash = f(input);
657 assert_eq!(hash, expected, "input was {} bytes", input.len());
658 }
659 }
660
661 #[test]
662 fn oneshot_with_seed() {
663 test_with_seed(Hasher::oneshot_with_seed);
664 }
665
666 #[test]
667 fn streaming_with_seed() {
668 test_with_seed(hash_byte_by_byte_with_seed);
669 }
670
671 #[track_caller]
672 fn test_with_seed(mut f: impl FnMut(u64, &[u8]) -> u64) {
673 let inputs = bytes![0, 1, 4, 9, 17, 129, 241, 1024];
674
675 let expected = [
676 0x4aed_e683_89c0_e311,
677 0x78fc_079a_75aa_f3c0,
678 0x1b73_06b8_9f25_4507,
679 0x7df7_627f_d1f9_39b6,
680 0x49ca_0fff_0950_1622,
681 0x2bfd_caec_30ff_3000,
682 0xf984_56bc_25be_0901,
683 0x2483_9f0f_cdf4_d078,
684 ];
685
686 for (input, expected) in inputs.iter().zip(expected) {
687 let hash = f(0xdead_cafe, input);
688 assert_eq!(hash, expected, "input was {} bytes", input.len());
689 }
690 }
691}