From dc952f9c43c431eb32ce3796d47eb0d3258a2fc5 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 11 Feb 2024 15:56:04 -0500 Subject: [PATCH] Add `blake3` and `blake3_file` functions (#1860) --- Cargo.lock | 97 ++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + README.md | 10 +++-- src/function.rs | 23 +++++++++-- tests/functions.rs | 24 ++++++++++++ 5 files changed, 148 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a6ebcf..5f97feb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,6 +68,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "atty" version = "0.2.14" @@ -91,6 +103,21 @@ version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +[[package]] +name = "blake3" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "memmap2", + "rayon", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -117,6 +144,15 @@ version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -145,6 +181,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + [[package]] name = "cpufeatures" version = "0.2.12" @@ -163,6 +205,31 @@ dependencies = [ "rustversion", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + [[package]] name = "crypto-common" version = "0.1.6" @@ -389,6 +456,7 @@ version = "1.24.0" dependencies = [ "ansi_term", "atty", + "blake3", "camino", "clap", "cradle", @@ -476,6 +544,15 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "memmap2" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6" +dependencies = [ + "libc", +] + [[package]] name = "nix" version = "0.27.1" @@ -582,6 +659,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.4.1" diff --git a/Cargo.toml b/Cargo.toml index 02cbbe0..7d86799 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ members = [".", "bin/ref-type", "bin/generate-book", "bin/update-contributors"] [dependencies] ansi_term = "0.12.0" atty = "0.2.0" +blake3 = { version = "1.5.0", features = ["rayon", "mmap"] } camino = "1.0.4" clap = { version = "2.33.0", features = ["wrap_help"] } ctrlc = { version = "3.1.1", features = ["termination"] } diff --git a/README.md b/README.md index 8a1ddbc..46a93e6 100644 --- a/README.md +++ b/README.md @@ -1456,12 +1456,16 @@ which will halt execution. #### UUID and Hash Generation -- `sha256(string)` - Return the SHA-256 hash of `string` as a hexadecimal +- `blake3(string)`master - Return [BLAKE3] hash of `string` as hexadecimal string. +- `blake3_file(path)`master - Return [BLAKE3] hash of file at `path` as hexadecimal + string. +- `sha256(string)` - Return the SHA-256 hash of `string` as hexadecimal string. +- `sha256_file(path)` - Return SHA-256 hash of file at `path` as hexadecimal string. -- `sha256_file(path)` - Return the SHA-256 hash of the file at `path` as a - hexadecimal string. - `uuid()` - Generate a random version 4 UUID. +[BLAKE3]: https://github.com/BLAKE3-team/BLAKE3/ + #### Semantic Versions - `semver_matches(version, requirement)`1.16.0 - Check whether a diff --git a/src/function.rs b/src/function.rs index 1262076..fee4bfc 100644 --- a/src/function.rs +++ b/src/function.rs @@ -21,6 +21,8 @@ pub(crate) fn get(name: &str) -> Option { let function = match name { "absolute_path" => Unary(absolute_path), "arch" => Nullary(arch), + "blake3" => Unary(blake3), + "blake3_file" => Unary(blake3_file), "canonicalize" => Unary(canonicalize), "cache_directory" => Nullary(|_| dir("cache", dirs::cache_dir)), "capitalize" => Unary(capitalize), @@ -107,6 +109,19 @@ fn arch(_context: &FunctionContext) -> Result { Ok(target::arch().to_owned()) } +fn blake3(_context: &FunctionContext, s: &str) -> Result { + Ok(blake3::hash(s.as_bytes()).to_string()) +} + +fn blake3_file(context: &FunctionContext, path: &str) -> Result { + let path = context.search.working_directory.join(path); + let mut hasher = blake3::Hasher::new(); + hasher + .update_mmap_rayon(&path) + .map_err(|err| format!("Failed to hash `{}`: {err}", path.display()))?; + Ok(hasher.finalize().to_string()) +} + fn canonicalize(_context: &FunctionContext, path: &str) -> Result { let canonical = std::fs::canonicalize(path).map_err(|err| format!("I/O error canonicalizing path: {err}"))?; @@ -377,12 +392,12 @@ fn sha256(_context: &FunctionContext, s: &str) -> Result { fn sha256_file(context: &FunctionContext, path: &str) -> Result { use sha2::{Digest, Sha256}; - let justpath = context.search.working_directory.join(path); + let path = context.search.working_directory.join(path); let mut hasher = Sha256::new(); - let mut file = fs::File::open(&justpath) - .map_err(|err| format!("Failed to open file at `{:?}`: {err}", justpath.to_str()))?; + let mut file = + fs::File::open(&path).map_err(|err| format!("Failed to open `{}`: {err}", path.display()))?; std::io::copy(&mut file, &mut hasher) - .map_err(|err| format!("Failed to read file at `{:?}`: {err}", justpath.to_str()))?; + .map_err(|err| format!("Failed to read `{}`: {err}", path.display()))?; let hash = hasher.finalize(); Ok(format!("{hash:x}")) } diff --git a/tests/functions.rs b/tests/functions.rs index c356524..2c0e814 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -663,6 +663,30 @@ fn just_pid() { assert_eq!(stdout.parse::().unwrap(), pid); } +#[test] +fn blake3() { + Test::new() + .justfile("x := blake3('5943ee37-0000-1000-8000-010203040506')") + .args(["--evaluate", "x"]) + .stdout("026c9f740a793ff536ddf05f8915ea4179421f47f0fa9545476076e9ba8f3f2b") + .run(); +} + +#[test] +fn blake3_file() { + Test::new() + .justfile("x := blake3_file('sub/blakefile')") + .tree(tree! { + sub: { + blakefile: "just is great\n", + } + }) + .current_dir("sub") + .args(["--evaluate", "x"]) + .stdout("8379241877190ca4b94076a8c8f89fe5747f95c62f3e4bf41f7408a0088ae16d") + .run(); +} + #[cfg(unix)] #[test] fn canonicalize() {