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}
@@ -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 ;
0 commit comments