mirror of
https://github.com/NixOS/nix.git
synced 2025-11-11 13:06:01 +01:00
Use hybrid C / Pascal strings in the evaluator
Replace the null-terminated C-style strings in Value with hybrid C / Pascal strings, where the length is stored in the allocation before the data, and there is still a null byte at the end for the sake of C interopt. Co-Authored-By: Taeer Bar-Yam <taeer@bar-yam.me> Co-Authored-By: Sergei Zimmerman <sergei@zimmerman.foo>
This commit is contained in:
parent
8c113f80f3
commit
3bf8c76072
14 changed files with 279 additions and 100 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
#include "nix/expr/tests/libexpr.hh"
|
#include "nix/expr/tests/libexpr.hh"
|
||||||
#include "nix/expr/value-to-json.hh"
|
#include "nix/expr/value-to-json.hh"
|
||||||
|
#include "nix/expr/static-string-data.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
// Testing the conversion to JSON
|
// Testing the conversion to JSON
|
||||||
|
|
@ -54,7 +55,7 @@ TEST_F(JSONValueTest, IntNegative)
|
||||||
TEST_F(JSONValueTest, String)
|
TEST_F(JSONValueTest, String)
|
||||||
{
|
{
|
||||||
Value v;
|
Value v;
|
||||||
v.mkStringNoCopy("test");
|
v.mkStringNoCopy("test"_sds);
|
||||||
ASSERT_EQ(getJSONValue(v), "\"test\"");
|
ASSERT_EQ(getJSONValue(v), "\"test\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,7 +63,7 @@ TEST_F(JSONValueTest, StringQuotes)
|
||||||
{
|
{
|
||||||
Value v;
|
Value v;
|
||||||
|
|
||||||
v.mkStringNoCopy("test\"");
|
v.mkStringNoCopy("test\""_sds);
|
||||||
ASSERT_EQ(getJSONValue(v), "\"test\\\"\"");
|
ASSERT_EQ(getJSONValue(v), "\"test\\\"\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "nix/expr/tests/libexpr.hh"
|
#include "nix/expr/tests/libexpr.hh"
|
||||||
|
#include "nix/expr/static-string-data.hh"
|
||||||
|
|
||||||
#include "nix/expr/value.hh"
|
#include "nix/expr/value.hh"
|
||||||
#include "nix/expr/print.hh"
|
#include "nix/expr/print.hh"
|
||||||
|
|
@ -35,14 +36,14 @@ TEST_F(ValuePrintingTests, tBool)
|
||||||
TEST_F(ValuePrintingTests, tString)
|
TEST_F(ValuePrintingTests, tString)
|
||||||
{
|
{
|
||||||
Value vString;
|
Value vString;
|
||||||
vString.mkStringNoCopy("some-string");
|
vString.mkStringNoCopy("some-string"_sds);
|
||||||
test(vString, "\"some-string\"");
|
test(vString, "\"some-string\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValuePrintingTests, tPath)
|
TEST_F(ValuePrintingTests, tPath)
|
||||||
{
|
{
|
||||||
Value vPath;
|
Value vPath;
|
||||||
vPath.mkStringNoCopy("/foo");
|
vPath.mkStringNoCopy("/foo"_sds);
|
||||||
test(vPath, "\"/foo\"");
|
test(vPath, "\"/foo\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -289,10 +290,10 @@ TEST_F(StringPrintingTests, maxLengthTruncation)
|
||||||
TEST_F(ValuePrintingTests, attrsTypeFirst)
|
TEST_F(ValuePrintingTests, attrsTypeFirst)
|
||||||
{
|
{
|
||||||
Value vType;
|
Value vType;
|
||||||
vType.mkStringNoCopy("puppy");
|
vType.mkStringNoCopy("puppy"_sds);
|
||||||
|
|
||||||
Value vApple;
|
Value vApple;
|
||||||
vApple.mkStringNoCopy("apple");
|
vApple.mkStringNoCopy("apple"_sds);
|
||||||
|
|
||||||
BindingsBuilder builder = state.buildBindings(10);
|
BindingsBuilder builder = state.buildBindings(10);
|
||||||
builder.insert(state.symbols.create("type"), &vType);
|
builder.insert(state.symbols.create("type"), &vType);
|
||||||
|
|
@ -333,7 +334,7 @@ TEST_F(ValuePrintingTests, ansiColorsBool)
|
||||||
TEST_F(ValuePrintingTests, ansiColorsString)
|
TEST_F(ValuePrintingTests, ansiColorsString)
|
||||||
{
|
{
|
||||||
Value v;
|
Value v;
|
||||||
v.mkStringNoCopy("puppy");
|
v.mkStringNoCopy("puppy"_sds);
|
||||||
|
|
||||||
test(v, ANSI_MAGENTA "\"puppy\"" ANSI_NORMAL, PrintOptions{.ansiColors = true});
|
test(v, ANSI_MAGENTA "\"puppy\"" ANSI_NORMAL, PrintOptions{.ansiColors = true});
|
||||||
}
|
}
|
||||||
|
|
@ -341,7 +342,7 @@ TEST_F(ValuePrintingTests, ansiColorsString)
|
||||||
TEST_F(ValuePrintingTests, ansiColorsStringElided)
|
TEST_F(ValuePrintingTests, ansiColorsStringElided)
|
||||||
{
|
{
|
||||||
Value v;
|
Value v;
|
||||||
v.mkStringNoCopy("puppy");
|
v.mkStringNoCopy("puppy"_sds);
|
||||||
|
|
||||||
test(
|
test(
|
||||||
v,
|
v,
|
||||||
|
|
@ -389,7 +390,7 @@ TEST_F(ValuePrintingTests, ansiColorsAttrs)
|
||||||
TEST_F(ValuePrintingTests, ansiColorsDerivation)
|
TEST_F(ValuePrintingTests, ansiColorsDerivation)
|
||||||
{
|
{
|
||||||
Value vDerivation;
|
Value vDerivation;
|
||||||
vDerivation.mkStringNoCopy("derivation");
|
vDerivation.mkStringNoCopy("derivation"_sds);
|
||||||
|
|
||||||
BindingsBuilder builder = state.buildBindings(10);
|
BindingsBuilder builder = state.buildBindings(10);
|
||||||
builder.insert(state.s.type, &vDerivation);
|
builder.insert(state.s.type, &vDerivation);
|
||||||
|
|
@ -412,7 +413,7 @@ TEST_F(ValuePrintingTests, ansiColorsError)
|
||||||
{
|
{
|
||||||
Value throw_ = state.getBuiltin("throw");
|
Value throw_ = state.getBuiltin("throw");
|
||||||
Value message;
|
Value message;
|
||||||
message.mkStringNoCopy("uh oh!");
|
message.mkStringNoCopy("uh oh!"_sds);
|
||||||
Value vError;
|
Value vError;
|
||||||
vError.mkApp(&throw_, &message);
|
vError.mkApp(&throw_, &message);
|
||||||
|
|
||||||
|
|
@ -429,12 +430,12 @@ TEST_F(ValuePrintingTests, ansiColorsDerivationError)
|
||||||
{
|
{
|
||||||
Value throw_ = state.getBuiltin("throw");
|
Value throw_ = state.getBuiltin("throw");
|
||||||
Value message;
|
Value message;
|
||||||
message.mkStringNoCopy("uh oh!");
|
message.mkStringNoCopy("uh oh!"_sds);
|
||||||
Value vError;
|
Value vError;
|
||||||
vError.mkApp(&throw_, &message);
|
vError.mkApp(&throw_, &message);
|
||||||
|
|
||||||
Value vDerivation;
|
Value vDerivation;
|
||||||
vDerivation.mkStringNoCopy("derivation");
|
vDerivation.mkStringNoCopy("derivation"_sds);
|
||||||
|
|
||||||
BindingsBuilder builder = state.buildBindings(10);
|
BindingsBuilder builder = state.buildBindings(10);
|
||||||
builder.insert(state.s.type, &vDerivation);
|
builder.insert(state.s.type, &vDerivation);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "nix/expr/value.hh"
|
#include "nix/expr/value.hh"
|
||||||
|
#include "nix/expr/static-string-data.hh"
|
||||||
|
|
||||||
#include "nix/store/tests/libstore.hh"
|
#include "nix/store/tests/libstore.hh"
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
@ -27,17 +28,17 @@ TEST_F(ValueTest, staticString)
|
||||||
{
|
{
|
||||||
Value vStr1;
|
Value vStr1;
|
||||||
Value vStr2;
|
Value vStr2;
|
||||||
vStr1.mkStringNoCopy("foo");
|
vStr1.mkStringNoCopy("foo"_sds);
|
||||||
vStr2.mkStringNoCopy("foo");
|
vStr2.mkStringNoCopy("foo"_sds);
|
||||||
|
|
||||||
auto sd1 = vStr1.string_view();
|
auto & sd1 = vStr1.string_data();
|
||||||
auto sd2 = vStr2.string_view();
|
auto & sd2 = vStr2.string_data();
|
||||||
|
|
||||||
// The strings should be the same
|
// 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
|
// The strings should also be backed by the same (static) allocation
|
||||||
ASSERT_EQ(sd1.data(), sd2.data());
|
ASSERT_EQ(&sd1, &sd2);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ struct AttrDb
|
||||||
for (auto * elem : *context) {
|
for (auto * elem : *context) {
|
||||||
if (!first)
|
if (!first)
|
||||||
ctx.push_back(' ');
|
ctx.push_back(' ');
|
||||||
ctx.append(elem);
|
ctx.append(elem->view());
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
state->insertAttributeWithContext.use()(key.first)(symbols[key.second])(AttrType::String) (s) (ctx)
|
state->insertAttributeWithContext.use()(key.first)(symbols[key.second])(AttrType::String) (s) (ctx)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include "nix/expr/primops.hh"
|
#include "nix/expr/primops.hh"
|
||||||
#include "nix/expr/print-options.hh"
|
#include "nix/expr/print-options.hh"
|
||||||
#include "nix/expr/symbol-table.hh"
|
#include "nix/expr/symbol-table.hh"
|
||||||
|
#include "nix/expr/value.hh"
|
||||||
#include "nix/util/exit.hh"
|
#include "nix/util/exit.hh"
|
||||||
#include "nix/util/types.hh"
|
#include "nix/util/types.hh"
|
||||||
#include "nix/util/util.hh"
|
#include "nix/util/util.hh"
|
||||||
|
|
@ -28,6 +29,8 @@
|
||||||
#include "parser-tab.hh"
|
#include "parser-tab.hh"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
@ -48,6 +51,9 @@ using json = nlohmann::json;
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just for doc strings. Not for regular string values.
|
||||||
|
*/
|
||||||
static char * allocString(size_t size)
|
static char * allocString(size_t size)
|
||||||
{
|
{
|
||||||
char * t;
|
char * t;
|
||||||
|
|
@ -61,6 +67,9 @@ static char * allocString(size_t size)
|
||||||
// string allocations.
|
// string allocations.
|
||||||
// This function handles makeImmutableString(std::string_view()) by returning
|
// This function handles makeImmutableString(std::string_view()) by returning
|
||||||
// the empty string.
|
// the empty string.
|
||||||
|
/**
|
||||||
|
* Just for doc strings. Not for regular string values.
|
||||||
|
*/
|
||||||
static const char * makeImmutableString(std::string_view s)
|
static const char * makeImmutableString(std::string_view s)
|
||||||
{
|
{
|
||||||
const size_t size = s.size();
|
const size_t size = s.size();
|
||||||
|
|
@ -72,6 +81,25 @@ static const char * makeImmutableString(std::string_view s)
|
||||||
return t;
|
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)
|
RootValue allocRootValue(Value * v)
|
||||||
{
|
{
|
||||||
return std::allocate_shared<Value *>(traceable_allocator<Value *>(), v);
|
return std::allocate_shared<Value *>(traceable_allocator<Value *>(), v);
|
||||||
|
|
@ -585,7 +613,9 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
|
||||||
.name = name,
|
.name = name,
|
||||||
.arity = 0, // FIXME: figure out how deep by syntax only? It's not semantically useful though...
|
.arity = 0, // FIXME: figure out how deep by syntax only? It's not semantically useful though...
|
||||||
.args = {},
|
.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)) {
|
if (isFunctor(v)) {
|
||||||
|
|
@ -819,7 +849,7 @@ DebugTraceStacker::DebugTraceStacker(EvalState & evalState, DebugTrace t)
|
||||||
|
|
||||||
void Value::mkString(std::string_view s)
|
void Value::mkString(std::string_view s)
|
||||||
{
|
{
|
||||||
mkStringNoCopy(makeImmutableString(s));
|
mkStringNoCopy(StringData::make(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuilder(const NixStringContext & context)
|
Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuilder(const NixStringContext & context)
|
||||||
|
|
@ -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());
|
auto ctx = new (allocBytes(sizeof(Context) + context.size() * sizeof(value_type))) Context(context.size());
|
||||||
std::ranges::transform(
|
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;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Value::mkString(std::string_view s, const NixStringContext & context)
|
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));
|
mkStringNoCopy(s, Value::StringWithContext::Context::fromBuilder(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Value::mkPath(const SourcePath & path)
|
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)
|
inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
||||||
|
|
@ -2099,21 +2129,21 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
.atPos(pos)
|
.atPos(pos)
|
||||||
.withFrame(env, *this)
|
.withFrame(env, *this)
|
||||||
.debugThrow();
|
.debugThrow();
|
||||||
std::string result_str;
|
std::string resultStr;
|
||||||
result_str.reserve(sSize);
|
resultStr.reserve(sSize);
|
||||||
for (const auto & part : strings) {
|
for (const auto & part : strings) {
|
||||||
result_str += *part;
|
resultStr += *part;
|
||||||
}
|
}
|
||||||
v.mkPath(state.rootPath(CanonPath(result_str)));
|
v.mkPath(state.rootPath(CanonPath(resultStr)));
|
||||||
} else {
|
} else {
|
||||||
char * result_str = allocString(sSize + 1);
|
auto & resultStr = StringData::alloc(sSize);
|
||||||
char * tmp = result_str;
|
auto * tmp = resultStr.data();
|
||||||
for (const auto & part : strings) {
|
for (const auto & part : strings) {
|
||||||
memcpy(tmp, part->data(), part->size());
|
std::memcpy(tmp, part->data(), part->size());
|
||||||
tmp += part->size();
|
tmp += part->size();
|
||||||
}
|
}
|
||||||
*tmp = 0;
|
*tmp = '\0';
|
||||||
v.mkStringMove(result_str, context);
|
v.mkStringMove(resultStr, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2288,7 +2318,7 @@ void copyContext(const Value & v, NixStringContext & context, const Experimental
|
||||||
{
|
{
|
||||||
if (auto * ctx = v.context())
|
if (auto * ctx = v.context())
|
||||||
for (auto * elem : *ctx)
|
for (auto * elem : *ctx)
|
||||||
context.insert(NixStringContextElem::parse(elem, xpSettings));
|
context.insert(NixStringContextElem::parse(elem->view(), xpSettings));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view EvalState::forceString(
|
std::string_view EvalState::forceString(
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ headers = [ config_pub_h ] + files(
|
||||||
'print.hh',
|
'print.hh',
|
||||||
'repl-exit-status.hh',
|
'repl-exit-status.hh',
|
||||||
'search-path.hh',
|
'search-path.hh',
|
||||||
|
'static-string-data.hh',
|
||||||
'symbol-table.hh',
|
'symbol-table.hh',
|
||||||
'value-to-json.hh',
|
'value-to-json.hh',
|
||||||
'value-to-xml.hh',
|
'value-to-xml.hh',
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory_resource>
|
#include <memory_resource>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
@ -11,6 +12,7 @@
|
||||||
#include "nix/expr/value.hh"
|
#include "nix/expr/value.hh"
|
||||||
#include "nix/expr/symbol-table.hh"
|
#include "nix/expr/symbol-table.hh"
|
||||||
#include "nix/expr/eval-error.hh"
|
#include "nix/expr/eval-error.hh"
|
||||||
|
#include "nix/expr/static-string-data.hh"
|
||||||
#include "nix/util/pos-idx.hh"
|
#include "nix/util/pos-idx.hh"
|
||||||
#include "nix/expr/counter.hh"
|
#include "nix/expr/counter.hh"
|
||||||
#include "nix/util/pos-table.hh"
|
#include "nix/util/pos-table.hh"
|
||||||
|
|
@ -186,22 +188,18 @@ struct ExprString : Expr
|
||||||
* This is only for strings already allocated in our polymorphic allocator,
|
* This is only for strings already allocated in our polymorphic allocator,
|
||||||
* or that live at least that long (e.g. c++ string literals)
|
* or that live at least that long (e.g. c++ string literals)
|
||||||
*/
|
*/
|
||||||
ExprString(const char * s)
|
ExprString(const StringData & s)
|
||||||
{
|
{
|
||||||
v.mkStringNoCopy(s);
|
v.mkStringNoCopy(s);
|
||||||
};
|
};
|
||||||
|
|
||||||
ExprString(std::pmr::polymorphic_allocator<char> & alloc, std::string_view sv)
|
ExprString(std::pmr::polymorphic_allocator<char> & alloc, std::string_view sv)
|
||||||
{
|
{
|
||||||
auto len = sv.length();
|
if (sv.size() == 0) {
|
||||||
if (len == 0) {
|
v.mkStringNoCopy(""_sds);
|
||||||
v.mkStringNoCopy("");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
char * s = alloc.allocate(len + 1);
|
v.mkStringNoCopy(StringData::make(*alloc.resource(), sv));
|
||||||
sv.copy(s, len);
|
|
||||||
s[len] = '\0';
|
|
||||||
v.mkStringNoCopy(s);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||||
|
|
@ -216,11 +214,7 @@ struct ExprPath : Expr
|
||||||
ExprPath(std::pmr::polymorphic_allocator<char> & alloc, ref<SourceAccessor> accessor, std::string_view sv)
|
ExprPath(std::pmr::polymorphic_allocator<char> & alloc, ref<SourceAccessor> accessor, std::string_view sv)
|
||||||
: accessor(accessor)
|
: accessor(accessor)
|
||||||
{
|
{
|
||||||
auto len = sv.length();
|
v.mkPath(&*accessor, StringData::make(*alloc.resource(), sv));
|
||||||
char * s = alloc.allocate(len + 1);
|
|
||||||
sv.copy(s, len);
|
|
||||||
s[len] = '\0';
|
|
||||||
v.mkPath(&*accessor, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
#include "nix/expr/eval.hh"
|
#include "nix/expr/eval.hh"
|
||||||
|
#include "nix/expr/value.hh"
|
||||||
|
#include "nix/expr/static-string-data.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
@ -240,7 +242,7 @@ inline Expr *
|
||||||
ParserState::stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
|
ParserState::stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
|
||||||
{
|
{
|
||||||
if (es.empty())
|
if (es.empty())
|
||||||
return exprs.add<ExprString>("");
|
return exprs.add<ExprString>(""_sds);
|
||||||
|
|
||||||
/* Figure out the minimum indentation. Note that by design
|
/* Figure out the minimum indentation. Note that by design
|
||||||
whitespace-only final lines are not taken into account. (So
|
whitespace-only final lines are not taken into account. (So
|
||||||
|
|
@ -332,7 +334,7 @@ ParserState::stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, st
|
||||||
// If there is nothing at all, return the empty string directly.
|
// If there is nothing at all, return the empty string directly.
|
||||||
// This also ensures that equivalent empty strings result in the same ast, which is helpful when testing formatters.
|
// This also ensures that equivalent empty strings result in the same ast, which is helpful when testing formatters.
|
||||||
if (es2.size() == 0) {
|
if (es2.size() == 0) {
|
||||||
auto * const result = exprs.add<ExprString>("");
|
auto * const result = exprs.add<ExprString>(""_sds);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
44
src/libexpr/include/nix/expr/static-string-data.hh
Normal file
44
src/libexpr/include/nix/expr/static-string-data.hh
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma once
|
||||||
|
///@file
|
||||||
|
|
||||||
|
#include "nix/expr/value.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
template<size_t N>
|
||||||
|
struct StringData::Static
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @note Must be first to make layout compatible with StringData.
|
||||||
|
*/
|
||||||
|
const size_t size = N - 1;
|
||||||
|
char data[N];
|
||||||
|
|
||||||
|
consteval Static(const char (&str)[N])
|
||||||
|
{
|
||||||
|
static_assert(N > 0);
|
||||||
|
if (str[size] != '\0')
|
||||||
|
throw;
|
||||||
|
std::copy_n(str, N, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
operator const StringData &() const &
|
||||||
|
{
|
||||||
|
static_assert(sizeof(decltype(*this)) >= sizeof(StringData));
|
||||||
|
static_assert(alignof(decltype(*this)) == alignof(StringData));
|
||||||
|
/* NOTE: This cast is somewhat on the fence of what's legal in C++.
|
||||||
|
The question boils down to whether flexible array members are
|
||||||
|
layout compatible with fixed-size arrays. This is a gray area, since
|
||||||
|
FAMs are not standard anyway.
|
||||||
|
*/
|
||||||
|
return *reinterpret_cast<const StringData *>(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<StringData::Static S>
|
||||||
|
const StringData & operator""_sds()
|
||||||
|
{
|
||||||
|
return S;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nix
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <memory_resource>
|
#include <memory_resource>
|
||||||
#include "nix/expr/value.hh"
|
#include "nix/expr/value.hh"
|
||||||
|
#include "nix/expr/static-string-data.hh"
|
||||||
#include "nix/util/chunked-vector.hh"
|
#include "nix/util/chunked-vector.hh"
|
||||||
#include "nix/util/error.hh"
|
#include "nix/util/error.hh"
|
||||||
|
|
||||||
|
|
@ -16,7 +17,6 @@ class SymbolValue : protected Value
|
||||||
friend class SymbolStr;
|
friend class SymbolStr;
|
||||||
friend class SymbolTable;
|
friend class SymbolTable;
|
||||||
|
|
||||||
uint32_t size_;
|
|
||||||
uint32_t idx;
|
uint32_t idx;
|
||||||
|
|
||||||
SymbolValue() = default;
|
SymbolValue() = default;
|
||||||
|
|
@ -24,7 +24,7 @@ class SymbolValue : protected Value
|
||||||
public:
|
public:
|
||||||
operator std::string_view() const noexcept
|
operator std::string_view() const noexcept
|
||||||
{
|
{
|
||||||
return {c_str(), size_};
|
return string_view();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -96,13 +96,13 @@ class SymbolStr
|
||||||
SymbolValueStore & store;
|
SymbolValueStore & store;
|
||||||
std::string_view s;
|
std::string_view s;
|
||||||
std::size_t hash;
|
std::size_t hash;
|
||||||
std::pmr::polymorphic_allocator<char> & alloc;
|
std::pmr::memory_resource & resource;
|
||||||
|
|
||||||
Key(SymbolValueStore & store, std::string_view s, std::pmr::polymorphic_allocator<char> & stringAlloc)
|
Key(SymbolValueStore & store, std::string_view s, std::pmr::memory_resource & stringMemory)
|
||||||
: store(store)
|
: store(store)
|
||||||
, s(s)
|
, s(s)
|
||||||
, hash(HashType{}(s))
|
, hash(HashType{}(s))
|
||||||
, alloc(stringAlloc)
|
, resource(stringMemory)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -122,14 +122,10 @@ public:
|
||||||
// for multi-threaded implementations: lock store and allocator here
|
// for multi-threaded implementations: lock store and allocator here
|
||||||
const auto & [v, idx] = key.store.add(SymbolValue{});
|
const auto & [v, idx] = key.store.add(SymbolValue{});
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
v.mkStringNoCopy("", nullptr);
|
v.mkStringNoCopy(""_sds, nullptr);
|
||||||
} else {
|
} else {
|
||||||
auto s = key.alloc.allocate(size + 1);
|
v.mkStringNoCopy(StringData::make(key.resource, key.s));
|
||||||
memcpy(s, key.s.data(), size);
|
|
||||||
s[size] = '\0';
|
|
||||||
v.mkStringNoCopy(s, nullptr);
|
|
||||||
}
|
}
|
||||||
v.size_ = size;
|
|
||||||
v.idx = idx;
|
v.idx = idx;
|
||||||
this->s = &v;
|
this->s = &v;
|
||||||
}
|
}
|
||||||
|
|
@ -139,6 +135,12 @@ public:
|
||||||
return *s == s2;
|
return *s == s2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[gnu::always_inline]]
|
||||||
|
const StringData & string_data() const noexcept
|
||||||
|
{
|
||||||
|
return s->string_data();
|
||||||
|
}
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
const char * c_str() const noexcept
|
const char * c_str() const noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -155,13 +157,17 @@ public:
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
bool empty() const noexcept
|
bool empty() const noexcept
|
||||||
{
|
{
|
||||||
return s->size_ == 0;
|
auto * p = &s->string_data();
|
||||||
|
// Save a dereference in the sentinel value case
|
||||||
|
if (p == &""_sds)
|
||||||
|
return true;
|
||||||
|
return p->size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
size_t size() const noexcept
|
size_t size() const noexcept
|
||||||
{
|
{
|
||||||
return s->size_;
|
return s->string_data().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
|
|
@ -259,7 +265,6 @@ private:
|
||||||
* During its lifetime the monotonic buffer holds all strings and nodes, if the symbol set is node based.
|
* During its lifetime the monotonic buffer holds all strings and nodes, if the symbol set is node based.
|
||||||
*/
|
*/
|
||||||
std::pmr::monotonic_buffer_resource buffer;
|
std::pmr::monotonic_buffer_resource buffer;
|
||||||
std::pmr::polymorphic_allocator<char> stringAlloc{&buffer};
|
|
||||||
SymbolStr::SymbolValueStore store{16};
|
SymbolStr::SymbolValueStore store{16};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -282,7 +287,7 @@ public:
|
||||||
// Most symbols are looked up more than once, so we trade off insertion performance
|
// Most symbols are looked up more than once, so we trade off insertion performance
|
||||||
// for lookup performance.
|
// for lookup performance.
|
||||||
// FIXME: make this thread-safe.
|
// FIXME: make this thread-safe.
|
||||||
return Symbol(*symbols.insert(SymbolStr::Key{store, s, stringAlloc}).first);
|
return Symbol(*symbols.insert(SymbolStr::Key{store, s, buffer}).first);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SymbolStr> resolve(const std::vector<Symbol> & symbols) const
|
std::vector<SymbolStr> resolve(const std::vector<Symbol> & symbols) const
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
|
#include <bit>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
|
#include <memory_resource>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <string_view>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <concepts>
|
#include <concepts>
|
||||||
|
|
||||||
|
|
@ -186,6 +192,91 @@ public:
|
||||||
friend struct Value;
|
friend struct Value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class StringData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using size_type = std::size_t;
|
||||||
|
|
||||||
|
size_type size_;
|
||||||
|
char data_[];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This in particular ensures that we cannot have a `StringData`
|
||||||
|
* that we use by value, which is just what we want!
|
||||||
|
*
|
||||||
|
* Dynamically sized types aren't a thing in C++ and even flexible array
|
||||||
|
* members are a language extension and beyond the realm of standard C++.
|
||||||
|
* Technically, sizeof data_ member is 0 and the intended way to use flexible
|
||||||
|
* array members is to allocate sizeof(StrindData) + count * sizeof(char) bytes
|
||||||
|
* and the compiler will consider alignment restrictions for the FAM.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
StringData(StringData &&) = delete;
|
||||||
|
StringData & operator=(StringData &&) = delete;
|
||||||
|
StringData(const StringData &) = delete;
|
||||||
|
StringData & operator=(const StringData &) = delete;
|
||||||
|
~StringData() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
StringData() = delete;
|
||||||
|
|
||||||
|
explicit StringData(size_type size)
|
||||||
|
: size_(size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Allocate StringData on the (possibly) GC-managed heap and copy
|
||||||
|
* the contents of s to it.
|
||||||
|
*/
|
||||||
|
static const StringData & make(std::string_view s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate StringData on the (possibly) GC-managed heap.
|
||||||
|
* @param size Length of the string (without the NUL terminator).
|
||||||
|
*/
|
||||||
|
static StringData & alloc(size_t size);
|
||||||
|
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * data() noexcept
|
||||||
|
{
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * data() const noexcept
|
||||||
|
{
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * c_str() const noexcept
|
||||||
|
{
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::string_view view() const noexcept
|
||||||
|
{
|
||||||
|
return std::string_view(data_, size_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t N>
|
||||||
|
struct Static;
|
||||||
|
|
||||||
|
static StringData & make(std::pmr::memory_resource & resource, std::string_view s)
|
||||||
|
{
|
||||||
|
auto & res =
|
||||||
|
*new (resource.allocate(sizeof(StringData) + s.size() + 1, alignof(StringData))) StringData(s.size());
|
||||||
|
std::memcpy(res.data_, s.data(), s.size());
|
||||||
|
res.data_[s.size()] = '\0';
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -219,7 +310,7 @@ struct ValueBase
|
||||||
*/
|
*/
|
||||||
struct StringWithContext
|
struct StringWithContext
|
||||||
{
|
{
|
||||||
const char * c_str;
|
const StringData * str;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of the context itself.
|
* The type of the context itself.
|
||||||
|
|
@ -234,7 +325,7 @@ struct ValueBase
|
||||||
*/
|
*/
|
||||||
struct Context
|
struct Context
|
||||||
{
|
{
|
||||||
using value_type = const char *;
|
using value_type = const StringData *;
|
||||||
using size_type = std::size_t;
|
using size_type = std::size_t;
|
||||||
using iterator = const value_type *;
|
using iterator = const value_type *;
|
||||||
|
|
||||||
|
|
@ -285,7 +376,7 @@ struct ValueBase
|
||||||
struct Path
|
struct Path
|
||||||
{
|
{
|
||||||
SourceAccessor * accessor;
|
SourceAccessor * accessor;
|
||||||
const char * path;
|
const StringData * path;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Null
|
struct Null
|
||||||
|
|
@ -646,13 +737,13 @@ protected:
|
||||||
void getStorage(StringWithContext & string) const noexcept
|
void getStorage(StringWithContext & string) const noexcept
|
||||||
{
|
{
|
||||||
string.context = untagPointer<decltype(string.context)>(payload[0]);
|
string.context = untagPointer<decltype(string.context)>(payload[0]);
|
||||||
string.c_str = std::bit_cast<const char *>(payload[1]);
|
string.str = std::bit_cast<const StringData *>(payload[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getStorage(Path & path) const noexcept
|
void getStorage(Path & path) const noexcept
|
||||||
{
|
{
|
||||||
path.accessor = untagPointer<decltype(path.accessor)>(payload[0]);
|
path.accessor = untagPointer<decltype(path.accessor)>(payload[0]);
|
||||||
path.path = std::bit_cast<const char *>(payload[1]);
|
path.path = std::bit_cast<const StringData *>(payload[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setStorage(NixInt integer) noexcept
|
void setStorage(NixInt integer) noexcept
|
||||||
|
|
@ -697,7 +788,7 @@ protected:
|
||||||
|
|
||||||
void setStorage(StringWithContext string) noexcept
|
void setStorage(StringWithContext string) noexcept
|
||||||
{
|
{
|
||||||
setUntaggablePayload<pdString>(string.context, string.c_str);
|
setUntaggablePayload<pdString>(string.context, string.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setStorage(Path path) noexcept
|
void setStorage(Path path) noexcept
|
||||||
|
|
@ -1050,22 +1141,22 @@ public:
|
||||||
setStorage(b);
|
setStorage(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mkStringNoCopy(const char * s, const Value::StringWithContext::Context * context = nullptr) noexcept
|
void mkStringNoCopy(const StringData & s, const Value::StringWithContext::Context * context = nullptr) noexcept
|
||||||
{
|
{
|
||||||
setStorage(StringWithContext{.c_str = s, .context = context});
|
setStorage(StringWithContext{.str = &s, .context = context});
|
||||||
}
|
}
|
||||||
|
|
||||||
void mkString(std::string_view s);
|
void mkString(std::string_view s);
|
||||||
|
|
||||||
void mkString(std::string_view s, const NixStringContext & context);
|
void mkString(std::string_view s, const NixStringContext & context);
|
||||||
|
|
||||||
void mkStringMove(const char * s, const NixStringContext & context);
|
void mkStringMove(const StringData & s, const NixStringContext & context);
|
||||||
|
|
||||||
void mkPath(const SourcePath & path);
|
void mkPath(const SourcePath & path);
|
||||||
|
|
||||||
inline void mkPath(SourceAccessor * accessor, const char * path) noexcept
|
inline void mkPath(SourceAccessor * accessor, const StringData & path) noexcept
|
||||||
{
|
{
|
||||||
setStorage(Path{.accessor = accessor, .path = path});
|
setStorage(Path{.accessor = accessor, .path = &path});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void mkNull() noexcept
|
inline void mkNull() noexcept
|
||||||
|
|
@ -1163,17 +1254,23 @@ public:
|
||||||
|
|
||||||
SourcePath path() const
|
SourcePath path() const
|
||||||
{
|
{
|
||||||
return SourcePath(ref(pathAccessor()->shared_from_this()), CanonPath(CanonPath::unchecked_t(), pathStr()));
|
return SourcePath(
|
||||||
|
ref(pathAccessor()->shared_from_this()), CanonPath(CanonPath::unchecked_t(), std::string(pathStrView())));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view string_view() const noexcept
|
const StringData & string_data() const noexcept
|
||||||
{
|
{
|
||||||
return std::string_view{getStorage<StringWithContext>().c_str};
|
return *getStorage<StringWithContext>().str;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * c_str() const noexcept
|
const char * c_str() const noexcept
|
||||||
{
|
{
|
||||||
return getStorage<StringWithContext>().c_str;
|
return getStorage<StringWithContext>().str->data();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view string_view() const noexcept
|
||||||
|
{
|
||||||
|
return string_data().view();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Value::StringWithContext::Context * context() const noexcept
|
const Value::StringWithContext::Context * context() const noexcept
|
||||||
|
|
@ -1233,12 +1330,12 @@ public:
|
||||||
|
|
||||||
const char * pathStr() const noexcept
|
const char * pathStr() const noexcept
|
||||||
{
|
{
|
||||||
return getStorage<Path>().path;
|
return getStorage<Path>().path->c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view pathStrView() const noexcept
|
std::string_view pathStrView() const noexcept
|
||||||
{
|
{
|
||||||
return std::string_view{getStorage<Path>().path};
|
return getStorage<Path>().path->view();
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceAccessor * pathAccessor() const noexcept
|
SourceAccessor * pathAccessor() const noexcept
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include "nix/expr/eval-settings.hh"
|
#include "nix/expr/eval-settings.hh"
|
||||||
#include "nix/expr/gc-small-vector.hh"
|
#include "nix/expr/gc-small-vector.hh"
|
||||||
#include "nix/expr/json-to-value.hh"
|
#include "nix/expr/json-to-value.hh"
|
||||||
|
#include "nix/expr/static-string-data.hh"
|
||||||
#include "nix/store/globals.hh"
|
#include "nix/store/globals.hh"
|
||||||
#include "nix/store/names.hh"
|
#include "nix/store/names.hh"
|
||||||
#include "nix/store/path-references.hh"
|
#include "nix/store/path-references.hh"
|
||||||
|
|
@ -487,34 +488,34 @@ static void prim_typeOf(EvalState & state, const PosIdx pos, Value ** args, Valu
|
||||||
state.forceValue(*args[0], pos);
|
state.forceValue(*args[0], pos);
|
||||||
switch (args[0]->type()) {
|
switch (args[0]->type()) {
|
||||||
case nInt:
|
case nInt:
|
||||||
v.mkStringNoCopy("int");
|
v.mkStringNoCopy("int"_sds);
|
||||||
break;
|
break;
|
||||||
case nBool:
|
case nBool:
|
||||||
v.mkStringNoCopy("bool");
|
v.mkStringNoCopy("bool"_sds);
|
||||||
break;
|
break;
|
||||||
case nString:
|
case nString:
|
||||||
v.mkStringNoCopy("string");
|
v.mkStringNoCopy("string"_sds);
|
||||||
break;
|
break;
|
||||||
case nPath:
|
case nPath:
|
||||||
v.mkStringNoCopy("path");
|
v.mkStringNoCopy("path"_sds);
|
||||||
break;
|
break;
|
||||||
case nNull:
|
case nNull:
|
||||||
v.mkStringNoCopy("null");
|
v.mkStringNoCopy("null"_sds);
|
||||||
break;
|
break;
|
||||||
case nAttrs:
|
case nAttrs:
|
||||||
v.mkStringNoCopy("set");
|
v.mkStringNoCopy("set"_sds);
|
||||||
break;
|
break;
|
||||||
case nList:
|
case nList:
|
||||||
v.mkStringNoCopy("list");
|
v.mkStringNoCopy("list"_sds);
|
||||||
break;
|
break;
|
||||||
case nFunction:
|
case nFunction:
|
||||||
v.mkStringNoCopy("lambda");
|
v.mkStringNoCopy("lambda"_sds);
|
||||||
break;
|
break;
|
||||||
case nExternal:
|
case nExternal:
|
||||||
v.mkString(args[0]->external()->typeOf());
|
v.mkString(args[0]->external()->typeOf());
|
||||||
break;
|
break;
|
||||||
case nFloat:
|
case nFloat:
|
||||||
v.mkStringNoCopy("float");
|
v.mkStringNoCopy("float"_sds);
|
||||||
break;
|
break;
|
||||||
case nThunk:
|
case nThunk:
|
||||||
unreachable();
|
unreachable();
|
||||||
|
|
@ -2024,9 +2025,9 @@ static void prim_dirOf(EvalState & state, const PosIdx pos, Value ** args, Value
|
||||||
pos, *args[0], context, "while evaluating the first argument passed to 'builtins.dirOf'", false, false);
|
pos, *args[0], context, "while evaluating the first argument passed to 'builtins.dirOf'", false, false);
|
||||||
auto pos = path->rfind('/');
|
auto pos = path->rfind('/');
|
||||||
if (pos == path->npos)
|
if (pos == path->npos)
|
||||||
v.mkStringMove(".", context);
|
v.mkStringMove("."_sds, context);
|
||||||
else if (pos == 0)
|
else if (pos == 0)
|
||||||
v.mkStringMove("/", context);
|
v.mkStringMove("/"_sds, context);
|
||||||
else
|
else
|
||||||
v.mkString(path->substr(0, pos), context);
|
v.mkString(path->substr(0, pos), context);
|
||||||
}
|
}
|
||||||
|
|
@ -2309,10 +2310,10 @@ static const Value & fileTypeToString(EvalState & state, SourceAccessor::Type ty
|
||||||
|
|
||||||
static const Constants stringValues = []() {
|
static const Constants stringValues = []() {
|
||||||
Constants res;
|
Constants res;
|
||||||
res.regular.mkStringNoCopy("regular");
|
res.regular.mkStringNoCopy("regular"_sds);
|
||||||
res.directory.mkStringNoCopy("directory");
|
res.directory.mkStringNoCopy("directory"_sds);
|
||||||
res.symlink.mkStringNoCopy("symlink");
|
res.symlink.mkStringNoCopy("symlink"_sds);
|
||||||
res.unknown.mkStringNoCopy("unknown");
|
res.unknown.mkStringNoCopy("unknown"_sds);
|
||||||
return res;
|
return res;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
|
@ -4463,7 +4464,7 @@ static void prim_substring(EvalState & state, const PosIdx pos, Value ** args, V
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
state.forceValue(*args[2], pos);
|
state.forceValue(*args[2], pos);
|
||||||
if (args[2]->type() == nString) {
|
if (args[2]->type() == nString) {
|
||||||
v.mkStringNoCopy("", args[2]->context());
|
v.mkStringNoCopy(""_sds, args[2]->context());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "nix/expr/primops.hh"
|
#include "nix/expr/primops.hh"
|
||||||
#include "nix/expr/eval-inline.hh"
|
#include "nix/expr/eval-inline.hh"
|
||||||
|
#include "nix/expr/static-string-data.hh"
|
||||||
|
|
||||||
#include "expr-config-private.hh"
|
#include "expr-config-private.hh"
|
||||||
|
|
||||||
|
|
@ -136,7 +137,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va
|
||||||
normalizeDatetimeFormat(t);
|
normalizeDatetimeFormat(t);
|
||||||
#endif
|
#endif
|
||||||
auto attrs = state.buildBindings(2);
|
auto attrs = state.buildBindings(2);
|
||||||
attrs.alloc("_type").mkStringNoCopy("timestamp");
|
attrs.alloc("_type").mkStringNoCopy("timestamp"_sds);
|
||||||
std::ostringstream s;
|
std::ostringstream s;
|
||||||
s << t;
|
s << t;
|
||||||
auto str = s.view();
|
auto str = s.view();
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include "nix/expr/eval-inline.hh"
|
#include "nix/expr/eval-inline.hh"
|
||||||
#include "nix/store/profiles.hh"
|
#include "nix/store/profiles.hh"
|
||||||
#include "nix/expr/print-ambiguous.hh"
|
#include "nix/expr/print-ambiguous.hh"
|
||||||
|
#include "nix/expr/static-string-data.hh"
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
@ -56,7 +57,7 @@ bool createUserEnv(
|
||||||
|
|
||||||
auto attrs = state.buildBindings(7 + outputs.size());
|
auto attrs = state.buildBindings(7 + outputs.size());
|
||||||
|
|
||||||
attrs.alloc(state.s.type).mkStringNoCopy("derivation");
|
attrs.alloc(state.s.type).mkStringNoCopy("derivation"_sds);
|
||||||
attrs.alloc(state.s.name).mkString(i.queryName());
|
attrs.alloc(state.s.name).mkString(i.queryName());
|
||||||
auto system = i.querySystem();
|
auto system = i.querySystem();
|
||||||
if (!system.empty())
|
if (!system.empty())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue