libexpr: heap-alloc string control blocks

despite not using allocation caches this does not have a statistically
significant performance impact, with less than 1% extra memory needed.

Change-Id: Ibe51a55ba986e471f217f3724977af17880fafff
This commit is contained in:
eldritch horrors
2025-09-28 00:02:20 +02:00
parent 5d6bb8c350
commit cbc378b277
4 changed files with 73 additions and 40 deletions
+10 -2
View File
@@ -188,13 +188,21 @@ public:
struct ExprString : ExprLiteral
{
std::string s;
ExprString(const PosIdx pos, std::string &&s) : ExprLiteral(pos), s(std::move(s)) { v.mkString(this->s.data()); };
Value::String strcb{.content = s.c_str(), .context = nullptr};
ExprString(const PosIdx pos, std::string && s) : ExprLiteral(pos), s(std::move(s))
{
v = {NewValueAs::string, &strcb};
}
};
struct ExprPath : ExprLiteral
{
std::string s;
ExprPath(const PosIdx pos, std::string s) : ExprLiteral(pos), s(std::move(s)) { v.mkPath(this->s.c_str()); };
Value::String strcb{.content = s.c_str(), .context = Value::String::path};
ExprPath(const PosIdx pos, std::string s) : ExprLiteral(pos), s(std::move(s))
{
v = {NewValueAs::path, &strcb};
}
};
typedef uint32_t Level;
+4 -1
View File
@@ -47,6 +47,8 @@ private:
*/
std::string contents;
Value::String strcb;
/*
* A value containing a string that can be immediately passed to the evaluator.
*/
@@ -55,7 +57,8 @@ private:
public:
explicit InternedSymbol(std::string_view s)
: contents(s)
, underlyingValue(NewValueAs::string, contents.c_str(), nullptr)
, strcb{.content = contents.c_str(), .context = nullptr}
, underlyingValue(NewValueAs::string, &strcb)
{
}
+6 -7
View File
@@ -12,14 +12,14 @@ namespace nix
static const Value::List emptyListData{.size = 0};
Value Value::EMPTY_LIST{Value::list_t{}, &emptyListData};
static void copyContextToValue(Value & v, const NixStringContext & context)
static void copyContextToValue(Value::String & s, const NixStringContext & context)
{
if (!context.empty()) {
size_t n = 0;
v._string.context = gcAllocType<char const *>(context.size() + 1);
s.context = gcAllocType<char const *>(context.size() + 1);
for (auto & i : context)
v._string.context[n++] = gcCopyStringIfNeeded(i.to_string());
v._string.context[n] = 0;
s.context[n++] = gcCopyStringIfNeeded(i.to_string());
s.context[n] = 0;
}
}
@@ -60,16 +60,15 @@ void Value::mkString(std::string_view s)
void Value::mkString(std::string_view s, const NixStringContext & context)
{
mkString(s);
copyContextToValue(*this, context);
copyContextToValue(*const_cast<String *>(_string), context);
}
void Value::mkStringMove(const char * s, const NixStringContext & context)
{
mkString(s);
copyContextToValue(*this, context);
copyContextToValue(*const_cast<String *>(_string), context);
}
void Value::mkPath(const SourcePath & path)
{
*this = Value(NewValueAs::path, path);
+53 -30
View File
@@ -213,6 +213,8 @@ public:
*/
static Value EMPTY_LIST;
struct String;
// Discount `using NewValueAs::*;`
// NOLINTNEXTLINE(bugprone-macro-parentheses)
#define USING_VALUETYPE(name) using name = NewValueAs::name
@@ -275,8 +277,14 @@ public:
/// enabled), and string and context data copied into that memory.
Value(string_t, char const * strPtr, char const ** contextPtr = nullptr)
: internalType(tString)
, _string({.content = strPtr, .context = contextPtr})
{ }
, _string_pad(0)
{
auto block = gcAllocType<String>();
*block = {.content = strPtr, .context = contextPtr};
_string = block;
}
Value(string_t, const String * str) : internalType(tString), _string(str), _string_pad(0) {}
/// Constructx a nix language value of type "string", with a copy of the
/// string data viewed by @ref copyFrom.
@@ -285,24 +293,28 @@ public:
/// performs a dynamic (GC) allocation to do so.
Value(string_t, std::string_view copyFrom, NixStringContext const & context = {})
: internalType(tString)
, _string({.content = gcCopyStringIfNeeded(copyFrom), .context = nullptr})
, _string_pad(0)
{
auto block = gcAllocType<String>();
*block = {.content = gcCopyStringIfNeeded(copyFrom), .context = nullptr};
_string = block;
if (context.empty()) {
// It stays nullptr.
return;
}
// Copy the context.
this->_string.context = gcAllocType<char const *>(context.size() + 1);
block->context = gcAllocType<char const *>(context.size() + 1);
size_t n = 0;
for (NixStringContextElem const & contextElem : context) {
this->_string.context[n] = gcCopyStringIfNeeded(contextElem.to_string());
block->context[n] = gcCopyStringIfNeeded(contextElem.to_string());
n += 1;
}
// Terminator sentinel.
this->_string.context[n] = nullptr;
block->context[n] = nullptr;
}
/// Constructx a nix language value of type "string", with the value of the
@@ -316,24 +328,28 @@ public:
/// to do so.
Value(string_t, char const * strPtr, NixStringContext const & context)
: internalType(tString)
, _string({.content = strPtr, .context = nullptr})
, _string_pad(0)
{
auto block = gcAllocType<String>();
*block = {.content = strPtr, .context = nullptr};
_string = block;
if (context.empty()) {
// It stays nullptr
return;
}
// Copy the context.
this->_string.context = gcAllocType<char const *>(context.size() + 1);
block->context = gcAllocType<char const *>(context.size() + 1);
size_t n = 0;
for (NixStringContextElem const & contextElem : context) {
this->_string.context[n] = gcCopyStringIfNeeded(contextElem.to_string());
block->context[n] = gcCopyStringIfNeeded(contextElem.to_string());
n += 1;
}
// Terminator sentinel.
this->_string.context[n] = nullptr;
block->context[n] = nullptr;
}
/// Constructs a nix language value of type "path", with the value of the
@@ -342,20 +358,22 @@ public:
/// The C-string is not copied; this constructor assumes suitable memory
/// has already been allocated (with the GC if enabled), and string data
/// has been copied into that memory.
Value(path_t, char const * strPtr)
: internalType(tString)
, _string{.content = strPtr, .context = String::path}
{ }
Value(path_t, const String * str) : internalType(tString), _string(str), _string_pad(0)
{
assert(str->isPath());
}
/// Constructs a nix language value of type "path", with the path
/// @ref path.
///
/// The data from @ref path *is* copied, and this constructor performs a
/// dynamic (GC) allocation to do so.
Value(path_t, SourcePath const & path)
: internalType(tString)
, _string{.content = gcCopyStringIfNeeded(path.canonical().abs()), .context = String::path}
{ }
Value(path_t, SourcePath const & path) : internalType(tString), _string_pad(0)
{
auto block = gcAllocType<String>();
*block = {.content = gcCopyStringIfNeeded(path.canonical().abs()), .context = String::path};
_string = block;
}
/// Constructs a nix language value of type "list", with element array
/// @ref items.
@@ -614,8 +632,11 @@ public:
uintptr_t _bool_pad;
};
String _string;
struct
{
const String * _string;
uintptr_t _string_pad;
};
struct {
Bindings * _attrs;
uintptr_t _attrs_pad;
@@ -661,7 +682,7 @@ public:
case tInt: return nInt;
case tBool: return nBool;
case tString:
return _string.isPath() ? nPath : nString;
return _string->isPath() ? nPath : nString;
case tNull: return nNull;
case tAttrs: return nAttrs;
case tList:
@@ -713,8 +734,9 @@ public:
inline void mkString(const char * s, const char * * context = 0)
{
internalType = tString;
_string.content = s;
_string.context = context;
auto block = gcAllocType<String>();
*block = {.content = s, .context = context};
_string = block;
}
void mkString(std::string_view s);
@@ -729,8 +751,9 @@ public:
{
clearValue();
internalType = tString;
_string.content = path;
_string.context = String::path;
auto block = gcAllocType<String>();
*block = {.content = path, .context = String::path};
_string = block;
}
inline void mkNull()
@@ -841,14 +864,14 @@ public:
SourcePath path() const
{
assert(internalType == tString && _string.isPath());
return SourcePath{CanonPath(_string.content)};
assert(internalType == tString && _string->isPath());
return SourcePath{CanonPath(_string->content)};
}
std::string_view str() const
{
assert(internalType == tString && !_string.isPath());
return std::string_view(_string.content);
assert(internalType == tString && !_string->isPath());
return std::string_view(_string->content);
}
NixInt integer() const
@@ -863,7 +886,7 @@ public:
const auto & string() const
{
return _string;
return *_string;
}
auto attrs() const