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>
@@ -47,28 +50,22 @@ using json = nlohmann::json;
4750
4851namespace 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
7471RootValue 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
819816void Value::mkString (std::string_view s)
820817{
821- mkStringNoCopy (makeImmutableString (s));
818+ mkStringNoCopy (StringData::make (s));
822819}
823820
824821static 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
838835void 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
848845void 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
853850inline 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}
@@ -2369,7 +2369,7 @@ BackedStringView EvalState::coerceToString(
23692369 if (!canonicalizePath && !copyToStore) {
23702370 // FIXME: hack to preserve path literals that end in a
23712371 // slash, as in /foo/${x}.
2372- return v.pathStrView ();
2372+ return v.pathStr (). view ();
23732373 } else if (copyToStore) {
23742374 return store->printStorePath (copyPathToStore (context, v.path ()));
23752375 } else {
0 commit comments