From 5c9cfa46fc0cdf8ec67e022c0a7086a4dcb639b8 Mon Sep 17 00:00:00 2001 From: Philipp Otterbein Date: Sun, 15 Jun 2025 21:59:13 +0200 Subject: [PATCH] libexpr: don't allocate additional set in builtins.listToAttrs --- src/libexpr/primops.cc | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 60f44ca62..7f19871e4 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2961,25 +2961,42 @@ static void prim_listToAttrs(EvalState & state, const PosIdx pos, Value * * args { state.forceList(*args[0], pos, "while evaluating the argument passed to builtins.listToAttrs"); - auto attrs = state.buildBindings(args[0]->listSize()); + size_t listSize = args[0]->listSize(); + auto & bindings = *state.allocBindings(listSize); - std::set seen; - - for (auto v2 : args[0]->listItems()) { + for (const auto & [n, v2] : enumerate(args[0]->listItems())) { state.forceAttrs(*v2, pos, "while evaluating an element of the list passed to builtins.listToAttrs"); auto j = state.getAttr(state.sName, v2->attrs(), "in a {name=...; value=...;} pair"); auto name = state.forceStringNoCtx(*j->value, j->pos, "while evaluating the `name` attribute of an element of the list passed to builtins.listToAttrs"); - auto sym = state.symbols.create(name); - if (seen.insert(sym).second) { - auto j2 = state.getAttr(state.sValue, v2->attrs(), "in a {name=...; value=...;} pair"); - attrs.insert(sym, j2->value, j2->pos); - } + + bindings[n] = Attr(sym, (Value *) &v2); } - v.mkAttrs(attrs); + std::sort(&bindings[0], &bindings[listSize], [](const Attr & a, const Attr & b) { + // a.value < b.value makes the sort stable because `value` points to an array item + return a < b || (!(a > b) && a.value < b.value); + }); + + Symbol prev; + for (size_t n = 0; n < listSize; n++) { + auto attr = bindings[n]; + if (prev == attr.name) { + continue; + } + Value * v2 = *(Value * const *) attr.value; + + auto j = state.getAttr(state.sValue, v2->attrs(), "in a {name=...; value=...;} pair"); + prev = attr.name; + bindings.push_back({prev, j->value, j->pos}); + } + // help GC and clear end of allocated array + for (size_t n = bindings.size(); n < listSize; n++) { + bindings[n] = Attr{}; + } + v.mkAttrs(&bindings); } static RegisterPrimOp primop_listToAttrs({