Skip to content

Commit dda5c2f

Browse files
glittersharkEricson2314
authored andcommitted
Pascal Strings, Baby
1 parent c020539 commit dda5c2f

File tree

19 files changed

+228
-117
lines changed

19 files changed

+228
-117
lines changed

src/libexpr-c/nix_api_value.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ nix_get_string(nix_c_context * context, const nix_value * value, nix_get_string_
235235
try {
236236
auto & v = check_value_in(value);
237237
assert(v.type() == nix::nString);
238-
call_nix_get_string_callback(v.c_str(), callback, user_data);
238+
call_nix_get_string_callback(v.string_view(), callback, user_data);
239239
}
240240
NIXC_CATCH_ERRS
241241
}
@@ -254,7 +254,7 @@ const char * nix_get_path_string(nix_c_context * context, const nix_value * valu
254254
// We could use v.path().to_string().c_str(), but I'm concerned this
255255
// crashes. Looks like .path() allocates a CanonPath with a copy of the
256256
// string, then it gets the underlying data from that.
257-
return v.pathStr();
257+
return v.pathStr()->view().data();
258258
}
259259
NIXC_CATCH_ERRS_NULL
260260
}
@@ -439,7 +439,7 @@ nix_get_attr_byidx(nix_c_context * context, nix_value * value, EvalState * state
439439
return nullptr;
440440
}
441441
const nix::Attr & a = (*v.attrs())[i];
442-
*name = state->state.symbols[a.name].c_str();
442+
*name = state->state.symbols[a.name].string_data()->view().data();
443443
nix_gc_incref(nullptr, a.value);
444444
state->state.forceValue(*a.value, nix::noPos);
445445
return as_nix_value_ptr(a.value);
@@ -460,7 +460,7 @@ nix_value * nix_get_attr_byidx_lazy(
460460
return nullptr;
461461
}
462462
const nix::Attr & a = (*v.attrs())[i];
463-
*name = state->state.symbols[a.name].c_str();
463+
*name = state->state.symbols[a.name].string_data()->view().data();
464464
nix_gc_incref(nullptr, a.value);
465465
// Note: intentionally NOT calling forceValue() to keep the attribute lazy
466466
return as_nix_value_ptr(a.value);
@@ -480,7 +480,7 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, nix_value * value,
480480
return nullptr;
481481
}
482482
const nix::Attr & a = (*v.attrs())[i];
483-
return state->state.symbols[a.name].c_str();
483+
return state->state.symbols[a.name].string_data()->view().data();
484484
}
485485
NIXC_CATCH_ERRS_NULL
486486
}

src/libexpr-test-support/include/nix/expr/tests/libexpr.hh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,11 @@ MATCHER(IsAttrs, "")
104104
MATCHER_P(IsStringEq, s, fmt("The string is equal to \"%1%\"", s))
105105
{
106106
if (arg.type() != nString) {
107+
*result_listener << "of non-String type " << arg.type();
107108
return false;
108109
}
109-
return std::string_view(arg.c_str()) == s;
110+
*result_listener << "string(" << arg.string_view() << ")";
111+
return arg.string_view() == s;
110112
}
111113

112114
MATCHER_P(IsIntEq, v, fmt("The string is equal to \"%1%\"", v))

src/libexpr-tests/value/value.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "nix/expr/value.hh"
22

33
#include "nix/store/tests/libstore.hh"
4+
#include <gtest/gtest.h>
45

56
namespace nix {
67

@@ -22,4 +23,21 @@ TEST_F(ValueTest, vInt)
2223
ASSERT_EQ(true, vInt.isValid());
2324
}
2425

26+
TEST_F(ValueTest, staticString)
27+
{
28+
Value vStr1;
29+
Value vStr2;
30+
vStr1.mkStringNoCopy<"foo">();
31+
vStr2.mkStringNoCopy<"foo">();
32+
33+
auto sd1 = vStr1.string_data();
34+
auto sd2 = vStr2.string_data();
35+
36+
// The strings should be the same
37+
ASSERT_EQ(sd1->view(), sd2->view());
38+
39+
// The strings should also be backed by the same (static) allocation
40+
ASSERT_EQ(std::bit_cast<size_t>(sd1), std::bit_cast<size_t>(sd2));
41+
}
42+
2543
} // namespace nix

