feat(tvix/eval): implement builtins.hashString
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>
This commit is contained in:
parent
ffb134398d
commit
5c3065b43a
14 changed files with 263 additions and 11 deletions
25
tvix/Cargo.lock
generated
25
tvix/Cargo.lock
generated
|
|
@ -1416,6 +1416,16 @@ version = "0.7.3"
|
|||
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"
|
||||
|
|
@ -2543,6 +2553,17 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
@ -10493,6 +10567,10 @@ rec {
|
|||
name = "codemap-diagnostic";
|
||||
packageId = "codemap-diagnostic";
|
||||
}
|
||||
{
|
||||
name = "data-encoding";
|
||||
packageId = "data-encoding";
|
||||
}
|
||||
{
|
||||
name = "dirs";
|
||||
packageId = "dirs";
|
||||
|
|
@ -10520,6 +10598,10 @@ rec {
|
|||
packageId = "lexical-core";
|
||||
features = [ "format" "parse-floats" ];
|
||||
}
|
||||
{
|
||||
name = "md-5";
|
||||
packageId = "md-5";
|
||||
}
|
||||
{
|
||||
name = "os_str_bytes";
|
||||
packageId = "os_str_bytes";
|
||||
|
|
@ -10557,6 +10639,14 @@ rec {
|
|||
name = "serde_json";
|
||||
packageId = "serde_json";
|
||||
}
|
||||
{
|
||||
name = "sha1";
|
||||
packageId = "sha1";
|
||||
}
|
||||
{
|
||||
name = "sha2";
|
||||
packageId = "sha2";
|
||||
}
|
||||
{
|
||||
name = "smol_str";
|
||||
packageId = "smol_str";
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 | | | |
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
with import ./../lib.nix;
|
||||
with import ./lib.nix;
|
||||
|
||||
builtins.groupBy (n:
|
||||
builtins.substring 0 1 (builtins.hashString "sha256" (toString n))
|
||||
|
|
@ -0,0 +1 @@
|
|||
[ "8a0614b4eaa4cffb7515ec101847e198" "8bd218cf61321d8aa05b3602b99f90d2d8cef3d6" "80ac06d74cb6c5d14af718ce8c3c1255969a1a595b76a3cf92354a95331a879a" "0edac513b6b0454705b553deda4c9b055da0939d26d2f73548862817ebeac5378cf64ff7a752ce1a0590a736735d3bbd9e8a7f04d93617cdf514313f5ab5baa4" ]
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
[
|
||||
(builtins.hashString "md5" "tvix")
|
||||
(builtins.hashString "sha1" "tvix")
|
||||
(builtins.hashString "sha256" "tvix")
|
||||
(builtins.hashString "sha512" "tvix")
|
||||
]
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue