Merge pull request #9747 from awakesecurity/mz/fix-quadratic-splitString
Fix performance of builtins.substring for empty substrings (cherry picked from commit b2deff1947c2fe57fdbf1a472eb9003eb407f8d3) Change-Id: I4ddfc8d26a4781c9520fff9807849a073ee7bed8
This commit is contained in:
parent
64b077cdaa
commit
2c85fcce87
3 changed files with 28 additions and 3 deletions
|
@ -3700,9 +3700,6 @@ static RegisterPrimOp primop_toString({
|
||||||
static void prim_substring(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
static void prim_substring(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
int start = state.forceInt(*args[0], pos, "while evaluating the first argument (the start offset) passed to builtins.substring");
|
int start = state.forceInt(*args[0], pos, "while evaluating the first argument (the start offset) passed to builtins.substring");
|
||||||
int len = state.forceInt(*args[1], pos, "while evaluating the second argument (the substring length) passed to builtins.substring");
|
|
||||||
NixStringContext context;
|
|
||||||
auto s = state.coerceToString(pos, *args[2], context, "while evaluating the third argument (the string) passed to builtins.substring");
|
|
||||||
|
|
||||||
if (start < 0)
|
if (start < 0)
|
||||||
state.debugThrowLastTrace(EvalError({
|
state.debugThrowLastTrace(EvalError({
|
||||||
|
@ -3710,6 +3707,22 @@ static void prim_substring(EvalState & state, const PosIdx pos, Value * * args,
|
||||||
.errPos = state.positions[pos]
|
.errPos = state.positions[pos]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
int len = state.forceInt(*args[1], pos, "while evaluating the second argument (the substring length) passed to builtins.substring");
|
||||||
|
|
||||||
|
// Special-case on empty substring to avoid O(n) strlen
|
||||||
|
// This allows for the use of empty substrings to efficently capture string context
|
||||||
|
if (len == 0) {
|
||||||
|
state.forceValue(*args[2], pos);
|
||||||
|
if (args[2]->type() == nString) {
|
||||||
|
v.mkString("", args[2]->string.context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NixStringContext context;
|
||||||
|
auto s = state.coerceToString(pos, *args[2], context, "while evaluating the third argument (the string) passed to builtins.substring");
|
||||||
|
|
||||||
v.mkString((unsigned int) start >= s->size() ? "" : s->substr(start, len), context);
|
v.mkString((unsigned int) start >= s->size() ? "" : s->substr(start, len), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
tests/functional/lang/eval-okay-substring-context.exp
Normal file
1
tests/functional/lang/eval-okay-substring-context.exp
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"okay"
|
11
tests/functional/lang/eval-okay-substring-context.nix
Normal file
11
tests/functional/lang/eval-okay-substring-context.nix
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
with builtins;
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
s = "${builtins.derivation { name = "test"; builder = "/bin/sh"; system = "x86_64-linux"; }}";
|
||||||
|
|
||||||
|
in
|
||||||
|
|
||||||
|
if getContext s == getContext "${substring 0 0 s + unsafeDiscardStringContext s}"
|
||||||
|
then "okay"
|
||||||
|
else throw "empty substring should preserve context"
|
Loading…
Reference in a new issue