src/libexpr/eval-cache.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ Value & AttrCursor::forceValue()
406406

407407
if (root->db && (!cachedValue || std::get_if<placeholder_t>(&cachedValue->second))) {
408408
if (v.type() == nString)
409-
cachedValue = {root->db->setString(getKey(), v.c_str(), v.context()), string_t{v.c_str(), {}}};
409+
cachedValue = {root->db->setString(getKey(), v.string_view(), v.context()), string_t{v.string_view(), {}}};
410410
else if (v.type() == nPath) {
411411
auto path = v.path().path;
412412
cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}};
@@ -541,7 +541,7 @@ std::string AttrCursor::getString()
541541
if (v.type() != nString && v.type() != nPath)
542542
root->state.error<TypeError>("'%s' is not a string but %s", getAttrPathStr(), showType(v)).debugThrow();
543543

544-
return v.type() == nString ? v.c_str() : v.path().to_string();
544+
return v.type() == nString ? std::string(v.string_view()) : v.path().to_string();
545545
}
546546

547547
string_t AttrCursor::getStringWithContext()
@@ -580,7 +580,7 @@ string_t AttrCursor::getStringWithContext()
580580
if (v.type() == nString) {
581581
NixStringContext context;
582582
copyContext(v, context);
583-
return {v.c_str(), std::move(context)};
583+
return {std::string(v.string_view()), std::move(context)};
584584
} else if (v.type() == nPath)
585585
return {v.path().to_string(), {}};
586586
else

src/libexpr/eval.cc

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "nix/expr/primops.hh"
44
#include "nix/expr/print-options.hh"
55
#include "nix/expr/symbol-table.hh"
6+
#include "nix/expr/value.hh"
67
#include "nix/util/exit.hh"
78
#include "nix/util/types.hh"
89
#include "nix/util/util.hh"
@@ -28,6 +29,8 @@
2829
#include "parser-tab.hh"
2930

3031
#include <algorithm>
32+
#include <cstddef>
33+
#include <cstdlib>
3134
#include <iostream>
3235
#include <sstream>
3336
#include <cstring>
@@ -47,28 +50,22 @@ using json = nlohmann::json;
4750

4851
namespace nix {
4952

50-
static char * allocString(size_t size)
53+
StringData * StringData::alloc(size_t size)
5154
{
52-
char * t;
53-
t = (char *) GC_MALLOC_ATOMIC(size);
54-
if (!t)
55+
auto res = (StringData *) GC_MALLOC_ATOMIC(sizeof(size_t) + size + 1);
56+
if (!res) {
5557
throw std::bad_alloc();
56-
return t;
58+
}
59+
res->m_size = size;
60+
return res;
5761
}
5862

59-
// When there's no need to write to the string, we can optimize away empty
60-
// string allocations.
61-
// This function handles makeImmutableString(std::string_view()) by returning
62-
// the empty string.
63-
static const char * makeImmutableString(std::string_view s)
63+
StringData * StringData::make(std::string_view s)
6464
{
65-
const size_t size = s.size();
66-
if (size == 0)
67-
return "";
68-
auto t = allocString(size + 1);
69-
memcpy(t, s.data(), size);
70-
t[size] = '\0';
71-
return t;
65+
auto res = alloc(s.size());
66+
memcpy(&res->m_data, s.data(), s.size());
67+
res->m_data[s.size()] = '\0';
68+
return res;
7269
}
7370

7471
RootValue allocRootValue(Value * v)
@@ -584,7 +581,7 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
584581
.name = name,
585582
.arity = 0, // FIXME: figure out how deep by syntax only? It's not semantically useful though...
586583
.args = {},
587-
.doc = makeImmutableString(s.view()), // NOTE: memory leak when compiled without GC
584+
.doc = StringData::make(s.view())->view().data(), // NOTE: memory leak when compiled without GC
588585
};
589586
}
590587
if (isFunctor(v)) {
@@ -818,7 +815,7 @@ DebugTraceStacker::DebugTraceStacker(EvalState & evalState, DebugTrace t)
818815

819816
void Value::mkString(std::string_view s)
820817
{
821-
mkStringNoCopy(makeImmutableString(s));
818+
mkStringNoCopy(StringData::make(s));
822819
}
823820

824821
static const char ** encodeContext(const NixStringContext & context)
@@ -827,7 +824,7 @@ static const char ** encodeContext(const NixStringContext & context)
827824
size_t n = 0;
828825
auto ctx = (const char **) allocBytes((context.size() + 1) * sizeof(char *));
829826
for (auto & i : context) {
830-
ctx[n++] = makeImmutableString({i.to_string()});
827+
ctx[n++] = StringData::make({i.to_string()})->view().data();
831828
}
832829
ctx[n] = nullptr;
833830
return ctx;
@@ -837,17 +834,17 @@ static const char ** encodeContext(const NixStringContext & context)
837834

838835
void Value::mkString(std::string_view s, const NixStringContext & context)
839836
{
840-
mkStringNoCopy(makeImmutableString(s), encodeContext(context));
837+
mkStringNoCopy(StringData::make(s), encodeContext(context));
841838
}
842839

843-
void Value::mkStringMove(const char * s, const NixStringContext & context)
840+
void Value::mkStringMove(const StringData * s, const NixStringContext & context)
844841
{
845842
mkStringNoCopy(s, encodeContext(context));
846843
}
847844

848845
void Value::mkPath(const SourcePath & path)
849846
{
850-
mkPath(&*path.accessor, makeImmutableString(path.path.abs()));
847+
mkPath(&*path.accessor, StringData::make(path.path.abs()));
851848
}
852849

853850
inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
@@ -1559,6 +1556,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
15591556
.withFrame(*vCur.lambda().env, lambda)
15601557
.debugThrow();
15611558
}
1559+
15621560
unreachable();
15631561
}
15641562
} else {
@@ -2095,25 +2093,27 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
20952093
} else if (firstType == nFloat) {
20962094
v.mkFloat(nf);
20972095
} else if (firstType == nPath) {
2098-
if (!context.empty())
2096+
if (!context.empty()) {
20992097
state.error<EvalError>("a string that refers to a store path cannot be appended to a path")
21002098
.atPos(pos)
21012099
.withFrame(env, *this)
21022100
.debugThrow();
2101+
}
2102+
21032103
std::string result_str;
21042104
result_str.reserve(sSize);
21052105
for (const auto & part : strings) {
21062106
result_str += *part;
21072107
}
21082108
v.mkPath(state.rootPath(CanonPath(result_str)));
21092109
} else {
2110-
char * result_str = allocString(sSize + 1);
2111-
char * tmp = result_str;
2110+
auto result_str = StringData::alloc(sSize);
2111+
auto tmp = result_str->data();
21122112
for (const auto & part : strings) {
21132113
memcpy(tmp, part->data(), part->size());
21142114
tmp += part->size();
21152115
}
2116-
*tmp = 0;
2116+
*tmp = '\0';
21172117
v.mkStringMove(result_str, context);
21182118
}
21192119
}
@@ -2366,12 +2366,15 @@ BackedStringView EvalState::coerceToString(
23662366
}
23672367

23682368
if (v.type() == nPath) {
2369-
return !canonicalizePath && !copyToStore
2370-
? // FIXME: hack to preserve path literals that end in a
2371-
// slash, as in /foo/${x}.
2372-
v.pathStr()
2373-
: copyToStore ? store->printStorePath(copyPathToStore(context, v.path()))
2374-
: std::string(v.path().path.abs());
2369+
if (!canonicalizePath && !copyToStore) {
2370+
// FIXME: hack to preserve path literals that end in a
2371+
// slash, as in /foo/${x}.
2372+
return v.pathStr()->view();
2373+
} else if (copyToStore) {
2374+
return store->printStorePath(copyPathToStore(context, v.path()));
2375+
} else {
2376+
return std::string(v.path().path.abs());
2377+
}
23752378
}
23762379

