Skip to content

Commit fa01589

Browse files
authored
fix(hash): fix base64 digest and hash salt (#13977)
* base64 encoding with base64 crate Use the base64 crate for standard and URL-safe base64 encoding instead of custom implementations. This improves maintainability and reduces potential bugs in the encoding logic. * fix(hash): fix hash salt not working * test(hash): add base64 hash tests * fix: use base64-simd for hash digests * test(hash): move hash tests to integration target
1 parent 4e0ad16 commit fa01589

4 files changed

Lines changed: 47 additions & 7 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/rspack_hash/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ repository = "https://github.com/web-infra-dev/rspack"
77
version.workspace = true
88

99
[dependencies]
10+
base64-simd = { workspace = true }
1011
md4 = { workspace = true }
1112
rspack_cacheable = { workspace = true }
1213
sha2 = { workspace = true }

crates/rspack_hash/src/lib.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::{
33
hash::{Hash, Hasher},
44
};
55

6+
use base64_simd::{STANDARD, URL_SAFE_NO_PAD};
67
use md4::Digest;
78
use rspack_cacheable::{cacheable, with::AsPreset};
89
use smol_str::SmolStr;
@@ -103,8 +104,8 @@ impl RspackHash {
103104

104105
pub fn with_salt(function: &HashFunction, salt: &HashSalt) -> Self {
105106
let mut this = Self::new(function);
106-
if !matches!(salt, HashSalt::None) {
107-
salt.hash(&mut this);
107+
if let HashSalt::Salt(salt) = salt {
108+
this.write(salt.as_bytes());
108109
}
109110
this
110111
}
@@ -201,8 +202,8 @@ impl RspackHashDigest {
201202
let s = hex(inner, &mut buf);
202203
s.into()
203204
}
204-
HashDigest::Base64 => encode_base_n(inner, BASE64_CHARSET).into(),
205-
HashDigest::Base64Url => encode_base_n(inner, BASE64URL_CHARSET).into(),
205+
HashDigest::Base64 => STANDARD.encode_to_string(inner).into(),
206+
HashDigest::Base64Url => URL_SAFE_NO_PAD.encode_to_string(inner).into(),
206207
HashDigest::Base62 => encode_base_n(inner, BASE62_CHARSET).into(),
207208
HashDigest::Base58 => encode_base_n(inner, BASE58_CHARSET).into(),
208209
HashDigest::Base52 => encode_base_n(inner, BASE52_CHARSET).into(),
@@ -267,9 +268,6 @@ const BASE49_CHARSET: &[u8] = b"abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXY
267268
const BASE52_CHARSET: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
268269
const BASE58_CHARSET: &[u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
269270
const BASE62_CHARSET: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
270-
const BASE64_CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
271-
const BASE64URL_CHARSET: &[u8] =
272-
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
273271

274272
/// Encodes raw hash bytes into a base-N string using the given charset.
275273
/// Matches webpack's `encode` in `hash-digest.js`: interprets the buffer as a

crates/rspack_hash/tests/hash.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use std::hash::Hasher;
2+
3+
use rspack_hash::{HashDigest, HashFunction, HashSalt, RspackHash, RspackHashDigest};
4+
5+
#[test]
6+
fn encodes_base64_with_standard_padding() {
7+
let digest = RspackHashDigest::new(b"\xfb\xef\xff", &HashDigest::Base64);
8+
9+
assert_eq!(digest.encoded(), "++//");
10+
11+
let digest = RspackHashDigest::new(b"hello", &HashDigest::Base64);
12+
13+
assert_eq!(digest.encoded(), "aGVsbG8=");
14+
}
15+
16+
#[test]
17+
fn encodes_base64url_without_padding() {
18+
let digest = RspackHashDigest::new(b"\xfb\xef\xff", &HashDigest::Base64Url);
19+
20+
assert_eq!(digest.encoded(), "--__");
21+
22+
let digest = RspackHashDigest::new(b"hello", &HashDigest::Base64Url);
23+
24+
assert_eq!(digest.encoded(), "aGVsbG8");
25+
}
26+
27+
#[test]
28+
fn hash_salt_is_written_as_raw_bytes() {
29+
let salt = HashSalt::Salt("salt".to_string());
30+
let salted = RspackHash::with_salt(&HashFunction::Xxhash64, &salt)
31+
.digest(&HashDigest::Hex)
32+
.encoded()
33+
.to_string();
34+
35+
let mut expected = RspackHash::new(&HashFunction::Xxhash64);
36+
expected.write(b"salt");
37+
let expected = expected.digest(&HashDigest::Hex).encoded().to_string();
38+
39+
assert_eq!(salted, expected);
40+
}

0 commit comments

Comments
 (0)