Skip to content

Commit

Permalink
primops: add builtins.convertHash and builtins.convertSRIHash
Browse files Browse the repository at this point in the history
  • Loading branch information
ShamrockLee committed May 25, 2023
1 parent 6e45702 commit 8324e57
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 0 deletions.
3 changes: 3 additions & 0 deletions doc/manual/src/release-notes/rl-next.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
The number of parallel downloads (also known as substitutions) has been separated from the [`--max-jobs` setting](../command-ref/conf-file.md#conf-max-jobs).
The new setting is called [`max-substitution-jobs`](../command-ref/conf-file.md#conf-max-substitution-jobs).
The number of parallel downloads is now set to 16 by default (previously, the default was 1 due to the coupling to build jobs).

- New built-in functions `builtins.convertHash` and `builtins.convertSRIHash` is introduced to convert a string of hash to the specified format, an eval-time analogy to `nix hash to-<format> [--type algorithm]`.
Aside from the standard SRI format, `builtins.convertSRIHash` also takes any hash in the form of `algo:body` used by some out-of-tree projects such as `nix-prefetch`.
82 changes: 82 additions & 0 deletions src/libexpr/primops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3664,6 +3664,88 @@ static RegisterPrimOp primop_hashString({
.fun = prim_hashString,
});

static void prim_convertHash(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto type = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.convertHash");
std::optional<HashType> ht = parseHashType(type);
if (!ht)
state.debugThrowLastTrace(Error({
.msg = hintfmt("unknown hash type '%1%'", type),
.errPos = state.positions[pos],
}));

auto base = state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument passed to builtins.convertHash");
Base hb;
if (base == "sri") {
hb = SRI;
} else if (base == "base16") {
hb = Base16;
} else if (base == "base32") {
hb = Base32;
} else if (base == "base64") {
hb = Base64;
} else {
state.debugThrowLastTrace(Error({
.msg = hintfmt("unknown hash format '%1%'", base),
.errPos = state.positions[pos],
}));
}

PathSet context; // discarded
auto s = state.forceString(*args[2], context, pos, "while evaluating the third argument passed to builtins.convertHash");

v.mkString(Hash::parseAny(s, ht).to_string(hb, hb == SRI));
}

static RegisterPrimOp primop_convertHash({
.name = "__convertHash",
.args = {"type", "base", "s"},
.doc = R"(
Return a *base* representation of the hash *s*. The hash algorithm
specified by *type* must be one of `"md5"`, `"sha1"`, `"sha256"` or
`"sha512"`. The hash format specified by *base* must be one of
`"base16"`, `"base32"`, `"base64"` or `"sri"`.
)",
.fun = prim_convertHash,
});

static void prim_convertSRIHash(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto base = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.convertSRIHash");
Base hb;
if (base == "sri") {
hb = SRI;
} else if (base == "base16") {
hb = Base16;
} else if (base == "base32") {
hb = Base32;
} else if (base == "base64") {
hb = Base64;
} else {
state.debugThrowLastTrace(Error({
.msg = hintfmt("unknown hash format '%1%'", base),
.errPos = state.positions[pos],
}));
}

PathSet context; // discarded
auto s = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.convertSRIHash");

v.mkString(Hash::parseAnyPrefixed(s).to_string(hb, hb == SRI));
}

static RegisterPrimOp primop_convertSRIHash({
.name = "__convertSRIHash",
.args = {"base", "s"},
.doc = R"(
Return a *base* representation of SRI hash *s*. The hash algorithm
specified by *type* must be one of `"md5"`, `"sha1"`, `"sha256"` or
`"sha512"`. Hashes prefixed with "${hashAlgo}:" are also supported
for compatibility reason.
)",
.fun = prim_convertSRIHash,
});