23772380
if (v.type() == nAttrs) {
@@ -2624,7 +2627,7 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st
26242627
return;
26252628

26262629
case nString:
2627-
if (strcmp(v1.c_str(), v2.c_str()) != 0) {
2630+
if (v1.string_view() != v2.string_view()) {
26282631
error<AssertionError>(
26292632
"string '%s' is not equal to string '%s'",
26302633
ValuePrinter(*this, v1, errorPrintOptions),
@@ -2641,7 +2644,7 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st
26412644
ValuePrinter(*this, v2, errorPrintOptions))
26422645
.debugThrow();
26432646
}
2644-
if (strcmp(v1.pathStr(), v2.pathStr()) != 0) {
2647+
if (v1.pathStr()->view() != v2.pathStr()->view()) {
26452648
error<AssertionError>(
26462649
"path '%s' is not equal to path '%s'",
26472650
ValuePrinter(*this, v1, errorPrintOptions),
@@ -2807,12 +2810,12 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
28072810
return v1.boolean() == v2.boolean();
28082811

28092812
case nString:
2810-
return strcmp(v1.c_str(), v2.c_str()) == 0;
2813+
return v1.string_view() == v2.string_view();
28112814

28122815
case nPath:
28132816
return
28142817
// FIXME: compare accessors by their fingerprint.
2815-
v1.pathAccessor() == v2.pathAccessor() && strcmp(v1.pathStr(), v2.pathStr()) == 0;
2818+
v1.pathAccessor() == v2.pathAccessor() && v1.pathStr()->view() == v2.pathStr()->view();
28162819

28172820
case nNull:
28182821
return true;

src/libexpr/get-drvs.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
168168
for (auto elem : outTI->listView()) {
169169
if (elem->type() != nString)
170170
throw errMsg;
171-
auto out = outputs.find(elem->c_str());
171+
auto out = outputs.find(elem->string_view());
172172
if (out == outputs.end())
173173
throw errMsg;
174174
result.insert(*out);
@@ -245,7 +245,7 @@ std::string PackageInfo::queryMetaString(const std::string & name)
245245
Value * v = queryMeta(name);
246246
if (!v || v->type() != nString)
247247
return "";
248-
return v->c_str();
248+
return std::string(v->string_view());
249249
}
250250

251251
NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def)
@@ -258,7 +258,7 @@ NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def)
258258
if (v->type() == nString) {
259259
/* Backwards compatibility with before we had support for
260260
integer meta fields. */
261-
if (auto n = string2Int<NixInt::Inner>(v->c_str()))
261+
if (auto n = string2Int<NixInt::Inner>(v->string_view()))
262262
return NixInt{*n};
263263
}
264264
return def;
@@ -274,7 +274,7 @@ NixFloat PackageInfo::queryMetaFloat(const std::string & name, NixFloat def)
274274
if (v->type() == nString) {
275275
/* Backwards compatibility with before we had support for
276276
float meta fields. */
277-
if (auto n = string2Float<NixFloat>(v->c_str()))
277+
if (auto n = string2Float<NixFloat>(v->string_view()))
278278
return *n;
279279
}
280280
return def;

src/libexpr/include/nix/expr/eval-cache.hh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,11 @@ struct int_t
7979

8080
typedef uint64_t AttrId;
8181
typedef std::pair<AttrId, Symbol> AttrKey;
82-
typedef std::pair<std::string, NixStringContext> string_t;
82+
typedef std::pair<
83+
// XXX aspen: StringData?
84+
std::string,
85+
NixStringContext>
86+
string_t;
8387

8488
typedef std::variant<
8589
std::vector<Symbol>,

src/libexpr/include/nix/expr/get-drvs.hh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace nix {
1515
struct PackageInfo
1616
{
1717
public:
18-
typedef std::map<std::string, std::optional<StorePath>> Outputs;
18+
typedef std::map<std::string, std::optional<StorePath>, std::less<>> Outputs;
1919

2020
private:
2121
EvalState * state;

0 commit comments

Comments
 (0)