Optimize small lists

The value pointers of lists with 1 or 2 elements are now stored in the
list value itself. In particular, this makes the "concatMap (x: if
cond then [(f x)] else [])" idiom cheaper.
This commit is contained in:
Eelco Dolstra 2015-07-23 22:05:09 +02:00
parent 14be783676
commit b83801f8b3
11 changed files with 157 additions and 121 deletions

View file

@ -76,15 +76,15 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
else if (apType == apIndex) { else if (apType == apIndex) {
if (v->type != tList) if (!v->isList())
throw TypeError( throw TypeError(
format("the expression selected by the selection path %1% should be a list but is %2%") format("the expression selected by the selection path %1% should be a list but is %2%")
% attrPath % showType(*v)); % attrPath % showType(*v));
if (attrIndex >= v->list.length) if (attrIndex >= v->listSize())
throw Error(format("list index %1% in selection path %2% is out of range") % attrIndex % attrPath); throw Error(format("list index %1% in selection path %2% is out of range") % attrIndex % attrPath);
v = v->list.elems[attrIndex]; v = v->listElems()[attrIndex];
} }
} }

View file

@ -66,7 +66,7 @@ inline void EvalState::forceAttrs(Value & v, const Pos & pos)
inline void EvalState::forceList(Value & v) inline void EvalState::forceList(Value & v)
{ {
forceValue(v); forceValue(v);
if (v.type != tList) if (!v.isList())
throwTypeError("value is %1% while a list was expected", v); throwTypeError("value is %1% while a list was expected", v);
} }
@ -74,7 +74,7 @@ inline void EvalState::forceList(Value & v)
inline void EvalState::forceList(Value & v, const Pos & pos) inline void EvalState::forceList(Value & v, const Pos & pos)
{ {
forceValue(v); forceValue(v);
if (v.type != tList) if (!v.isList())
throwTypeError("value is %1% while a list was expected, at %2%", v, pos); throwTypeError("value is %1% while a list was expected, at %2%", v, pos);
} }

View file

