diff options
author | Padraic-O-Mhuiris <patrick.morris.310@gmail.com> | 2024-02-21T16·49+0000 |
---|---|---|
committer | Pádraic Ó Mhuiris <patrick.morris.310@gmail.com> | 2024-02-23T16·04+0000 |
commit | 5c3065b43a61a5fa019cbbb157984fc5eb81d439 (patch) | |
tree | 897a44fdb7da446413276861c13d2a2365ea5f4b /tvix | |
parent | ffb134398dedcae6cd13cdf49b2cd57d43793bda (diff) |
feat(tvix/eval): implement `builtins.hashString` r/7597
Implements md5, sha1, sha256 and sha512 using the related crates from the RustCrypto hashes project (https://github.com/RustCrypto/hashes) Change-Id: I00730dea44ec9ef85309edc27addab0ae88814b8 Reviewed-on: https://cl.tvl.fyi/c/depot/+/11005 Tested-by: BuildkiteCI Reviewed-by: aspen <root@gws.fyi>
Diffstat (limited to 'tvix')
-rw-r--r-- | tvix/Cargo.lock | 25 | ||||
-rw-r--r-- | tvix/Cargo.nix | 90 | ||||
-rw-r--r-- | tvix/eval/Cargo.toml | 4 | ||||
-rw-r--r-- | tvix/eval/docs/builtins.md | 2 | ||||
-rw-r--r-- | tvix/eval/src/builtins/mod.rs | 32 | ||||
-rw-r--r-- | tvix/eval/src/errors.rs | 10 | ||||
-rw-r--r-- | tvix/eval/src/tests/nix_tests/eval-okay-groupBy.exp (renamed from tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-groupBy.exp) | 0 | ||||
-rw-r--r-- | tvix/eval/src/tests/nix_tests/eval-okay-groupBy.nix (renamed from tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-groupBy.nix) | 2 | ||||
-rw-r--r-- | tvix/eval/src/tests/nix_tests/eval-okay-hashstring.exp (renamed from tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-hashstring.exp) | 0 | ||||
-rw-r--r-- | tvix/eval/src/tests/nix_tests/eval-okay-hashstring.nix (renamed from tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-hashstring.nix) | 0 | ||||
-rw-r--r-- | tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.exp | 1 | ||||
-rw-r--r-- | tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.nix | 6 | ||||
-rw-r--r-- | tvix/eval/src/value/string.rs | 6 |
13 files changed, 167 insertions, 11 deletions
diff --git a/tvix/Cargo.lock b/tvix/Cargo.lock index 585199440b1e..7995402ead4a 100644 --- a/tvix/Cargo.lock +++ b/tvix/Cargo.lock @@ -1417,6 +1417,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] name = "memchr" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2544,6 +2554,17 @@ dependencies = [ ] [[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] name = "sha2" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3346,12 +3367,14 @@ dependencies = [ "codemap", "codemap-diagnostic", "criterion", + "data-encoding", "dirs", "genawaiter", "imbl", "itertools 0.12.0", "lazy_static", "lexical-core", + "md-5", "os_str_bytes", "path-clean", "pretty_assertions", @@ -3362,6 +3385,8 @@ dependencies = [ "rstest", "serde", "serde_json", + "sha1", + "sha2", "smol_str", "tabwriter", "tempfile", diff --git a/tvix/Cargo.nix b/tvix/Cargo.nix index b0c36524e472..9ee11cbd94f7 100644 --- a/tvix/Cargo.nix +++ b/tvix/Cargo.nix @@ -4191,6 +4191,41 @@ rec { features = { }; resolvedDefaultFeatures = [ "default" ]; }; + "md-5" = rec { + crateName = "md-5"; + version = "0.10.6"; + edition = "2018"; + sha256 = "1kvq5rnpm4fzwmyv5nmnxygdhhb2369888a06gdc9pxyrzh7x7nq"; + libName = "md5"; + authors = [ + "RustCrypto Developers" + ]; + dependencies = [ + { + name = "cfg-if"; + packageId = "cfg-if"; + } + { + name = "digest"; + packageId = "digest"; + } + ]; + devDependencies = [ + { + name = "digest"; + packageId = "digest"; + features = [ "dev" ]; + } + ]; + features = { + "asm" = [ "md5-asm" ]; + "default" = [ "std" ]; + "md5-asm" = [ "dep:md5-asm" ]; + "oid" = [ "digest/oid" ]; + "std" = [ "digest/std" ]; + }; + resolvedDefaultFeatures = [ "default" "std" ]; + }; "memchr" = rec { crateName = "memchr"; version = "2.7.1"; @@ -7763,6 +7798,45 @@ rec { ]; }; + "sha1" = rec { + crateName = "sha1"; + version = "0.10.6"; + edition = "2018"; + sha256 = "1fnnxlfg08xhkmwf2ahv634as30l1i3xhlhkvxflmasi5nd85gz3"; + authors = [ + "RustCrypto Developers" + ]; + dependencies = [ + { + name = "cfg-if"; + packageId = "cfg-if"; + } + { + name = "cpufeatures"; + packageId = "cpufeatures"; + target = { target, features }: (("aarch64" == target."arch" or null) || ("x86" == target."arch" or null) || ("x86_64" == target."arch" or null)); + } + { + name = "digest"; + packageId = "digest"; + } + ]; + devDependencies = [ + { + name = "digest"; + packageId = "digest"; + features = [ "dev" ]; + } + ]; + features = { + "asm" = [ "sha1-asm" ]; + "default" = [ "std" ]; + "oid" = [ "digest/oid" ]; + "sha1-asm" = [ "dep:sha1-asm" ]; + "std" = [ "digest/std" ]; + }; + resolvedDefaultFeatures = [ "default" "std" ]; + }; "sha2" = rec { crateName = "sha2"; version = "0.10.8"; @@ -10494,6 +10568,10 @@ rec { packageId = "codemap-diagnostic"; } { + name = "data-encoding"; + packageId = "data-encoding"; + } + { name = "dirs"; packageId = "dirs"; } @@ -10521,6 +10599,10 @@ rec { features = [ "format" "parse-floats" ]; } { + name = "md-5"; + packageId = "md-5"; + } + { name = "os_str_bytes"; packageId = "os_str_bytes"; features = [ "conversions" ]; @@ -10558,6 +10640,14 @@ rec { packageId = "serde_json"; } { + name = "sha1"; + packageId = "sha1"; + } + { + name = "sha2"; + packageId = "sha2"; + } + { name = "smol_str"; packageId = "smol_str"; } diff --git a/tvix/eval/Cargo.toml b/tvix/eval/Cargo.toml index 4f4551a349d2..e2bd07f8279f 100644 --- a/tvix/eval/Cargo.toml +++ b/tvix/eval/Cargo.toml @@ -33,6 +33,10 @@ tabwriter = "1.2" test-strategy = { version = "0.2.1", optional = true } toml = "0.6.0" xml-rs = "0.8.4" +sha2 = "0.10.8" +sha1 = "0.10.6" +md-5 = "0.10.6" +data-encoding = "2.5.0" [dev-dependencies] criterion = "0.5" diff --git a/tvix/eval/docs/builtins.md b/tvix/eval/docs/builtins.md index 39b59437e26b..eff761c7057d 100644 --- a/tvix/eval/docs/builtins.md +++ b/tvix/eval/docs/builtins.md @@ -66,7 +66,7 @@ The `impl` column indicates implementation status in tvix: | hasAttr | false | | | | | hasContext | false | | | | | hashFile | false | | false | todo | -| hashString | false | | | todo | +| hashString | false | | | | | head | false | | | | | import | true | | | | | intersectAttrs | false | | | | diff --git a/tvix/eval/src/builtins/mod.rs b/tvix/eval/src/builtins/mod.rs index 131f2b7bb201..119c0bda2dc3 100644 --- a/tvix/eval/src/builtins/mod.rs +++ b/tvix/eval/src/builtins/mod.rs @@ -5,9 +5,14 @@ use bstr::{ByteSlice, ByteVec}; use builtin_macros::builtins; +use data_encoding::HEXLOWER; use genawaiter::rc::Gen; use imbl::OrdMap; +use md5::Md5; use regex::Regex; +use sha1::Sha1; +use sha2::digest::Output; +use sha2::{Digest, Sha256, Sha512}; use std::cmp::{self, Ordering}; use std::collections::VecDeque; use std::collections::{BTreeMap, HashSet}; @@ -686,15 +691,24 @@ mod pure_builtins { #[builtin("hashString")] #[allow(non_snake_case)] - async fn builtin_hashString( - co: GenCo, - _algo: Value, - _string: Value, - ) -> Result<Value, ErrorKind> { - // FIXME: propagate contexts here. - Ok(Value::from(CatchableErrorKind::UnimplementedFeature( - "hashString".into(), - ))) + async fn builtin_hashString(co: GenCo, algo: Value, s: Value) -> Result<Value, ErrorKind> { + fn hash<D: Digest>(b: &[u8]) -> Output<D> { + let mut hasher = D::new(); + hasher.update(b); + hasher.finalize() + } + + let s = s.to_str()?; + + let encoded_hash = match algo.to_str()?.as_bytes() { + b"md5" => HEXLOWER.encode(hash::<Md5>(&s).as_bstr()), + b"sha1" => HEXLOWER.encode(hash::<Sha1>(&s).as_bstr()), + b"sha256" => HEXLOWER.encode(hash::<Sha256>(&s).as_bstr()), + b"sha512" => HEXLOWER.encode(hash::<Sha512>(&s).as_bstr()), + _ => return Err(ErrorKind::UnknownHashType(s.into())), + }; + + Ok(Value::from(encoded_hash)) } #[builtin("head")] diff --git a/tvix/eval/src/errors.rs b/tvix/eval/src/errors.rs index db02093b8d65..652252dadfa0 100644 --- a/tvix/eval/src/errors.rs +++ b/tvix/eval/src/errors.rs @@ -229,6 +229,10 @@ pub enum ErrorKind { /// tvix-eval when returning a result to the user, never inside of /// eval code. CatchableError(CatchableErrorKind), + + /// Invalid hash type specified, must be one of "md5", "sha1", "sha256" + /// or "sha512" + UnknownHashType(String), } impl error::Error for Error { @@ -533,6 +537,10 @@ to a missing value in the attribute set(s) included via `with`."#, ErrorKind::CatchableError(inner) => { write!(f, "{}", inner) } + + ErrorKind::UnknownHashType(hash_type) => { + write!(f, "unknown hash type '{}'", hash_type) + } } } } @@ -821,6 +829,7 @@ impl Error { | ErrorKind::TvixBug { .. } | ErrorKind::NotImplemented(_) | ErrorKind::WithContext { .. } + | ErrorKind::UnknownHashType(_) | ErrorKind::CatchableError(_) => return None, }; @@ -866,6 +875,7 @@ impl Error { ErrorKind::NotSerialisableToJson(_) => "E036", ErrorKind::UnexpectedContext => "E037", ErrorKind::Utf8 => "E038", + ErrorKind::UnknownHashType(_) => "E039", // Special error code for errors from other Tvix // components. We may want to introduce a code namespacing diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-groupBy.exp b/tvix/eval/src/tests/nix_tests/eval-okay-groupBy.exp index bfca5652a59b..bfca5652a59b 100644 --- a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-groupBy.exp +++ b/tvix/eval/src/tests/nix_tests/eval-okay-groupBy.exp diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-groupBy.nix b/tvix/eval/src/tests/nix_tests/eval-okay-groupBy.nix index 7e0eab28b036..862d89dbd670 100644 --- a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-groupBy.nix +++ b/tvix/eval/src/tests/nix_tests/eval-okay-groupBy.nix @@ -1,4 +1,4 @@ -with import ./../lib.nix; +with import ./lib.nix; builtins.groupBy (n: builtins.substring 0 1 (builtins.hashString "sha256" (toString n)) diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-hashstring.exp b/tvix/eval/src/tests/nix_tests/eval-okay-hashstring.exp index d720a082ddb3..d720a082ddb3 100644 --- a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-hashstring.exp +++ b/tvix/eval/src/tests/nix_tests/eval-okay-hashstring.exp diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-hashstring.nix b/tvix/eval/src/tests/nix_tests/eval-okay-hashstring.nix index b0f62b245ca8..b0f62b245ca8 100644 --- a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-hashstring.nix +++ b/tvix/eval/src/tests/nix_tests/eval-okay-hashstring.nix diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.exp new file mode 100644 index 000000000000..e00b80e561fb --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.exp @@ -0,0 +1 @@ +[ "8a0614b4eaa4cffb7515ec101847e198" "8bd218cf61321d8aa05b3602b99f90d2d8cef3d6" "80ac06d74cb6c5d14af718ce8c3c1255969a1a595b76a3cf92354a95331a879a" "0edac513b6b0454705b553deda4c9b055da0939d26d2f73548862817ebeac5378cf64ff7a752ce1a0590a736735d3bbd9e8a7f04d93617cdf514313f5ab5baa4" ] diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.nix new file mode 100644 index 000000000000..aed723d3670a --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.nix @@ -0,0 +1,6 @@ +[ + (builtins.hashString "md5" "tvix") + (builtins.hashString "sha1" "tvix") + (builtins.hashString "sha256" "tvix") + (builtins.hashString "sha512" "tvix") +] diff --git a/tvix/eval/src/value/string.rs b/tvix/eval/src/value/string.rs index 2f1592546b85..6ce0d190c0a0 100644 --- a/tvix/eval/src/value/string.rs +++ b/tvix/eval/src/value/string.rs @@ -524,6 +524,12 @@ impl<'a> From<&'a NixString> for &'a BStr { } } +impl From<NixString> for String { + fn from(s: NixString) -> Self { + s.to_string() + } +} + impl From<NixString> for BString { fn from(s: NixString) -> Self { s.as_bstr().to_owned() |