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"
2829#include " parser-tab.hh"
2930
3031#include < algorithm>
32+ #include < cstddef>
33+ #include < cstdlib>
3134#include < iostream>
3235#include < sstream>
3336#include < cstring>
@@ -48,28 +51,22 @@ using json = nlohmann::json;
4851
4952namespace nix {
5053
51- static char * allocString (size_t size)
54+ StringData & StringData::alloc (size_t size)
5255{
53- char * t;
54- t = (char *) GC_MALLOC_ATOMIC (size);
55- if (!t)
56+ auto res = (StringData *) GC_MALLOC_ATOMIC (sizeof (size_t ) + size + 1 );
57+ if (!res) {
5658 throw std::bad_alloc ();
57- return t;
59+ }
60+ res->m_size = size;
61+ return *res;
5862}
5963
60- // When there's no need to write to the string, we can optimize away empty
61- // string allocations.
62- // This function handles makeImmutableString(std::string_view()) by returning
63- // the empty string.
64- static const char * makeImmutableString (std::string_view s)
64+ StringData & StringData::make (std::string_view s)
6565{
66- const size_t size = s.size ();
67- if (size == 0 )
68- return " " ;
69- auto t = allocString (size + 1 );
70- memcpy (t, s.data (), size);
71- t[size] = ' \0 ' ;
72- return t;
66+ auto & res = alloc (s.size ());
67+ memcpy (&res.m_data , s.data (), s.size ());
68+ res.m_data [s.size ()] = ' \0 ' ;
69+ return res;
7370}
7471
7572RootValue allocRootValue (Value * v)
@@ -585,7 +582,7 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
585582 .name = name,
586583 .arity = 0 , // FIXME: figure out how deep by syntax only? It's not semantically useful though...
587584 .args = {},
588- .doc = makeImmutableString (s.view ()), // NOTE: memory leak when compiled without GC
585+ .doc = StringData::make (s.view ()). view (). data ( ), // NOTE: memory leak when compiled without GC
589586 };
590587 }
591588 if (isFunctor (v)) {
@@ -819,7 +816,7 @@ DebugTraceStacker::DebugTraceStacker(EvalState & evalState, DebugTrace t)
819816
820817void Value::mkString (std::string_view s)
821818{
822- mkStringNoCopy (makeImmutableString (s));
819+ mkStringNoCopy (StringData::make (s));
823820}
824821
825822Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuilder (const NixStringContext & context)
@@ -829,23 +826,23 @@ Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuild
829826
830827 auto ctx = new (allocBytes (sizeof (Context) + context.size () * sizeof (value_type))) Context (context.size ());
831828 std::ranges::transform (
832- context, ctx->elems , [](const NixStringContextElem & elt) { return makeImmutableString (elt.to_string ()); });
829+ context, ctx->elems , [](const NixStringContextElem & elt) { return & StringData::make (elt.to_string ()); });
833830 return ctx;
834831}
835832
836833void Value::mkString (std::string_view s, const NixStringContext & context)
837834{
838- mkStringNoCopy (makeImmutableString (s), Value::StringWithContext::Context::fromBuilder (context));
835+ mkStringNoCopy (StringData::make (s), Value::StringWithContext::Context::fromBuilder (context));
839836}
840837
841- void Value::mkStringMove (const char * s, const NixStringContext & context)
838+ void Value::mkStringMove (const StringData & s, const NixStringContext & context)
842839{
843840 mkStringNoCopy (s, Value::StringWithContext::Context::fromBuilder (context));
844841}
845842
846843void Value::mkPath (const SourcePath & path)
847844{
848- mkPath (&*path.accessor , makeImmutableString (path.path .abs ()));
845+ mkPath (&*path.accessor , StringData::make (path.path .abs ()));
849846}
850847
851848inline Value * EvalState::lookupVar (Env * env, const ExprVar & var, bool noEval)
@@ -1558,6 +1555,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
15581555 .withFrame (*vCur.lambda ().env , lambda)
15591556 .debugThrow ();
15601557 }
1558+
15611559 unreachable ();
15621560 }
15631561 } else {
@@ -2094,25 +2092,27 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
20942092 } else if (firstType == nFloat) {
20952093 v.mkFloat (nf);
20962094 } else if (firstType == nPath) {
2097- if (!context.empty ())
2095+ if (!context.empty ()) {
20982096 state.error <EvalError>(" a string that refers to a store path cannot be appended to a path" )
20992097 .atPos (pos)
21002098 .withFrame (env, *this )
21012099 .debugThrow ();
2100+ }
2101+
21022102 std::string result_str;
21032103 result_str.reserve (sSize );
21042104 for (const auto & part : strings) {
21052105 result_str += *part;
21062106 }
21072107 v.mkPath (state.rootPath (CanonPath (result_str)));
21082108 } else {
2109- char * result_str = allocString (sSize + 1 );
2110- char * tmp = result_str;
2109+ auto & result_str = StringData::alloc (sSize );
2110+ auto * tmp = result_str. data () ;
21112111 for (const auto & part : strings) {
21122112 memcpy (tmp, part->data (), part->size ());
21132113 tmp += part->size ();
21142114 }
2115- *tmp = 0 ;
2115+ *tmp = ' \0 ' ;
21162116 v.mkStringMove (result_str, context);
21172117 }
21182118}
@@ -2288,7 +2288,7 @@ void copyContext(const Value & v, NixStringContext & context, const Experimental
22882288{
22892289 if (auto * ctx = v.context ())
22902290 for (auto * elem : *ctx)
2291- context.insert (NixStringContextElem::parse (elem, xpSettings));
2291+ context.insert (NixStringContextElem::parse (elem-> view () , xpSettings));
22922292}
22932293
22942294std::string_view EvalState::forceString (
@@ -2370,7 +2370,7 @@ BackedStringView EvalState::coerceToString(
23702370 if (!canonicalizePath && !copyToStore) {
23712371 // FIXME: hack to preserve path literals that end in a
23722372 // slash, as in /foo/${x}.
2373- return v.pathStrView ();
2373+ return v.pathStr (). view ();
23742374 } else if (copyToStore) {
23752375 return store->printStorePath (copyPathToStore (context, v.path ()));
23762376 } else {
0 commit comments