From 0fbbb1e49b7d34010b4609a17062185a108ebdc1 Mon Sep 17 00:00:00 2001 From: piegames Date: Tue, 17 Dec 2024 17:22:03 +0100 Subject: [PATCH] libexpr: Switch StaticEnv to LinearMap Change-Id: If98bfafce9fa5235fe962274c03c619fe965dd60 --- lix/libcmd/repl.cc | 32 +++++++++++++++++--------------- lix/libexpr/eval.cc | 4 ++-- lix/libexpr/nixexpr.cc | 23 ++++++++++++++--------- lix/libexpr/nixexpr.hh | 30 ++---------------------------- lix/libexpr/primops.cc | 17 ++++++++--------- 5 files changed, 43 insertions(+), 63 deletions(-) diff --git a/lix/libcmd/repl.cc b/lix/libcmd/repl.cc index a017c2177..50ce1cd3a 100644 --- a/lix/libcmd/repl.cc +++ b/lix/libcmd/repl.cc @@ -1056,19 +1056,24 @@ template void NixRepl::addToScope(T && things, NameFn nameFn, ValueFn valueFn) { size_t added = 0; - for (auto && thing : things) { - if (displ + 1 >= envSize) - throw Error("environment full; cannot add more variables"); - const auto name = nameFn(thing); - staticEnv->vars.emplace_back(name, displ); - env->values[displ++] = valueFn(thing); - varNames.emplace(evaluator.symbols[name]); - added++; - } + staticEnv->vars.unsafe_insert_bulk([&] (auto & map) { + auto oldSize = map.size(); + for (auto && thing : things) { + if (displ + 1 >= envSize) + throw Error("environment full; cannot add more variables"); + + const auto name = nameFn(thing); + map.emplace_back(name, displ); + env->values[displ++] = valueFn(thing); + varNames.emplace(evaluator.symbols[name]); + added++; + } + // safety: we sort the range that we inserted so that we don't have to push that + // invariant up to the caller + std::sort(map.begin() + oldSize, map.end()); + }); - staticEnv->sort(); - staticEnv->deduplicate(); if (added > 0) { notice("Added %1% variables.", added); } @@ -1093,14 +1098,11 @@ void NixRepl::addVarToScope(const Symbol name, Value & v) { if (displ >= envSize) throw Error("environment full; cannot add more variables"); - if (auto oldVar = staticEnv->find(name); oldVar != staticEnv->vars.end()) { - staticEnv->vars.erase(oldVar); + if (staticEnv->vars.insert_or_assign(name, displ).second) { notice("Updated %s.", evaluator.symbols[name]); } else { notice("Added %s.", evaluator.symbols[name]); } - staticEnv->vars.emplace_back(name, displ); - staticEnv->sort(); env->values[displ++] = &v; varNames.emplace(evaluator.symbols[name]); } diff --git a/lix/libexpr/eval.cc b/lix/libexpr/eval.cc index 3beec65d3..c9b8a365c 100644 --- a/lix/libexpr/eval.cc +++ b/lix/libexpr/eval.cc @@ -549,7 +549,7 @@ void EvalBuiltins::addConstant(const std::string & name, Value * v, Constant inf assert(info.type == gotType); /* Install value the base environment. */ - staticEnv->vars.emplace_back(symbols.create(name), baseEnvDispl); + staticEnv->vars.insert_or_assign(symbols.create(name), baseEnvDispl); env.values[baseEnvDispl++] = v; env.values[0]->attrs->push_back(Attr(symbols.create(name2), v)); } @@ -584,7 +584,7 @@ Value * EvalBuiltins::addPrimOp(PrimOp && primOp) Value * v = mem.allocValue(); v->mkPrimOp(new PrimOp(primOp)); - staticEnv->vars.emplace_back(envName, baseEnvDispl); + staticEnv->vars.insert_or_assign(auto(envName), baseEnvDispl); env.values[baseEnvDispl++] = v; env.values[0]->attrs->push_back(Attr(symbols.create(primOp.name), v)); return v; diff --git a/lix/libexpr/nixexpr.cc b/lix/libexpr/nixexpr.cc index 14e30a934..f6f5d1741 100644 --- a/lix/libexpr/nixexpr.cc +++ b/lix/libexpr/nixexpr.cc @@ -465,7 +465,7 @@ void VarBinder::visit(ExprVar & e, std::unique_ptr & ptr) if (curEnv->isWith) { if (withLevel == -1) withLevel = level; } else { - auto i = curEnv->find(e.name); + auto i = curEnv->vars.find(e.name); if (i != curEnv->vars.end()) { if (e.needsRoot && !curEnv->isRoot) { throw ParseError({ @@ -523,9 +523,12 @@ std::shared_ptr ExprAttrs::buildRecursiveEnv(const std::shared_ { auto newEnv = std::make_shared(nullptr, env.get(), attrs.size()); - Displacement displ = 0; - for (auto & i : attrs) - newEnv->vars.emplace_back(i.first, i.second.displ = displ++); + // safety: the attrs is already sorted + newEnv->vars.unsafe_insert_bulk([&] (auto & map) { + Displacement displ = 0; + for (auto & i : attrs) + map.emplace_back(i.first, i.second.displ = displ++); + }); return newEnv; } @@ -662,7 +665,7 @@ void VarBinder::visit(ExprPos & e, std::unique_ptr & ptr) std::shared_ptr SimplePattern::buildEnv(const StaticEnv * up) { auto newEnv = std::make_shared(nullptr, up, 1); - newEnv->vars.emplace_back(name, 0); + newEnv->vars.insert_or_assign(name, 0); return newEnv; } @@ -677,12 +680,14 @@ std::shared_ptr AttrsPattern::buildEnv(const StaticEnv * up) Displacement displ = 0; - if (name) newEnv->vars.emplace_back(name, displ++); + if (name) newEnv->vars.insert_or_assign(name, displ++); - for (auto & i : formals) - newEnv->vars.emplace_back(i.name, displ++); + // safety: The formals are already sorted + newEnv->vars.unsafe_insert_bulk([&] (auto & map) { + for (auto & i : formals) + map.emplace_back(i.name, displ++); + }); - newEnv->sort(); return newEnv; } diff --git a/lix/libexpr/nixexpr.hh b/lix/libexpr/nixexpr.hh index 0adea91bb..e9e1acd9c 100644 --- a/lix/libexpr/nixexpr.hh +++ b/lix/libexpr/nixexpr.hh @@ -11,6 +11,7 @@ #include "lix/libexpr/eval-error.hh" #include "lix/libexpr/pos-idx.hh" #include "lix/libutil/strings.hh" +#include "lix/libutil/linear-map.hh" namespace nix { @@ -607,9 +608,7 @@ struct StaticEnv ExprWith * isWith; const StaticEnv * up; - // Note: these must be in sorted order. - typedef std::vector> Vars; - Vars vars; + LinearMap vars; /* See ExprVar::needsRoot */ bool isRoot = false; @@ -617,31 +616,6 @@ struct StaticEnv StaticEnv(ExprWith * isWith, const StaticEnv * up, size_t expectedSize = 0) : isWith(isWith), up(up) { vars.reserve(expectedSize); }; - - void sort() - { - std::stable_sort(vars.begin(), vars.end(), - [](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; }); - } - - void deduplicate() - { - auto it = vars.begin(), jt = it, end = vars.end(); - while (jt != end) { - *it = *jt++; - while (jt != end && it->first == jt->first) *it = *jt++; - it++; - } - vars.erase(it, end); - } - - Vars::const_iterator find(Symbol name) const - { - Vars::value_type key(name, 0); - auto i = std::lower_bound(vars.begin(), vars.end(), key); - if (i != vars.end() && i->first == name) return i; - return vars.end(); - } }; diff --git a/lix/libexpr/primops.cc b/lix/libexpr/primops.cc index 993a30d9f..bcca2afd4 100644 --- a/lix/libexpr/primops.cc +++ b/lix/libexpr/primops.cc @@ -242,14 +242,14 @@ static void import(EvalState & state, Value & vPath, Value * vScope, Value & v) nullptr, state.ctx.builtins.staticEnv.get(), vScope->attrs->size() ); - unsigned int displ = 0; - for (auto & attr : *vScope->attrs) { - staticEnv->vars.emplace_back(attr.name, displ); - env->values[displ++] = attr.value; - } - - // No need to call staticEnv.sort(), because - // args[0]->attrs is already sorted. + staticEnv->vars.unsafe_insert_bulk([&] (auto & map) { + unsigned int displ = 0; + for (auto & attr : *vScope->attrs) { + // safety: args[0]->attrs is already sorted. + map.emplace_back(attr.name, displ); + env->values[displ++] = attr.value; + } + }); debug("evaluating file '%1%'", path); Expr & e = state.ctx.parseExprFromFile(state.ctx.paths.resolveExprPath(path), staticEnv); @@ -2835,7 +2835,6 @@ void EvalBuiltins::createBaseEnv(const SearchPath & searchPath, const Path & sto because attribute lookups expect it to be sorted. */ env.values[0]->attrs->sort(); - staticEnv->sort(); staticEnv->isRoot = true; }