Skip to content

Commit da9c930

Browse files
authored
perf: switch md5 to xxhash (#262)
Closes #259.
1 parent ce20ad6 commit da9c930

File tree

8 files changed

+41
-26
lines changed

8 files changed

+41
-26
lines changed

Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ alloy-json-abi = { version = "0.8", features = ["serde_json"] }
4646
alloy-primitives = { version = "0.8", features = ["serde", "rand"] }
4747
cfg-if = "1.0"
4848
dunce = "1.0"
49-
md-5 = "0.10"
5049
memmap2 = "0.9"
5150
path-slash = "0.2"
5251
rayon = "1.8"

crates/artifacts/solc/Cargo.toml

+1-4
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ yansi.workspace = true
3131
tokio = { workspace = true, optional = true, features = ["fs"] }
3232
futures-util = { workspace = true, optional = true }
3333

34-
# checksum
35-
md-5 = { workspace = true, optional = true }
36-
3734
# walkdir
3835
walkdir = { workspace = true, optional = true }
3936

@@ -50,6 +47,6 @@ foundry-compilers-core = { workspace = true, features = ["test-utils"] }
5047

5148
[features]
5249
async = ["dep:tokio", "dep:futures-util"]
53-
checksum = ["dep:md-5"]
50+
checksum = ["foundry-compilers-core/hasher"]
5451
walkdir = ["dep:walkdir", "foundry-compilers-core/walkdir"]
5552
rayon = ["dep:rayon"]

crates/artifacts/solc/src/sources.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ impl Source {
204204
/// Generate a non-cryptographically secure checksum of the given source.
205205
#[cfg(feature = "checksum")]
206206
pub fn content_hash_of(src: &str) -> String {
207-
alloy_primitives::hex::encode(<md5::Md5 as md5::Digest>::digest(src))
207+
foundry_compilers_core::utils::unique_hash(src)
208208
}
209209
}
210210

crates/compilers/Cargo.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,14 @@ foundry-compilers-artifacts = { workspace = true, features = [
2020
"walkdir",
2121
"rayon",
2222
] }
23-
foundry-compilers-core = { workspace = true, features = ["regex"] }
23+
foundry-compilers-core = { workspace = true, features = ["hasher", "regex"] }
2424
serde.workspace = true
2525
semver.workspace = true
2626
alloy-primitives.workspace = true
2727
serde_json.workspace = true
2828
tracing.workspace = true
2929
alloy-json-abi.workspace = true
3030
rayon.workspace = true
31-
md-5.workspace = true
3231
thiserror.workspace = true
3332
path-slash.workspace = true
3433
yansi.workspace = true

crates/compilers/src/buildinfo.rs

+5-16
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
use crate::compilers::{
44
CompilationError, CompilerContract, CompilerInput, CompilerOutput, Language,
55
};
6-
use alloy_primitives::hex;
76
use foundry_compilers_core::{error::Result, utils};
8-
use md5::Digest;
97
use semver::Version;
108
use serde::{de::DeserializeOwned, Deserialize, Serialize};
119
use std::{
@@ -97,22 +95,13 @@ impl<L: Language> RawBuildInfo<L> {
9795
let version = input.version().clone();
9896
let build_context = BuildContext::new(input, output)?;
9997

100-
let mut hasher = md5::Md5::new();
101-
102-
hasher.update(ETHERS_FORMAT_VERSION);
103-
10498
let solc_short = format!("{}.{}.{}", version.major, version.minor, version.patch);
105-
hasher.update(&solc_short);
106-
hasher.update(version.to_string());
107-
10899
let input = serde_json::to_value(input)?;
109-
hasher.update(&serde_json::to_string(&input)?);
110-
111-
// create the hash for `{_format,solcVersion,solcLongVersion,input}`
112-
// N.B. this is not exactly the same as hashing the json representation of these values but
113-
// the must efficient one
114-
let result = hasher.finalize();
115-
let id = hex::encode(result);
100+
let id = utils::unique_hash_many([
101+
ETHERS_FORMAT_VERSION,
102+
&version.to_string(),
103+
&serde_json::to_string(&input)?,
104+
]);
116105

117106
let mut build_info = BTreeMap::new();
118107

crates/compilers/src/compile/output/mod.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -607,8 +607,7 @@ impl<C: Compiler> AggregatedCompilerOutput<C> {
607607
///
608608
/// There can be multiple `BuildInfo`, since we support multiple versions.
609609
///
610-
/// The created files have the md5 hash `{_format,solcVersion,solcLongVersion,input}` as their
611-
/// file name
610+
/// The created files have a unique identifier as their name.
612611
pub fn write_build_infos(&self, build_info_dir: &Path) -> Result<(), SolcError> {
613612
if self.build_infos.is_empty() {
614613
return Ok(());

crates/core/Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ serde_json.workspace = true
2323
serde.workspace = true
2424
thiserror.workspace = true
2525

26+
# hasher
27+
xxhash-rust = { version = "0.8", optional = true, default-features = false, features = [
28+
"xxh3",
29+
] }
30+
2631
# regex
2732
regex = { workspace = true, optional = true }
2833

@@ -46,6 +51,7 @@ tempfile.workspace = true
4651

4752
[features]
4853
async = ["dep:tokio"]
54+
hasher = ["dep:xxhash-rust"]
4955
project-util = ["dep:tempfile", "dep:fs_extra"]
5056
regex = ["dep:regex"]
5157
svm-solc = ["dep:svm", "dep:tokio"]

crates/core/src/utils/mod.rs

+26
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,32 @@ pub static SUPPORTS_BASE_PATH: Lazy<VersionReq> =
7878
pub static SUPPORTS_INCLUDE_PATH: Lazy<VersionReq> =
7979
Lazy::new(|| VersionReq::parse(">=0.8.8").unwrap());
8080

81+
/// A non-cryptographic hash function for creating unique identifiers.
82+
///
83+
/// The exact algorithm being used shouldn't matter.
84+
// See Hardhat: https://github.com/NomicFoundation/hardhat/blob/e9ab5332a5505a6d1fe9bfbc687f5f46bdff6dd7/packages/hardhat-core/src/internal/util/hash.ts#L1-L16
85+
#[cfg(feature = "hasher")]
86+
pub fn unique_hash(input: impl AsRef<[u8]>) -> String {
87+
encode_hash(xxhash_rust::xxh3::xxh3_64(input.as_ref()))
88+
}
89+
90+
/// A non-cryptographic hash function for creating unique identifiers.
91+
///
92+
/// See [`unique_hash`] for more details.
93+
#[cfg(feature = "hasher")]
94+
pub fn unique_hash_many(inputs: impl IntoIterator<Item = impl AsRef<[u8]>>) -> String {
95+
let mut hasher = xxhash_rust::xxh3::Xxh3Default::new();
96+
for input in inputs {
97+
hasher.update(input.as_ref());
98+
}
99+
encode_hash(hasher.digest())
100+
}
101+
102+
#[cfg(feature = "hasher")]
103+
fn encode_hash(x: u64) -> String {
104+
hex::encode(x.to_be_bytes())
105+
}
106+
81107
/// Move a range by a specified offset
82108
pub fn range_by_offset(range: &Range<usize>, offset: isize) -> Range<usize> {
83109
Range {

0 commit comments

Comments
 (0)