1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-15 23:12:44 +01:00

Garbage-collect strings

This commit is contained in:
Eelco Dolstra 2019-04-29 15:17:58 +02:00
parent 9b822de4ef
commit 2995f9c48f
9 changed files with 83 additions and 36 deletions

View file

@ -24,10 +24,13 @@ namespace nix {
SymbolTable symbols; SymbolTable symbols;
unsigned long stringBytes = 0;
// FIXME // FIXME
static char * dupString(const char * s) static char * dupString(const char * s)
{ {
char * t; char * t;
stringBytes += strlen(s) + 1;
t = strdup(s); t = strdup(s);
if (!t) throw std::bad_alloc(); if (!t) throw std::bad_alloc();
return t; return t;
@ -52,6 +55,7 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
str << (v.boolean ? "true" : "false"); str << (v.boolean ? "true" : "false");
break; break;
case tShortString: case tShortString:
case tStaticString:
case tLongString: case tLongString:
str << "\""; str << "\"";
for (const char * i = v.getString(); *i; i++) for (const char * i = v.getString(); *i; i++)
@ -141,7 +145,9 @@ string showType(const Value & v)
switch (v.type) { switch (v.type) {
case tInt: return "an integer"; case tInt: return "an integer";
case tBool: return "a boolean"; case tBool: return "a boolean";
case tShortString: return "a string"; case tShortString:
case tStaticString:
return "a string";
case tLongString: return v.string.context ? "a string with context" : "a string"; case tLongString: return v.string.context ? "a string with context" : "a string";
case tPath: return "a path"; case tPath: return "a path";
case tNull: return "null"; case tNull: return "null";
@ -480,22 +486,14 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2,
} }
void mkString(Value & v, const char * s)
{
auto len = strlen(s); // FIXME: only need to know if > short
if (len < WORD_SIZE * 2 + Object::miscBytes)
v.setShortString(s);
else
mkStringNoCopy(v, dupString(s));
}
Value & mkString(Value & v, const string & s, const PathSet & context) Value & mkString(Value & v, const string & s, const PathSet & context)
{ {
if (context.empty()) if (context.empty())
mkString(v, s.c_str()); mkString(v, s.c_str());
else { else {
mkStringNoCopy(v, dupString(s.c_str())); v.string.s = String::alloc(s.c_str());
v.string.context = nullptr;
v.type = tLongString;
v.setContext(context); v.setContext(context);
} }
return v; return v;
@ -1461,10 +1459,10 @@ string EvalState::forceStringNoCtx(Value & v, const Pos & pos)
v.getContext(context); v.getContext(context);
if (pos) if (pos)
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%'), at %3%", throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%'), at %3%",
v.string._s, *context.begin(), pos); v.getString(), *context.begin(), pos);
else else
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')", throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')",
v.string._s, *context.begin()); v.getString(), *context.begin());
} }
return s; return s;
} }
@ -1721,6 +1719,7 @@ void EvalState::printStats()
topObj.attr("nrLookups", nrLookups); topObj.attr("nrLookups", nrLookups);
topObj.attr("nrPrimOpCalls", nrPrimOpCalls); topObj.attr("nrPrimOpCalls", nrPrimOpCalls);
topObj.attr("nrFunctionCalls", nrFunctionCalls); topObj.attr("nrFunctionCalls", nrFunctionCalls);
topObj.attr("stringBytes", stringBytes);
if (countCalls) { if (countCalls) {
{ {
@ -1766,6 +1765,7 @@ void EvalState::printStats()
} }
// FIXME: move to gc.cc and make generic.
size_t valueSize(Value & v) size_t valueSize(Value & v)
{ {
std::set<const void *> seen; std::set<const void *> seen;
@ -1787,7 +1787,7 @@ size_t valueSize(Value & v)
switch (v.type) { switch (v.type) {
case tLongString: case tLongString:
sz += doString(v.string._s); sz += v.string.s->words() * WORD_SIZE;
break; break;
case tPath: case tPath:
sz += doString(v.path); sz += doString(v.path);

View file

@ -146,6 +146,7 @@ void GC::gc()
break; break;
} }
case tString:
case tContext: case tContext:
case tInt: case tInt:
case tBool: case tBool:
@ -153,11 +154,12 @@ void GC::gc()
case tList0: case tList0:
case tFloat: case tFloat:
case tShortString: case tShortString:
case tStaticString:
break; break;
case tLongString: { case tLongString: {
auto obj2 = (Value *) obj; auto obj2 = (Value *) obj;
// FIXME: GC string push(obj2->string.s);
// See setContext(). // See setContext().
if (!(((ptrdiff_t) obj2->string.context) & 1)) if (!(((ptrdiff_t) obj2->string.context) & 1))
push(obj2->string.context); push(obj2->string.context);
@ -285,6 +287,9 @@ std::pair<size_t, size_t> GC::freeUnmarked(Arena & arena)
case tFree: case tFree:
objSize = ((Free *) obj)->words(); objSize = ((Free *) obj)->words();
break; break;
case tString:
objSize = ((String *) obj)->words();
break;
case tBindings: case tBindings:
objSize = ((Bindings *) obj)->words(); objSize = ((Bindings *) obj)->words();
break; break;

View file

@ -5,6 +5,7 @@
#include <stack> #include <stack>
#include <limits> #include <limits>
#include <cassert> #include <cassert>
#include <cstring>
//#define GC_DEBUG 1 //#define GC_DEBUG 1
@ -16,6 +17,7 @@ enum Tag {
tFree = 3, tFree = 3,
// Misc types // Misc types
tString,
tBindings, tBindings,
tValueList, tValueList,
tEnv, tEnv,
@ -27,6 +29,7 @@ enum Tag {
tInt, tInt,
tBool, tBool,
tShortString, tShortString,
tStaticString,
tLongString, tLongString,
tPath, tPath,
tNull, tNull,
@ -436,4 +439,30 @@ struct Root
} }
}; };
struct String : Object
{
char s[0];
String(size_t len, const char * src)
: Object(tString, len)
{
std::memcpy(s, src, len + 1);
}
size_t words() const { return wordsFor(getMisc()); }
/* Return the number of words needed to store a string of 'len'
characters (where 'len' excludes the terminator). */
static size_t wordsFor(size_t len)
{
return len / WORD_SIZE + 2;
}
static String * alloc(const char * src)
{
auto len = strlen(src);
return gc.alloc<String>(wordsFor(len), len, src);
}
};
} }

View file

@ -239,6 +239,7 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu
case tInt: t = "int"; break; case tInt: t = "int"; break;
case tBool: t = "bool"; break; case tBool: t = "bool"; break;
case tShortString: case tShortString:
case tStaticString:
case tLongString: case tLongString:
t = "string"; break; t = "string"; break;
case tPath: t = "path"; break; case tPath: t = "path"; break;
@ -937,15 +938,20 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
DirEntries entries = readDirectory(state.checkSourcePath(path)); DirEntries entries = readDirectory(state.checkSourcePath(path));
state.mkAttrs(v, entries.size()); state.mkAttrs(v, entries.size());
auto sRegular = state.symbols.create("regular");
auto sDirectory = state.symbols.create("directory");
auto sSymlink = state.symbols.create("symlink");
auto sUnknown = state.symbols.create("unknown");
for (auto & ent : entries) { for (auto & ent : entries) {
auto ent_val = state.allocAttr(v, state.symbols.create(ent.name)); auto ent_val = state.allocAttr(v, state.symbols.create(ent.name));
if (ent.type == DT_UNKNOWN) if (ent.type == DT_UNKNOWN)
ent.type = getFileType(path + "/" + ent.name); ent.type = getFileType(path + "/" + ent.name);
mkStringNoCopy(*ent_val, mkString(*ent_val,
ent.type == DT_REG ? "regular" : ent.type == DT_REG ? sRegular :
ent.type == DT_DIR ? "directory" : ent.type == DT_DIR ? sDirectory :
ent.type == DT_LNK ? "symlink" : ent.type == DT_LNK ? sSymlink :
"unknown"); sUnknown);
} }
v.attrs->sort(); v.attrs->sort();

View file

@ -160,7 +160,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
if (iter != i.value->attrs->end()) { if (iter != i.value->attrs->end()) {
if (state.forceBool(*iter->value, *iter->pos)) { if (state.forceBool(*iter->value, *iter->pos)) {
if (!isDerivation(i.name)) { if (!isDerivation(i.name)) {
throw EvalError("Tried to add all-outputs context of %s, which is not a derivation, to a string, at %s", i.name, i.pos); throw EvalError("tried to add all-outputs context of %s, which is not a derivation, to a string, at %s", i.name, *i.pos);
} }
context.insert("=" + string(i.name)); context.insert("=" + string(i.name));
} }
@ -170,7 +170,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
if (iter != i.value->attrs->end()) { if (iter != i.value->attrs->end()) {
state.forceList(*iter->value, *iter->pos); state.forceList(*iter->value, *iter->pos);
if (iter->value->listSize() && !isDerivation(i.name)) { if (iter->value->listSize() && !isDerivation(i.name)) {
throw EvalError("Tried to add derivation output context of %s, which is not a derivation, to a string, at %s", i.name, i.pos); throw EvalError("tried to add derivation output context of %s, which is not a derivation, to a string, at %s", i.name, *i.pos);
} }
for (unsigned int n = 0; n < iter->value->listSize(); ++n) { for (unsigned int n = 0; n < iter->value->listSize(); ++n) {
auto name = state.forceStringNoCtx(*iter->value->listElems()[n], *iter->pos); auto name = state.forceStringNoCtx(*iter->value->listElems()[n], *iter->pos);

View file

@ -27,6 +27,7 @@ void printValueAsJSON(EvalState & state, bool strict,
break; break;
case tShortString: case tShortString:
case tStaticString:
case tLongString: case tLongString:
v.getContext(context); v.getContext(context);
out.write(v.getString()); out.write(v.getString());

View file

@ -69,6 +69,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
break; break;
case tShortString: case tShortString:
case tStaticString:
case tLongString: case tLongString:
/* !!! show the context? */ /* !!! show the context? */
v.getContext(context); v.getContext(context);

View file

@ -116,10 +116,10 @@ struct Value : Object
derivation, and the other store paths in C will be added to derivation, and the other store paths in C will be added to
the inputSrcs of the derivations. */ the inputSrcs of the derivations. */
struct { struct {
const char * _s; String * s;
Context * context; Context * context;
} string; } string;
const char * staticString;
const char * path; const char * path;
Bindings * attrs; Bindings * attrs;
PtrList<Value> * bigList; PtrList<Value> * bigList;
@ -202,7 +202,7 @@ public:
bool isString() const bool isString() const
{ {
return type == tShortString || type == tLongString; return type == tShortString || type == tStaticString || type == tLongString;
} }
void setShortString(const char * s) void setShortString(const char * s)
@ -217,8 +217,10 @@ public:
{ {
if (type == tShortString) if (type == tShortString)
return getMiscData(); return getMiscData();
else if (type == tStaticString)
return staticString;
else else
return string._s; return string.s->s;
} }
}; };
@ -266,21 +268,23 @@ static inline void mkPrimOpApp(Value & v, Value & left, Value & right)
} }
static inline void mkStringNoCopy(Value & v, const char * s) static inline void mkString(Value & v, const char * s)
{ {
// FIXME: copy short strings? auto len = strlen(s); // FIXME: only need to know if > short
v.type = tLongString; if (len < WORD_SIZE * 2 + Object::miscBytes)
v.string._s = s; v.setShortString(s);
else {
v.string.s = gc.alloc<String>(String::wordsFor(len), len, s);
v.string.context = 0; v.string.context = 0;
v.type = tLongString;
}
} }
void mkString(Value & v, const char * s);
static inline void mkString(Value & v, const Symbol & s) static inline void mkString(Value & v, const Symbol & s)
{ {
mkString(v, ((const string &) s).c_str()); v.staticString = ((const string &) s).c_str();
v.type = tStaticString;
} }

View file

@ -607,6 +607,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
break; break;
case tShortString: case tShortString:
case tStaticString:
case tLongString: case tLongString:
str << ESC_YEL; str << ESC_YEL;
printStringValue(str, v.getString()); printStringValue(str, v.getString());