libexpr/symbol-table: introduce InternedSymbol

The backing storage for symbols becomes a class storing a Value and a
string.

The Value is itself a string which contents points to the owned string.

Recovering a `SymbolStr` is still possible.

Change-Id: I171151abc3c0a513f2150c4b54edd61dea256cce
Signed-off-by: Raito Bezarius <raito@lix.systems>
This commit is contained in:
Raito Bezarius
2025-06-10 13:58:08 +02:00
parent 364e94fe23
commit 5e98a2159b
3 changed files with 67 additions and 5 deletions
+6
View File
@@ -19,6 +19,12 @@ std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol)
return printIdentifier(str, s);
}
std::ostream & operator<<(std::ostream & str, const InternedSymbol & symbol)
{
str << SymbolStr(symbol);
return str;
}
AttrName::AttrName(PosIdx pos, Symbol s) : pos(pos), symbol(s)
{
}
+2 -2
View File
@@ -810,7 +810,7 @@ drvName, Bindings * attrs, Value & v)
for (auto & i : attrs->lexicographicOrder(state.ctx.symbols)) {
if (i->name == state.ctx.s.ignoreNulls) continue;
const std::string & key = state.ctx.symbols[i->name];
auto & key = state.ctx.symbols[i->name];
vomit("processing attribute '%1%'", key);
auto handleHashMode = [&](const std::string_view s, NeverAsync = {}) {
@@ -1539,7 +1539,7 @@ static void prim_path(EvalState & state, Value * * args, Value & v)
state.forceAttrs(*args[0], noPos, "while evaluating the argument passed to 'builtins.path'");
for (auto & attr : *args[0]->attrs) {
auto n = state.ctx.symbols[attr.name];
auto & n = state.ctx.symbols[attr.name];
if (n == "path")
path.emplace(state.coerceToPath(attr.pos, *attr.value, context, "while evaluating the 'path' attribute passed to 'builtins.path'"));
else if (attr.name == state.ctx.s.name)
+59 -3
View File
@@ -6,6 +6,8 @@
#include "lix/libutil/types.hh"
#include "lix/libutil/chunked-vector.hh"
#include "lix/libexpr/value.hh"
namespace nix {
/**
@@ -16,6 +18,7 @@ namespace nix {
class SymbolStr
{
friend class SymbolTable;
friend class InternedSymbol;
private:
const std::string * s;
@@ -41,6 +44,59 @@ public:
friend std::ostream & operator <<(std::ostream & os, const SymbolStr & symbol);
};
class InternedSymbol
{
private:
/*
* The type that actually stores the string contained inside of the Value.
*/
std::string contents;
/*
* A value containing a string that can be immediately passed to the evaluator.
*/
Value underlyingValue;
public:
explicit InternedSymbol(std::string_view s)
: contents(s)
, underlyingValue(NewValueAs::string, contents.c_str(), nullptr)
{
}
InternedSymbol(InternedSymbol &&) = default;
InternedSymbol & operator=(InternedSymbol &&) = default;
KJ_DISALLOW_COPY(InternedSymbol);
operator SymbolStr() const
{
return SymbolStr(contents);
}
bool operator==(std::string_view s2) const
{
return contents == s2;
}
operator const std::string &() const
{
return contents;
}
operator std::string_view() const
{
return contents;
}
const Value * toValuePtr() const
{
return &underlyingValue;
}
friend std::ostream & operator<<(std::ostream & os, const InternedSymbol & symbol);
};
/**
* Symbols have the property that they can be compared efficiently
* (using an equality test), because the symbol table stores only one
@@ -77,7 +133,7 @@ private:
* ChunkedVector references are never invalidated.
*/
std::unordered_map<std::string_view, uint32_t> symbols;
ChunkedVector<std::string, 8192> store{16};
ChunkedVector<InternedSymbol, 8192> store{16};
public:
@@ -101,11 +157,11 @@ public:
return Symbol(idx + 1);
}
SymbolStr operator[](Symbol s) const
const InternedSymbol & operator[](Symbol s) const
{
if (s.id == 0 || s.id > store.size())
abort();
return SymbolStr(store[s.id - 1]);
return store[s.id - 1];
}
size_t size() const