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:
+10
-2
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user