Skip to content

Commit

Permalink
Fix performance of builtins.substring for empty substrings
Browse files Browse the repository at this point in the history
When returning a 0-length substring, avoid calling coerceFromString,
since it returns a string_view with the string's length, which is
expensive to compute for large strings.
  • Loading branch information
bacchanalia committed Jan 11, 2024
1 parent 8450267 commit 2fd8dce
Showing 1 changed file with 14 additions and 3 deletions.
17 changes: 14 additions & 3 deletions src/libexpr/primops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3712,16 +3712,27 @@ static RegisterPrimOp primop_toString({
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 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)
state.debugThrowLastTrace(EvalError({
.msg = hintfmt("negative start position in 'substring'"),
.errPos = state.positions[pos]
}));


int len = state.forceInt(*args[1], pos, "while evaluating the second argument (the substring length) passed to builtins.substring");

if (len <= 0) {
state.forceValue(*args[2], pos);
if (args[2]->type() == nString) {
v.mkString("", v.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);
}

Expand Down

0 comments on commit 2fd8dce

Please sign in to comment.