struct RegexCache
{
// TODO use C++20 transparent comparison when available
Expand Down
1 change: 1 addition & 0 deletions tests/lang/eval-okay-convertHash.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ hashesBase16 = [ "d41d8cd98f00b204e9800998ecf8427e" "6c69ee7f211c640419d5366cc076ae46" "bb3438fbabd460ea6dbd27d153e2233b" "da39a3ee5e6b4b0d3255bfef95601890afd80709" "cd54e8568c1b37cf1e5badb0779bcbf382212189" "6d12e10b1d331dad210e47fd25d4f260802b7e77" "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" "900a4469df00ccbfd0c145c6d1e4b7953dd0afafadd7534e3a4019e8d38fc663" "ad0387b3bd8652f730ca46d25f9c170af0fd589f42e7f23f5a9e6412d97d7e56" "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" "9d0886f8c6b389398a16257bc79780fab9831c7fc11c8ab07fa732cb7b348feade382f92617c9c5305fefba0af02ab5fd39a587d330997ff5bd0db19f7666653" "21644b72aa259e5a588cd3afbafb1d4310f4889680f6c83b9d531596a5a284f34dbebff409d23bcc86aee6bad10c891606f075c6f4755cb536da27db5693f3a7" ]; hashesBase32 = [ "3y8bwfr609h3lh9ch0izcqq7fl" "26mrvc0v1nslch8r0w45zywsbc" "1v4gi57l97pmnylq6lmgxkhd5v" "143xibwh31h9bvxzalr0sjvbbvpa6ffs" "i4hj30pkrfdpgc5dbcgcydqviibfhm6d" "fxz2p030yba2bza71qhss79k3l5y24kd" "0mdqa9w1p6cmli6976v4wi0sw9r4p5prkj7lzfd1877wk11c9c73" "0qy6iz9yh6a079757mxdmypx0gcmnzjd3ij5q78bzk00vxll82lh" "0mkygpci4r4yb8zz5rs2kxcgvw0a2yf5zlj6r8qgfll6pnrqf0xd" "0zdl9zrg8r3i9c1g90lgg9ip5ijzv3yhz91i0zzn3r8ap9ws784gkp9dk9j3aglhgf1amqb0pj21mh7h1nxcl18akqvvf7ggqsy30yg" "19ncrpp37dx0nzzjw4k6zaqkb9mzaq2myhgpzh5aff7qqcj5wwdxslg6ixwncm7gyq8l761gwf87fgsh2bwfyr52s53k2dkqvw8c24x" "2kz74snvckxldmmbisz9ikmy031d28cs6xfdbl6rhxx42glpyz4vww4lajrc5akklxwixl0js4g84233pxvmbykiic5m7i5m9r4nr11" ]; hashesBase64 = [ "1B2M2Y8AsgTpgAmY7PhCfg==" "bGnufyEcZAQZ1TZswHauRg==" "uzQ4+6vUYOptvSfRU+IjOw==" "2jmj7l5rSw0yVb/vlWAYkK/YBwk=" "zVToVowbN88eW62wd5vL84IhIYk=" "bRLhCx0zHa0hDkf9JdTyYIArfnc=" "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=" "kApEad8AzL/QwUXG0eS3lT3Qr6+t11NOOkAZ6NOPxmM=" "rQOHs72GUvcwykbSX5wXCvD9WJ9C5/I/Wp5kEtl9flY=" "z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==" "nQiG+MaziTmKFiV7x5eA+rmDHH/BHIqwf6cyy3s0j+reOC+SYXycUwX++6CvAqtf05pYfTMJl/9b0NsZ92ZmUw==" "IWRLcqolnlpYjNOvuvsdQxD0iJaA9sg7nVMVlqWihPNNvr/0CdI7zIau5rrRDIkWBvB1xvR1XLU22ifbVpPzpw==" ]; hashesSRI = [ "md5-1B2M2Y8AsgTpgAmY7PhCfg==" "md5-bGnufyEcZAQZ1TZswHauRg==" "md5-uzQ4+6vUYOptvSfRU+IjOw==" "sha1-2jmj7l5rSw0yVb/vlWAYkK/YBwk=" "sha1-zVToVowbN88eW62wd5vL84IhIYk=" "sha1-bRLhCx0zHa0hDkf9JdTyYIArfnc=" "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=" "sha256-kApEad8AzL/QwUXG0eS3lT3Qr6+t11NOOkAZ6NOPxmM=" "sha256-rQOHs72GUvcwykbSX5wXCvD9WJ9C5/I/Wp5kEtl9flY=" "sha512-z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==" "sha512-nQiG+MaziTmKFiV7x5eA+rmDHH/BHIqwf6cyy3s0j+reOC+SYXycUwX++6CvAqtf05pYfTMJl/9b0NsZ92ZmUw==" "sha512-IWRLcqolnlpYjNOvuvsdQxD0iJaA9sg7nVMVlqWihPNNvr/0CdI7zIau5rrRDIkWBvB1xvR1XLU22ifbVpPzpw==" ]; }
30 changes: 30 additions & 0 deletions tests/lang/eval-okay-convertHash.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
let
algos = [ "md5" "md5" "md5" "sha1" "sha1" "sha1" "sha256" "sha256" "sha256" "sha512" "sha512" "sha512" ];
hashesBase16 = import ./eval-okay-hashstring.exp;
map2 = f: { fsts, snds }: if fsts == [ ] then [ ] else [ (f (builtins.head fsts) (builtins.head snds)) ] ++ map2 f { fsts = builtins.tail fsts; snds = builtins.tail snds; };
map2' = f: fsts: snds: map2 f { inherit fsts snds; };
getOutputHashes = hashes: {
hashesBase16 = map2' (algo: builtins.convertHash algo "base16") algos hashesBase16;
hashesBase32 = map2' (algo: builtins.convertHash algo "base32") algos hashesBase16;
hashesBase64 = map2' (algo: builtins.convertHash algo "base64") algos hashesBase16;
hashesSRI = map2' (algo: builtins.convertHash algo "sri") algos hashesBase16;
};
getOutputHashesPrefixed = delim: hashes: {
hashesBase16 = map2' (algo: hashBody: builtins.convertSRIHash "base16" (algo + delim + hashBody)) algos hashesBase16;
hashesBase32 = map2' (algo: hashBody: builtins.convertSRIHash "base32" (algo + delim + hashBody)) algos hashesBase16;
hashesBase64 = map2' (algo: hashBody: builtins.convertSRIHash "base64" (algo + delim + hashBody)) algos hashesBase16;
hashesSRI = map2' (algo: hashBody: builtins.convertSRIHash "sri" (algo + delim + hashBody)) algos hashesBase16;
};
outputHashes = getOutputHashes hashesBase16;
in
# map2'`
assert map2' (s1: s2: s1 + s2) [ "a" "b" ] [ "c" "d" ] == [ "ac" "bd" ];
# standard SRI hashes
assert outputHashes.hashesSRI == (map2' (algo: hashBody: algo + "-" + hashBody) algos outputHashes.hashesBase64);
# hashesBase16
assert outputHashes.hashesBase16 == hashesBase16;
# without prefix
assert builtins.all (x: getOutputHashes x == outputHashes) (builtins.attrValues outputHashes);
# colon-separated
assert builtins.all (x: getOutputHashesPrefixed ":" x == outputHashes) (builtins.attrValues outputHashes);
outputHashes

0 comments on commit 8324e57

Please sign in to comment.