Skip to main content

twox_hash/
xxhash3_64.rs

1//! The implementation of XXH3_64.
2
3#![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/// Calculates the 64-bit hash.
22#[derive(Clone)]
23pub struct Hasher {
24    #[cfg(feature = "alloc")]
25    inner: AllocRawHasher,
26    _private: (),
27}
28
29impl Hasher {
30    /// Hash all data at once. If you can use this function, you may
31    /// see noticable speed gains for certain types of input.
32    #[must_use]
33    #[inline]
34    pub fn oneshot(input: &[u8]) -> u64 {
35        // Hashing a short input is latency sensitive, so the
36        // bulkier code for long inputs is kept in a separate
37        // function.
38        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                // Re-establish information from our `if` statement
48                // that is lost because we use `inline(never)`.
49                //
50                // SAFETY: this nested function is defined and called
51                // only once in the corresponding `else` branch.
52                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    /// Hash all data at once using the provided seed and a secret
63    /// derived from the seed. If you can use this function, you may
64    /// see noticable speed gains for certain types of input.
65    #[must_use]
66    #[inline]
67    pub fn oneshot_with_seed(seed: u64, input: &[u8]) -> u64 {
68        // Below the cutoff, the secret derived from the seed goes
69        // unread. Above the cutoff, deriving the secret with the
70        // default seed is a no-op. In both cases, we can use the
71        // default secret instead of doing the work to derive another.
72        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            // Deriving the secret from the seed takes a good chunk of
80            // stack space. Moving that work to a separate function
81            // drastically improves speed for the cases <= 128 bytes.
82            #[inline(never)]
83            fn outline(seed: u64, input: &[u8]) -> u64 {
84                // Re-establish information from our `if` statement
85                // that is lost because we use `inline(never)`.
86                //
87                // SAFETY: this nested function is defined and called
88                // only once in the corresponding `else` branch.
89                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    /// Hash all data at once using the provided secret and the
106    /// default seed. If you can use this function, you may see
107    /// noticable speed gains for certain types of input.
108    #[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    /// Hash all data at once using the provided seed and secret. If
115    /// you can use this function, you may see noticable speed gains
116    /// for certain types of input.
117    #[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        /// Constructs the hasher using the default seed and secret values.
142        pub fn new() -> Self {
143            Self {
144                inner: RawHasherCore::allocate_default(),
145                _private: (),
146            }
147        }
148
149        /// Constructs the hasher using the provided seed and a secret
150        /// derived from the seed.
151        pub fn with_seed(seed: u64) -> Self {
152            Self {
153                inner: RawHasherCore::allocate_with_seed(seed),
154                _private: (),
155            }
156        }
157
158        /// Constructs the hasher using the provided seed and secret.
159        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        /// Returns the secret.
170        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)]
195/// A lower-level interface for computing a hash from streaming data.
196///
197/// The algorithm requires a secret which can be a reasonably large
198/// piece of data. [`Hasher`][] makes one concrete implementation
199/// decision that uses dynamic memory allocation, but specialized
200/// usages may desire more flexibility. This type, combined with
201/// [`SecretBuffer`][], offer that flexibility at the cost of a
202/// generic type.
203pub struct RawHasher<S>(RawHasherCore<S>);
204
205impl<S> RawHasher<S> {
206    /// Construct the hasher with the provided seed, secret, and
207    /// temporary buffer.
208    pub fn new(secret_buffer: SecretBuffer<S>) -> Self {
209        Self(RawHasherCore::new(secret_buffer))
210    }
211
212    /// Returns the secret.
213    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    // FUTURE: TEST: "Note that the XXH3-64 result is the lower half of XXH3-128 result."
295    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/// Keeps `acc` in a regular register, which blocks the compiler from
356/// auto-vectorizing.
357#[inline(always)]
358fn prevent_autovectorization(acc: u64) -> u64 {
359    #[cfg(all(target_arch = "x86_64", not(miri)))]
360    {
361        let mut acc = acc;
362        // This mirrors `XXH_COMPILER_GUARD(var)`. Unlike
363        // `hint::black_box`, the value is not forced to the stack, so
364        // no load or store is added.
365        //
366        // SAFETY: This assembly doesn't *do* anything, other than add
367        // a constraint that the argument should be in a register.
368        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            // lower_boundary
579            0x9ef3_41a9_9de3_7328,
580            0xf691_2490_d4c0_eed5,
581            0x60e7_2614_3cf5_0312,
582            // chunk_boundary
583            0x4f36_db8e_4df3_78fd,
584            0x3523_581f_e96e_4c05,
585            0xe68c_56ba_8899_1e58,
586            // upper_boundary
587            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            // lower_boundary
617            0xec76_42b4_31ba_3e5a,
618            0x4d32_24b1_0090_8a87,
619            0xe57f_7ea6_741f_e3a0,
620            // upper_boundary
621            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}