Skip to main content

turbo_tasks_hash/
lib.rs

1//! Hashing and encoding functions for turbopack.
2//!
3//! An example use of this module is hashing a file's content for cache
4//! invalidation, and encoding the hash to a base38 or hexadecimal string for
5//! use in a file name.
6
7mod base38;
8mod base64;
9mod deterministic_hash;
10mod hex;
11mod sha;
12mod xxh3_hash128;
13mod xxh3_hash64;
14
15use bincode::{Decode, Encode};
16
17#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Decode, Encode)]
18pub enum HashAlgorithm {
19    /// The default hash algorithm is using xxh3, which is a fast non-cryptographic hash function.
20    #[default]
21    Xxh3Hash64Hex,
22    Xxh3Hash128Hex,
23    /// xxh3 64-bit hash encoded as a 13-character base38 string (0-9 a-z _ -)
24    Xxh3Hash64Base38,
25    /// xxh3 128-bit hash encoded as a 25-character base38 string (0-9 a-z _ -)
26    Xxh3Hash128Base38,
27    /// Used for [Subresource Integrity (SRI)][sri].
28    ///
29    /// [sri]: https://nextjs.org/docs/app/guides/content-security-policy#enabling-sri
30    Sha256Base64,
31    /// Used for [Subresource Integrity (SRI)][sri].
32    ///
33    /// [sri]: https://nextjs.org/docs/app/guides/content-security-policy#enabling-sri
34    Sha384Base64,
35    /// Used for [Subresource Integrity (SRI)][sri].
36    ///
37    /// [sri]: https://nextjs.org/docs/app/guides/content-security-policy#enabling-sri
38    Sha512Base64,
39}
40
41/// Feed `salt` (if non-empty) then `input` into `hasher` in a single pass, then return it.
42/// An empty salt writes zero bytes, which produces the same result as calling with no prefix.
43fn feed<H: DeterministicHasher, T: DeterministicHash>(mut h: H, salt: &str, input: T) -> H {
44    h.write_bytes(salt.as_bytes());
45    input.deterministic_hash(&mut h);
46    h
47}
48
49/// Hash `input` with `algorithm`. If `salt` is non-empty it is written into
50/// the hasher before the content so the two are mixed in a single pass —
51/// never as a hash-of-hash composition. An empty salt produces the same
52/// result as hashing without a prefix.
53pub fn deterministic_hash<T: DeterministicHash>(
54    salt: &str,
55    input: T,
56    algorithm: HashAlgorithm,
57) -> String {
58    // Each arm feeds salt+input into the appropriate hasher and encodes the output.
59    // The inherent finish() methods on the hasher types are used (not the trait method,
60    // which panics for 128-bit and SHA hashers).
61    match algorithm {
62        HashAlgorithm::Xxh3Hash64Hex => {
63            encode_hex(feed(Xxh3Hash64Hasher::new(), salt, &input).finish())
64        }
65        HashAlgorithm::Xxh3Hash128Hex => {
66            encode_hex_128(feed(Xxh3Hash128Hasher::new(), salt, &input).finish())
67        }
68        HashAlgorithm::Xxh3Hash64Base38 => {
69            encode_base38(feed(Xxh3Hash64Hasher::new(), salt, &input).finish())
70        }
71        HashAlgorithm::Xxh3Hash128Base38 => {
72            encode_base38_128(feed(Xxh3Hash128Hasher::new(), salt, &input).finish())
73        }
74        HashAlgorithm::Sha256Base64 => feed(ShaHasher::new_sha256(), salt, &input).finish_base64(),
75        HashAlgorithm::Sha384Base64 => feed(ShaHasher::new_sha384(), salt, &input).finish_base64(),
76        HashAlgorithm::Sha512Base64 => feed(ShaHasher::new_sha512(), salt, &input).finish_base64(),
77    }
78}
79
80pub use crate::{
81    base38::{BASE38_LEN_64, BASE38_LEN_128, encode_base38, encode_base38_128},
82    base64::encode_base64,
83    deterministic_hash::{DeterministicHash, DeterministicHasher},
84    hex::{encode_hex, encode_hex_128},
85    sha::ShaHasher,
86    xxh3_hash64::{Xxh3Hash64Hasher, hash_xxh3_hash64},
87    xxh3_hash128::{Xxh3Hash128Hasher, hash_xxh3_hash128},
88};