Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ MATCHER(IsAttrs, "")
MATCHER_P(IsStringEq, s, fmt("The string is equal to \"%1%\"", s))
{
if (arg.type() != nString) {
*result_listener << "Expected a string got " << arg.type();
return false;
}
return arg.string_view() == s;
Expand Down
5 changes: 3 additions & 2 deletions src/libexpr-tests/json.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "nix/expr/tests/libexpr.hh"
#include "nix/expr/value-to-json.hh"
#include "nix/expr/static-string-data.hh"

namespace nix {
// Testing the conversion to JSON
Expand Down Expand Up @@ -54,15 +55,15 @@ TEST_F(JSONValueTest, IntNegative)
TEST_F(JSONValueTest, String)
{
Value v;
v.mkStringNoCopy("test");
v.mkStringNoCopy("test"_sds);
ASSERT_EQ(getJSONValue(v), "\"test\"");
}

TEST_F(JSONValueTest, StringQuotes)
{
Value v;

v.mkStringNoCopy("test\"");
v.mkStringNoCopy("test\""_sds);
ASSERT_EQ(getJSONValue(v), "\"test\\\"\"");
}

Expand Down
21 changes: 11 additions & 10 deletions src/libexpr-tests/value/print.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "nix/expr/tests/libexpr.hh"
#include "nix/expr/static-string-data.hh"

#include "nix/expr/value.hh"
#include "nix/expr/print.hh"
Expand Down Expand Up @@ -35,14 +36,14 @@ TEST_F(ValuePrintingTests, tBool)
TEST_F(ValuePrintingTests, tString)
{
Value vString;
vString.mkStringNoCopy("some-string");
vString.mkStringNoCopy("some-string"_sds);
test(vString, "\"some-string\"");
}

TEST_F(ValuePrintingTests, tPath)
{
Value vPath;
vPath.mkStringNoCopy("/foo");
vPath.mkStringNoCopy("/foo"_sds);
test(vPath, "\"/foo\"");
}

Expand Down Expand Up @@ -289,10 +290,10 @@ TEST_F(StringPrintingTests, maxLengthTruncation)
TEST_F(ValuePrintingTests, attrsTypeFirst)
{
Value vType;
vType.mkStringNoCopy("puppy");
vType.mkStringNoCopy("puppy"_sds);

Value vApple;
vApple.mkStringNoCopy("apple");
vApple.mkStringNoCopy("apple"_sds);

BindingsBuilder builder = state.buildBindings(10);
builder.insert(state.symbols.create("type"), &vType);
Expand Down Expand Up @@ -333,15 +334,15 @@ TEST_F(ValuePrintingTests, ansiColorsBool)
TEST_F(ValuePrintingTests, ansiColorsString)
{
Value v;
v.mkStringNoCopy("puppy");
v.mkStringNoCopy("puppy"_sds);

test(v, ANSI_MAGENTA "\"puppy\"" ANSI_NORMAL, PrintOptions{.ansiColors = true});
}

TEST_F(ValuePrintingTests, ansiColorsStringElided)
{
Value v;
v.mkStringNoCopy("puppy");
v.mkStringNoCopy("puppy"_sds);

test(
v,
Expand Down Expand Up @@ -389,7 +390,7 @@ TEST_F(ValuePrintingTests, ansiColorsAttrs)
TEST_F(ValuePrintingTests, ansiColorsDerivation)
{
Value vDerivation;
vDerivation.mkStringNoCopy("derivation");
vDerivation.mkStringNoCopy("derivation"_sds);

BindingsBuilder builder = state.buildBindings(10);
builder.insert(state.s.type, &vDerivation);
Expand All @@ -412,7 +413,7 @@ TEST_F(ValuePrintingTests, ansiColorsError)
{
Value throw_ = state.getBuiltin("throw");
Value message;
message.mkStringNoCopy("uh oh!");
message.mkStringNoCopy("uh oh!"_sds);
Value vError;
vError.mkApp(&throw_, &message);

Expand All @@ -429,12 +430,12 @@ TEST_F(ValuePrintingTests, ansiColorsDerivationError)
{
Value throw_ = state.getBuiltin("throw");
Value message;
message.mkStringNoCopy("uh oh!");
message.mkStringNoCopy("uh oh!"_sds);
Value vError;
vError.mkApp(&throw_, &message);

Value vDerivation;
vDerivation.mkStringNoCopy("derivation");
vDerivation.mkStringNoCopy("derivation"_sds);

BindingsBuilder builder = state.buildBindings(10);
builder.insert(state.s.type, &vDerivation);
Expand Down
13 changes: 7 additions & 6 deletions src/libexpr-tests/value/value.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "nix/expr/value.hh"
#include "nix/expr/static-string-data.hh"

#include "nix/store/tests/libstore.hh"
#include <gtest/gtest.h>
Expand Down Expand Up @@ -27,17 +28,17 @@ TEST_F(ValueTest, staticString)
{
Value vStr1;
Value vStr2;
vStr1.mkStringNoCopy("foo");
vStr2.mkStringNoCopy("foo");
vStr1.mkStringNoCopy("foo"_sds);
vStr2.mkStringNoCopy("foo"_sds);

auto sd1 = vStr1.string_view();
auto sd2 = vStr2.string_view();
auto & sd1 = vStr1.string_data();
auto & sd2 = vStr2.string_data();

// The strings should be the same
ASSERT_EQ(sd1, sd2);
ASSERT_EQ(sd1.view(), sd2.view());

// The strings should also be backed by the same (static) allocation
ASSERT_EQ(sd1.data(), sd2.data());
ASSERT_EQ(&sd1, &sd2);
}

} // namespace nix
2 changes: 1 addition & 1 deletion src/libexpr/eval-cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ struct AttrDb
for (auto * elem : *context) {
if (!first)
ctx.push_back(' ');
ctx.append(elem);
ctx.append(elem->view());
first = false;
}
state->insertAttributeWithContext.use()(key.first)(symbols[key.second])(AttrType::String) (s) (ctx)
Expand Down
62 changes: 46 additions & 16 deletions src/libexpr/eval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "nix/expr/primops.hh"
#include "nix/expr/print-options.hh"
#include "nix/expr/symbol-table.hh"
#include "nix/expr/value.hh"
#include "nix/util/exit.hh"
#include "nix/util/types.hh"
#include "nix/util/util.hh"
Expand All @@ -28,6 +29,8 @@
#include "parser-tab.hh"

#include <algorithm>
#include <cstddef>
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <cstring>
Expand All @@ -48,6 +51,9 @@ using json = nlohmann::json;

namespace nix {

/**
* Just for doc strings. Not for regular string values.
*/
static char * allocString(size_t size)
{
char * t;
Expand All @@ -61,6 +67,9 @@ static char * allocString(size_t size)
// string allocations.
// This function handles makeImmutableString(std::string_view()) by returning
// the empty string.
/**
* Just for doc strings. Not for regular string values.
*/
static const char * makeImmutableString(std::string_view s)
{
const size_t size = s.size();
Expand All @@ -72,6 +81,25 @@ static const char * makeImmutableString(std::string_view s)
return t;
}

StringData & StringData::alloc(size_t size)
{
void * t = GC_MALLOC_ATOMIC(sizeof(StringData) + size + 1);
if (!t)
throw std::bad_alloc();
auto res = new (t) StringData(size);
return *res;
}

const StringData & StringData::make(std::string_view s)
{
if (s.empty())
return ""_sds;
auto & res = alloc(s.size());
std::memcpy(&res.data_, s.data(), s.size());
res.data_[s.size()] = '\0';
return res;
}

RootValue allocRootValue(Value * v)
{
return std::allocate_shared<Value *>(traceable_allocator<Value *>(), v);
Expand Down Expand Up @@ -585,7 +613,9 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
.name = name,
.arity = 0, // FIXME: figure out how deep by syntax only? It's not semantically useful though...
.args = {},
.doc = makeImmutableString(s.view()), // NOTE: memory leak when compiled without GC
/* N.B. Can't use StringData here, because that would lead to an interior pointer.
NOTE: memory leak when compiled without GC. */
.doc = makeImmutableString(s.view()),
};
}
if (isFunctor(v)) {
Expand Down Expand Up @@ -819,7 +849,7 @@ DebugTraceStacker::DebugTraceStacker(EvalState & evalState, DebugTrace t)

void Value::mkString(std::string_view s)
{
mkStringNoCopy(makeImmutableString(s));
mkStringNoCopy(StringData::make(s));
}

Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuilder(const NixStringContext & context)
Expand All @@ -829,23 +859,23 @@ Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuild

auto ctx = new (allocBytes(sizeof(Context) + context.size() * sizeof(value_type))) Context(context.size());
std::ranges::transform(
context, ctx->elems, [](const NixStringContextElem & elt) { return makeImmutableString(elt.to_string()); });
context, ctx->elems, [](const NixStringContextElem & elt) { return &StringData::make(elt.to_string()); });
return ctx;
}

void Value::mkString(std::string_view s, const NixStringContext & context)
{
mkStringNoCopy(makeImmutableString(s), Value::StringWithContext::Context::fromBuilder(context));
mkStringNoCopy(StringData::make(s), Value::StringWithContext::Context::fromBuilder(context));
}

void Value::mkStringMove(const char * s, const NixStringContext & context)
void Value::mkStringMove(const StringData & s, const NixStringContext & context)
{
mkStringNoCopy(s, Value::StringWithContext::Context::fromBuilder(context));
}

void Value::mkPath(const SourcePath & path)
{
mkPath(&*path.accessor, makeImmutableString(path.path.abs()));
mkPath(&*path.accessor, StringData::make(path.path.abs()));
}

inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
Expand Down Expand Up @@ -2099,21 +2129,21 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
.atPos(pos)
.withFrame(env, *this)
.debugThrow();
std::string result_str;
result_str.reserve(sSize);
std::string resultStr;
resultStr.reserve(sSize);
for (const auto & part : strings) {
result_str += *part;
resultStr += *part;
}
v.mkPath(state.rootPath(CanonPath(result_str)));
v.mkPath(state.rootPath(CanonPath(resultStr)));
} else {
char * result_str = allocString(sSize + 1);
char * tmp = result_str;
auto & resultStr = StringData::alloc(sSize);
auto * tmp = resultStr.data();
for (const auto & part : strings) {
memcpy(tmp, part->data(), part->size());
std::memcpy(tmp, part->data(), part->size());
tmp += part->size();
}
*tmp = 0;
v.mkStringMove(result_str, context);
*tmp = '\0';
v.mkStringMove(resultStr, context);
}
}

Expand Down Expand Up @@ -2288,7 +2318,7 @@ void copyContext(const Value & v, NixStringContext & context, const Experimental
{
if (auto * ctx = v.context())
for (auto * elem : *ctx)
context.insert(NixStringContextElem::parse(elem, xpSettings));
context.insert(NixStringContextElem::parse(elem->view(), xpSettings));
}

std::string_view EvalState::forceString(
Expand Down
1 change: 1 addition & 0 deletions src/libexpr/include/nix/expr/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ headers = [ config_pub_h ] + files(
'print.hh',
'repl-exit-status.hh',
'search-path.hh',
'static-string-data.hh',
'symbol-table.hh',
'value-to-json.hh',
'value-to-xml.hh',
Expand Down
20 changes: 7 additions & 13 deletions src/libexpr/include/nix/expr/nixexpr.hh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <map>
#include <span>
#include <memory>
#include <vector>
#include <memory_resource>
#include <algorithm>
Expand All @@ -11,6 +12,7 @@
#include "nix/expr/value.hh"
#include "nix/expr/symbol-table.hh"
#include "nix/expr/eval-error.hh"
#include "nix/expr/static-string-data.hh"
#include "nix/util/pos-idx.hh"
#include "nix/expr/counter.hh"
#include "nix/util/pos-table.hh"
Expand Down Expand Up @@ -186,22 +188,18 @@ struct ExprString : Expr
* This is only for strings already allocated in our polymorphic allocator,
* or that live at least that long (e.g. c++ string literals)
*/
ExprString(const char * s)
ExprString(const StringData & s)
{
v.mkStringNoCopy(s);
};

ExprString(std::pmr::polymorphic_allocator<char> & alloc, std::string_view sv)
{
auto len = sv.length();
if (len == 0) {
v.mkStringNoCopy("");
if (sv.size() == 0) {
v.mkStringNoCopy(""_sds);
return;
}
char * s = alloc.allocate(len + 1);
sv.copy(s, len);
s[len] = '\0';
v.mkStringNoCopy(s);
v.mkStringNoCopy(StringData::make(*alloc.resource(), sv));
};

Value * maybeThunk(EvalState & state, Env & env) override;
Expand All @@ -216,11 +214,7 @@ struct ExprPath : Expr
ExprPath(std::pmr::polymorphic_allocator<char> & alloc, ref<SourceAccessor> accessor, std::string_view sv)
: accessor(accessor)
{
auto len = sv.length();
char * s = alloc.allocate(len + 1);
sv.copy(s, len);
s[len] = '\0';
v.mkPath(&*accessor, s);
v.mkPath(&*accessor, StringData::make(*alloc.resource(), sv));
}

Value * maybeThunk(EvalState & state, Env & env) override;
Expand Down
Loading
Loading