diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 871dfa64c..5351ca0c3 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -912,16 +912,29 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) { - state.nrListConcats++; Value v1; e1->eval(state, env, v1); - state.forceList(v1); Value v2; e2->eval(state, env, v2); - state.forceList(v2); - state.mkList(v, v1.list.length + v2.list.length); - for (unsigned int n = 0; n < v1.list.length; ++n) - v.list.elems[n] = v1.list.elems[n]; - for (unsigned int n = 0; n < v2.list.length; ++n) - v.list.elems[n + v1.list.length] = v2.list.elems[n]; + Value * lists[2] = { &v1, &v2 }; + state.concatLists(v, 2, lists); +} + + +void EvalState::concatLists(Value & v, unsigned int nrLists, Value * * lists) +{ + nrListConcats++; + + unsigned int len = 0; + for (unsigned int n = 0; n < nrLists; ++n) { + forceList(*lists[n]); + len += lists[n]->list.length; + } + + mkList(v, len); + for (unsigned int n = 0, pos = 0; n < nrLists; ++n) { + unsigned int l = lists[n]->list.length; + memcpy(v.list.elems + pos, lists[n]->list.elems, l * sizeof(Value *)); + pos += l; + } } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 4e66c4dfc..a1f26a056 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -232,6 +232,8 @@ public: void mkAttrs(Value & v, unsigned int expected); void mkThunk_(Value & v, Expr * expr); + void concatLists(Value & v, unsigned int nrLists, Value * * lists); + /* Print statistics. */ void printStats(); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index aa27df6ad..f5c004044 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -931,6 +931,7 @@ static void prim_filter(EvalState & state, Value * * args, Value & v) } +/* Return true if a list contains a given element. */ static void prim_elem(EvalState & state, Value * * args, Value & v) { bool res = false; @@ -944,6 +945,14 @@ static void prim_elem(EvalState & state, Value * * args, Value & v) } +/* Concatenate a list of lists. */ +static void prim_concatLists(EvalState & state, Value * * args, Value & v) +{ + state.forceList(*args[0]); + state.concatLists(v, args[0]->list.length, args[0]->list.elems); +} + + /* Return the length of a list. This is an O(1) time operation. */ static void prim_length(EvalState & state, Value * * args, Value & v) { @@ -1160,6 +1169,7 @@ void EvalState::createBaseEnv() addPrimOp("map", 2, prim_map); addPrimOp("__filter", 2, prim_filter); addPrimOp("__elem", 2, prim_elem); + addPrimOp("__concatLists", 1, prim_concatLists); addPrimOp("__length", 1, prim_length); // Integer arithmetic