@ -108,10 +108,12 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
str << "}"; str << "}";
break; break;
} }
case tList: case tList1:
case tList2:
case tListN:
str << "[ "; str << "[ ";
for (unsigned int n = 0; n < v.list.length; ++n) { for (unsigned int n = 0; n < v.listSize(); ++n) {
printValue(str, active, *v.list.elems[n]); printValue(str, active, *v.listElems()[n]);
str << " "; str << " ";
} }
str << "]"; str << "]";
@ -157,7 +159,7 @@ string showType(const Value & v)
case tPath: return "a path"; case tPath: return "a path";
case tNull: return "null"; case tNull: return "null";
case tAttrs: return "a set"; case tAttrs: return "a set";
case tList: return "a list"; case tList1: case tList2: case tListN: return "a list";
case tThunk: return "a thunk"; case tThunk: return "a thunk";
case tApp: return "a function application"; case tApp: return "a function application";
case tLambda: return "a function"; case tLambda: return "a function";
@ -519,13 +521,19 @@ Bindings * EvalState::allocBindings(Bindings::size_t capacity)
} }
void EvalState::mkList(Value & v, unsigned int length) void EvalState::mkList(Value & v, unsigned int size)
{ {
clearValue(v); clearValue(v);
v.type = tList; if (size == 1)
v.list.length = length; v.type = tList1;
v.list.elems = length ? (Value * *) allocBytes(length * sizeof(Value *)) : 0; else if (size == 2)
nrListElems += length; v.type = tList2;
else {
v.type = tListN;
v.bigList.size = size;
v.bigList.elems = size ? (Value * *) allocBytes(size * sizeof(Value *)) : 0;
}
nrListElems += size;
} }
@ -807,8 +815,8 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
void ExprList::eval(EvalState & state, Env & env, Value & v) void ExprList::eval(EvalState & state, Env & env, Value & v)
{ {
state.mkList(v, elems.size()); state.mkList(v, elems.size());
for (unsigned int n = 0; n < v.list.length; ++n) for (unsigned int n = 0; n < elems.size(); ++n)
v.list.elems[n] = elems[n]->maybeThunk(state, env); v.listElems()[n] = elems[n]->maybeThunk(state, env);
} }
@ -1201,20 +1209,21 @@ void EvalState::concatLists(Value & v, unsigned int nrLists, Value * * lists, co
unsigned int len = 0; unsigned int len = 0;
for (unsigned int n = 0; n < nrLists; ++n) { for (unsigned int n = 0; n < nrLists; ++n) {
forceList(*lists[n], pos); forceList(*lists[n], pos);
unsigned int l = lists[n]->list.length; unsigned int l = lists[n]->listSize();
len += l; len += l;
if (l) nonEmpty = lists[n]; if (l) nonEmpty = lists[n];
} }
if (nonEmpty && len == nonEmpty->list.length) { if (nonEmpty && len == nonEmpty->listSize()) {
v = *nonEmpty; v = *nonEmpty;
return; return;
} }
mkList(v, len); mkList(v, len);
auto out = v.listElems();
for (unsigned int n = 0, pos = 0; n < nrLists; ++n) { for (unsigned int n = 0, pos = 0; n < nrLists; ++n) {
unsigned int l = lists[n]->list.length; unsigned int l = lists[n]->listSize();
memcpy(v.list.elems + pos, lists[n]->list.elems, l * sizeof(Value *)); memcpy(out + pos, lists[n]->listElems(), l * sizeof(Value *));
pos += l; pos += l;
} }
} }
@ -1290,9 +1299,9 @@ void EvalState::forceValueDeep(Value & v)
} }
} }
else if (v.type == tList) { else if (v.isList()) {
for (unsigned int n = 0; n < v.list.length; ++n) for (unsigned int n = 0; n < v.listSize(); ++n)
recurse(*v.list.elems[n]); recurse(*v.listElems()[n]);
} }
}; };
@ -1416,14 +1425,14 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
if (v.type == tInt) return int2String(v.integer); if (v.type == tInt) return int2String(v.integer);
if (v.type == tNull) return ""; if (v.type == tNull) return "";
if (v.type == tList) { if (v.isList()) {
string result; string result;
for (unsigned int n = 0; n < v.list.length; ++n) { for (unsigned int n = 0; n < v.listSize(); ++n) {
result += coerceToString(pos, *v.list.elems[n], result += coerceToString(pos, *v.listElems()[n],
context, coerceMore, copyToStore); context, coerceMore, copyToStore);
if (n < v.list.length - 1 if (n < v.listSize() - 1
/* !!! not quite correct */ /* !!! not quite correct */
&& (v.list.elems[n]->type != tList || v.list.elems[n]->list.length != 0)) && (!v.listElems()[n]->isList() || v.listElems()[n]->listSize() != 0))
result += " "; result += " ";
} }
return result; return result;
@ -1494,10 +1503,12 @@ bool EvalState::eqValues(Value & v1, Value & v2)
case tNull: case tNull:
return true; return true;
case tList: case tList1:
if (v1.list.length != v2.list.length) return false; case tList2:
for (unsigned int n = 0; n < v1.list.length; ++n) case tListN:
if (!eqValues(*v1.list.elems[n], *v2.list.elems[n])) return false; if (v1.listSize() != v2.listSize()) return false;
for (unsigned int n = 0; n < v1.listSize(); ++n)
if (!eqValues(*v1.listElems()[n], *v2.listElems()[n])) return false;
return true; return true;
case tAttrs: { case tAttrs: {
@ -1645,12 +1656,14 @@ size_t valueSize(Value & v)
sz += doValue(*i.value); sz += doValue(*i.value);
} }
break; break;
case tList: case tList1:
if (seen.find(v.list.elems) == seen.end()) { case tList2:
seen.insert(v.list.elems); case tListN:
sz += v.list.length * sizeof(Value *); if (seen.find(v.listElems()) == seen.end()) {
for (unsigned int n = 0; n < v.list.length; ++n) seen.insert(v.listElems());
sz += doValue(*v.list.elems[n]); sz += v.listSize() * sizeof(Value *);
for (unsigned int n = 0; n < v.listSize(); ++n)
sz += doValue(*v.listElems()[n]);
} }
break; break;
case tThunk: case tThunk:

View file

@ -39,9 +39,9 @@ DrvInfo::Outputs DrvInfo::queryOutputs()
state->forceList(*i->value, *i->pos); state->forceList(*i->value, *i->pos);
/* For each output... */ /* For each output... */
for (unsigned int j = 0; j < i->value->list.length; ++j) { for (unsigned int j = 0; j < i->value->listSize(); ++j) {
/* Evaluate the corresponding set. */ /* Evaluate the corresponding set. */
string name = state->forceStringNoCtx(*i->value->list.elems[j], *i->pos); string name = state->forceStringNoCtx(*i->value->listElems()[j], *i->pos);
Bindings::iterator out = attrs->find(state->symbols.create(name)); Bindings::iterator out = attrs->find(state->symbols.create(name));
if (out == attrs->end()) continue; // FIXME: throw error? if (out == attrs->end()) continue; // FIXME: throw error?
state->forceAttrs(*out->value); state->forceAttrs(*out->value);
@ -94,9 +94,9 @@ StringSet DrvInfo::queryMetaNames()
bool DrvInfo::checkMeta(Value & v) bool DrvInfo::checkMeta(Value & v)
{ {
state->forceValue(v); state->forceValue(v);
if (v.type == tList) { if (v.isList()) {
for (unsigned int n = 0; n < v.list.length; ++n) for (unsigned int n = 0; n < v.listSize(); ++n)
if (!checkMeta(*v.list.elems[n])) return false; if (!checkMeta(*v.listElems()[n])) return false;
return true; return true;
} }
else if (v.type == tAttrs) { else if (v.type == tAttrs) {
@ -277,13 +277,13 @@ static void getDerivations(EvalState & state, Value & vIn,
} }
} }
else if (v.type == tList) { else if (v.isList()) {
for (unsigned int n = 0; n < v.list.length; ++n) { for (unsigned int n = 0; n < v.listSize(); ++n) {
startNest(nest, lvlDebug, startNest(nest, lvlDebug,
format("evaluating list element")); format("evaluating list element"));
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str()); string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done, ignoreAssertionFailures)) if (getDerivation(state, *v.listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); getDerivations(state, *v.listElems()[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
} }
} }

View file

@ -73,7 +73,7 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
s++; s++;
state.mkList(v, values.size()); state.mkList(v, values.size());
for (size_t n = 0; n < values.size(); ++n) for (size_t n = 0; n < values.size(); ++n)
v.list.elems[n] = values[n]; v.listElems()[n] = values[n];
} }
else if (*s == '{') { else if (*s == '{') {

View file

@ -100,8 +100,8 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
v2 = state.allocAttr(w, state.symbols.create(o.first)); v2 = state.allocAttr(w, state.symbols.create(o.first));
mkString(*v2, o.second.path, mkString(*v2, o.second.path,
singleton<PathSet>("!" + o.first + "!" + path)); singleton<PathSet>("!" + o.first + "!" + path));
outputsVal->list.elems[outputs_index] = state.allocValue(); outputsVal->listElems()[outputs_index] = state.allocValue();
mkString(*(outputsVal->list.elems[outputs_index++]), o.first); mkString(*(outputsVal->listElems()[outputs_index++]), o.first);
} }
w.attrs->sort(); w.attrs->sort();
Value fun; Value fun;
@ -188,7 +188,7 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu
case tPath: t = "path"; break; case tPath: t = "path"; break;
case tNull: t = "null"; break; case tNull: t = "null"; break;
case tAttrs: t = "set"; break; case tAttrs: t = "set"; break;
case tList: t = "list"; break; case tList1: case tList2: case tListN: t = "list"; break;
case tLambda: case tLambda:
case tPrimOp: case tPrimOp:
case tPrimOpApp: case tPrimOpApp:
@ -284,8 +284,8 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
state.forceList(*startSet->value, pos); state.forceList(*startSet->value, pos);
ValueList workSet; ValueList workSet;
for (unsigned int n = 0; n < startSet->value->list.length; ++n) for (unsigned int n = 0; n < startSet->value->listSize(); ++n)
workSet.push_back(startSet->value->list.elems[n]); workSet.push_back(startSet->value->listElems()[n]);
/* Get the operator. */ /* Get the operator. */
Bindings::iterator op = Bindings::iterator op =
@ -323,9 +323,9 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
state.forceList(call, pos); state.forceList(call, pos);
/* Add the values returned by the operator to the work set. */ /* Add the values returned by the operator to the work set. */
for (unsigned int n = 0; n < call.list.length; ++n) { for (unsigned int n = 0; n < call.listSize(); ++n) {
state.forceValue(*call.list.elems[n]); state.forceValue(*call.listElems()[n]);
workSet.push_back(call.list.elems[n]); workSet.push_back(call.listElems()[n]);
} }
} }
@ -333,7 +333,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
state.mkList(v, res.size()); state.mkList(v, res.size());
unsigned int n = 0; unsigned int n = 0;
for (auto & i : res) for (auto & i : res)
v.list.elems[n++] = i; v.listElems()[n++] = i;
} }
@ -495,8 +495,8 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
command-line arguments to the builder. */ command-line arguments to the builder. */
if (key == "args") { if (key == "args") {
state.forceList(*i.value, pos); state.forceList(*i.value, pos);
for (unsigned int n = 0; n < i.value->list.length; ++n) { for (unsigned int n = 0; n < i.value->listSize(); ++n) {
string s = state.coerceToString(posDrvName, *i.value->list.elems[n], context, true); string s = state.coerceToString(posDrvName, *i.value->listElems()[n], context, true);
drv.args.push_back(s); drv.args.push_back(s);
} }
} }
@ -766,8 +766,8 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
SearchPath searchPath; SearchPath searchPath;
PathSet context; PathSet context;
for (unsigned int n = 0; n < args[0]->list.length; ++n) { for (unsigned int n = 0; n < args[0]->listSize(); ++n) {
Value & v2(*args[0]->list.elems[n]); Value & v2(*args[0]->listElems()[n]);
state.forceAttrs(v2, pos); state.forceAttrs(v2, pos);
string prefix; string prefix;
@ -972,9 +972,9 @@ static void prim_attrNames(EvalState & state, const Pos & pos, Value * * args, V
unsigned int n = 0; unsigned int n = 0;
for (auto & i : *args[0]->attrs) for (auto & i : *args[0]->attrs)
mkString(*(v.list.elems[n++] = state.allocValue()), i.name); mkString(*(v.listElems()[n++] = state.allocValue()), i.name);
std::sort(v.list.elems, v.list.elems + n, std::sort(v.listElems(), v.listElems() + n,
[](Value * v1, Value * v2) { return strcmp(v1->string.s, v2->string.s) < 0; }); [](Value * v1, Value * v2) { return strcmp(v1->string.s, v2->string.s) < 0; });
} }
@ -989,13 +989,13 @@ static void prim_attrValues(EvalState & state, const Pos & pos, Value * * args,
unsigned int n = 0; unsigned int n = 0;
for (auto & i : *args[0]->attrs) for (auto & i : *args[0]->attrs)
v.list.elems[n++] = (Value *) &i; v.listElems()[n++] = (Value *) &i;
std::sort(v.list.elems, v.list.elems + n, std::sort(v.listElems(), v.listElems() + n,
[](Value * v1, Value * v2) { return (string) ((Attr *) v1)->name < (string) ((Attr *) v2)->name; }); [](Value * v1, Value * v2) { return (string) ((Attr *) v1)->name < (string) ((Attr *) v2)->name; });
for (unsigned int i = 0; i < n; ++i) for (unsigned int i = 0; i < n; ++i)
v.list.elems[i] = ((Attr *) v.list.elems[i])->value; v.listElems()[i] = ((Attr *) v.listElems()[i])->value;
} }
@ -1052,9 +1052,9 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args,
/* Get the attribute names to be removed. */ /* Get the attribute names to be removed. */
std::set<Symbol> names; std::set<Symbol> names;
for (unsigned int i = 0; i < args[1]->list.length; ++i) { for (unsigned int i = 0; i < args[1]->listSize(); ++i) {
state.forceStringNoCtx(*args[1]->list.elems[i], pos); state.forceStringNoCtx(*args[1]->listElems()[i], pos);
names.insert(state.symbols.create(args[1]->list.elems[i]->string.s)); names.insert(state.symbols.create(args[1]->listElems()[i]->string.s));
} }
/* Copy all attributes not in that set. Note that we don't need /* Copy all attributes not in that set. Note that we don't need
@ -1077,12 +1077,12 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
{ {
state.forceList(*args[0], pos); state.forceList(*args[0], pos);
state.mkAttrs(v, args[0]->list.length); state.mkAttrs(v, args[0]->listSize());
std::set<Symbol> seen; std::set<Symbol> seen;
for (unsigned int i = 0; i < args[0]->list.length; ++i) { for (unsigned int i = 0; i < args[0]->listSize(); ++i) {
Value & v2(*args[0]->list.elems[i]); Value & v2(*args[0]->listElems()[i]);
state.forceAttrs(v2, pos); state.forceAttrs(v2, pos);
Bindings::iterator j = v2.attrs->find(state.sName); Bindings::iterator j = v2.attrs->find(state.sName);
@ -1135,11 +1135,11 @@ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Va
Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos)); Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos));
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
Value * res[args[1]->list.length]; Value * res[args[1]->listSize()];
unsigned int found = 0; unsigned int found = 0;
for (unsigned int n = 0; n < args[1]->list.length; ++n) { for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
Value & v2(*args[1]->list.elems[n]); Value & v2(*args[1]->listElems()[n]);
state.forceAttrs(v2, pos); state.forceAttrs(v2, pos);
Bindings::iterator i = v2.attrs->find(attrName); Bindings::iterator i = v2.attrs->find(attrName);
if (i != v2.attrs->end()) if (i != v2.attrs->end())
@ -1148,7 +1148,7 @@ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Va
state.mkList(v, found); state.mkList(v, found);
for (unsigned int n = 0; n < found; ++n) for (unsigned int n = 0; n < found; ++n)
v.list.elems[n] = res[n]; v.listElems()[n] = res[n];
} }
@ -1193,17 +1193,17 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
static void prim_isList(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_isList(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceValue(*args[0]); state.forceValue(*args[0]);
mkBool(v, args[0]->type == tList); mkBool(v, args[0]->isList());
} }
static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Value & v) static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Value & v)
{ {
state.forceList(list, pos); state.forceList(list, pos);
if (n < 0 || (unsigned int) n >= list.list.length) if (n < 0 || (unsigned int) n >= list.listSize())
throw Error(format("list index %1% is out of bounds, at %2%") % n % pos); throw Error(format("list index %1% is out of bounds, at %2%") % n % pos);
state.forceValue(*list.list.elems[n]); state.forceValue(*list.listElems()[n]);
v = *list.list.elems[n]; v = *list.listElems()[n];
} }
@ -1227,11 +1227,11 @@ static void prim_head(EvalState & state, const Pos & pos, Value * * args, Value
static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceList(*args[0], pos); state.forceList(*args[0], pos);
if (args[0]->list.length == 0) if (args[0]->listSize() == 0)
throw Error(format("tail called on an empty list, at %1%") % pos); throw Error(format("tail called on an empty list, at %1%") % pos);
state.mkList(v, args[0]->list.length - 1); state.mkList(v, args[0]->listSize() - 1);
for (unsigned int n = 0; n < v.list.length; ++n) for (unsigned int n = 0; n < v.listSize(); ++n)
v.list.elems[n] = args[0]->list.elems[n + 1]; v.listElems()[n] = args[0]->listElems()[n + 1];
} }
@ -1241,11 +1241,11 @@ static void prim_map(EvalState & state, const Pos & pos, Value * * args, Value &
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
state.mkList(v, args[1]->list.length); state.mkList(v, args[1]->listSize());
for (unsigned int n = 0; n < v.list.length; ++n) for (unsigned int n = 0; n < v.listSize(); ++n)
mkApp(*(v.list.elems[n] = state.allocValue()), mkApp(*(v.listElems()[n] = state.allocValue()),
*args[0], *args[1]->list.elems[n]); *args[0], *args[1]->listElems()[n]);
} }
@ -1258,15 +1258,15 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
// FIXME: putting this on the stack is risky. // FIXME: putting this on the stack is risky.
Value * vs[args[1]->list.length]; Value * vs[args[1]->listSize()];
unsigned int k = 0; unsigned int k = 0;
bool same = true; bool same = true;
for (unsigned int n = 0; n < args[1]->list.length; ++n) { for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
Value res; Value res;
state.callFunction(*args[0], *args[1]->list.elems[n], res, noPos); state.callFunction(*args[0], *args[1]->listElems()[n], res, noPos);
if (state.forceBool(res)) if (state.forceBool(res))
vs[k++] = args[1]->list.elems[n]; vs[k++] = args[1]->listElems()[n];
else else
same = false; same = false;
} }
@ -1275,7 +1275,7 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu
v = *args[1]; v = *args[1];
else { else {
state.mkList(v, k); state.mkList(v, k);
for (unsigned int n = 0; n < k; ++n) v.list.elems[n] = vs[n]; for (unsigned int n = 0; n < k; ++n) v.listElems()[n] = vs[n];
} }
} }
@ -1285,8 +1285,8 @@ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value
{ {
bool res = false; bool res = false;
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
for (unsigned int n = 0; n < args[1]->list.length; ++n) for (unsigned int n = 0; n < args[1]->listSize(); ++n)
if (state.eqValues(*args[0], *args[1]->list.elems[n])) { if (state.eqValues(*args[0], *args[1]->listElems()[n])) {
res = true; res = true;
break; break;
} }
@ -1298,7 +1298,7 @@ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value
static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceList(*args[0], pos); state.forceList(*args[0], pos);
state.concatLists(v, args[0]->list.length, args[0]->list.elems, pos); state.concatLists(v, args[0]->listSize(), args[0]->listElems(), pos);
} }
@ -1306,7 +1306,7 @@ static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args,
static void prim_length(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_length(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceList(*args[0], pos); state.forceList(*args[0], pos);
mkInt(v, args[0]->list.length); mkInt(v, args[0]->listSize());
} }
@ -1319,12 +1319,12 @@ static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args,
Value * vCur = args[1]; Value * vCur = args[1];
if (args[2]->list.length) if (args[2]->listSize())
for (unsigned int n = 0; n < args[2]->list.length; ++n) { for (unsigned int n = 0; n < args[2]->listSize(); ++n) {
Value vTmp; Value vTmp;
state.callFunction(*args[0], *vCur, vTmp, pos); state.callFunction(*args[0], *vCur, vTmp, pos);
vCur = n == args[2]->list.length - 1 ? &v : state.allocValue(); vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue();
state.callFunction(vTmp, *args[2]->list.elems[n], *vCur, pos); state.callFunction(vTmp, *args[2]->listElems()[n], *vCur, pos);
} }
else else
v = *vCur; v = *vCur;
@ -1339,8 +1339,8 @@ static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * arg
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
Value vTmp; Value vTmp;
for (unsigned int n = 0; n < args[1]->list.length; ++n) { for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
state.callFunction(*args[0], *args[1]->list.elems[n], vTmp, pos); state.callFunction(*args[0], *args[1]->listElems()[n], vTmp, pos);
bool res = state.forceBool(vTmp); bool res = state.forceBool(vTmp);
if (res == any) { if (res == any) {
mkBool(v, any); mkBool(v, any);
@ -1507,9 +1507,9 @@ static void prim_match(EvalState & state, const Pos & pos, Value * * args, Value
for (unsigned int n = 0; n < len; ++n) { for (unsigned int n = 0; n < len; ++n) {
auto i = subs.find(n); auto i = subs.find(n);
if (i == subs.end()) if (i == subs.end())
mkNull(*(v.list.elems[n] = state.allocValue())); mkNull(*(v.listElems()[n] = state.allocValue()));
else else
mkString(*(v.list.elems[n] = state.allocValue()), i->second); mkString(*(v.listElems()[n] = state.allocValue()), i->second);
} }
} }
@ -1743,7 +1743,7 @@ void EvalState::createBaseEnv()
mkList(v, searchPath.size()); mkList(v, searchPath.size());
int n = 0; int n = 0;
for (auto & i : searchPath) { for (auto & i : searchPath) {
v2 = v.list.elems[n++] = allocValue(); v2 = v.listElems()[n++] = allocValue();
mkAttrs(*v2, 2); mkAttrs(*v2, 2);
mkString(*allocAttr(*v2, symbols.create("path")), i.second); mkString(*allocAttr(*v2, symbols.create("path")), i.second);
mkString(*allocAttr(*v2, symbols.create("prefix")), i.first); mkString(*allocAttr(*v2, symbols.create("prefix")), i.first);

View file

@ -71,11 +71,11 @@ void printValueAsJSON(EvalState & state, bool strict,
break; break;
} }
case tList: { case tList1: case tList2: case tListN: {
JSONList json(str); JSONList json(str);
for (unsigned int n = 0; n < v.list.length; ++n) { for (unsigned int n = 0; n < v.listSize(); ++n) {
json.elem(); json.elem();
printValueAsJSON(state, strict, *v.list.elems[n], str, context); printValueAsJSON(state, strict, *v.listElems()[n], str, context);
} }
break; break;
} }

View file

@ -119,10 +119,10 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
break; break;
case tList: { case tList1: case tList2: case tListN: {
XMLOpenElement _(doc, "list"); XMLOpenElement _(doc, "list");
for (unsigned int n = 0; n < v.list.length; ++n) for (unsigned int n = 0; n < v.listSize(); ++n)
printValueAsXML(state, strict, location, *v.list.elems[n], doc, context, drvsSeen); printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen);
break; break;
} }

View file

@ -12,7 +12,9 @@ typedef enum {
tPath, tPath,
tNull, tNull,
tAttrs, tAttrs,
tList, tList1,
tList2,
tListN,
tThunk, tThunk,
tApp, tApp,
tLambda, tLambda,
@ -119,9 +121,10 @@ struct Value
const char * path; const char * path;
Bindings * attrs; Bindings * attrs;
struct { struct {
unsigned int length; unsigned int size;
Value * * elems; Value * * elems;
} list; } bigList;
Value * smallList[2];
struct { struct {
Env * env; Env * env;
Expr * expr; Expr * expr;
@ -139,6 +142,26 @@ struct Value
} primOpApp; } primOpApp;
ExternalValueBase * external; ExternalValueBase * external;
}; };
bool isList() const
{
return type == tList1 || type == tList2 || type == tListN;
}
Value * * listElems()
{
return type == tList1 || type == tList2 ? smallList : bigList.elems;
}
const Value * const * listElems() const
{
return type == tList1 || type == tList2 ? smallList : bigList.elems;
}
unsigned int listSize() const
{
return type == tList1 ? 1 : type == tList2 ? 2 : bigList.size;
}
}; };

View file

@ -1127,13 +1127,13 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
attrs2["type"] = "bool"; attrs2["type"] = "bool";
attrs2["value"] = v->boolean ? "true" : "false"; attrs2["value"] = v->boolean ? "true" : "false";
xml.writeEmptyElement("meta", attrs2); xml.writeEmptyElement("meta", attrs2);
} else if (v->type == tList) { } else if (v->isList()) {
attrs2["type"] = "strings"; attrs2["type"] = "strings";
XMLOpenElement m(xml, "meta", attrs2); XMLOpenElement m(xml, "meta", attrs2);
for (unsigned int j = 0; j < v->list.length; ++j) { for (unsigned int j = 0; j < v->listSize(); ++j) {
if (v->list.elems[j]->type != tString) continue; if (v->listElems()[j]->type != tString) continue;
XMLAttrs attrs3; XMLAttrs attrs3;
attrs3["value"] = v->list.elems[j]->string.s; attrs3["value"] = v->listElems()[j]->string.s;
xml.writeEmptyElement("string", attrs3); xml.writeEmptyElement("string", attrs3);
} }
} }

View file

@ -52,7 +52,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
Path drvPath = keepDerivations ? i.queryDrvPath() : ""; Path drvPath = keepDerivations ? i.queryDrvPath() : "";
Value & v(*state.allocValue()); Value & v(*state.allocValue());
manifest.list.elems[n++] = &v; manifest.listElems()[n++] = &v;
state.mkAttrs(v, 16); state.mkAttrs(v, 16);
mkString(*state.allocAttr(v, state.sType), "derivation"); mkString(*state.allocAttr(v, state.sType), "derivation");
@ -69,7 +69,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
state.mkList(vOutputs, outputs.size()); state.mkList(vOutputs, outputs.size());
unsigned int m = 0; unsigned int m = 0;
for (auto & j : outputs) { for (auto & j : outputs) {
mkString(*(vOutputs.list.elems[m++] = state.allocValue()), j.first); mkString(*(vOutputs.listElems()[m++] = state.allocValue()), j.first);
Value & vOutputs = *state.allocAttr(v, state.symbols.create(j.first)); Value & vOutputs = *state.allocAttr(v, state.symbols.create(j.first));
state.mkAttrs(vOutputs, 2); state.mkAttrs(vOutputs, 2);
mkString(*state.allocAttr(vOutputs, state.sOutPath), j.second); mkString(*state.allocAttr(vOutputs, state.sOutPath), j.second);