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};