Merge branch 'more-stringviews' of https://github.com/pennae/nix

This commit is contained in:
Eelco Dolstra 2022-02-02 12:38:37 +01:00
commit cd35bbbeef
29 changed files with 247 additions and 161 deletions

View file

@ -121,7 +121,7 @@ Pos findPackageFilename(EvalState & state, Value & v, std::string what)
std::string filename(pos, 0, colon); std::string filename(pos, 0, colon);
unsigned int lineno; unsigned int lineno;
try { try {
lineno = std::stoi(std::string(pos, colon + 1)); lineno = std::stoi(std::string(pos, colon + 1, string::npos));
} catch (std::invalid_argument & e) { } catch (std::invalid_argument & e) {
throw ParseError("cannot parse line number '%s'", pos); throw ParseError("cannot parse line number '%s'", pos);
} }

View file

@ -1,5 +1,6 @@
#include "eval.hh" #include "eval.hh"
#include "hash.hh" #include "hash.hh"
#include "types.hh"
#include "util.hh" #include "util.hh"
#include "store-api.hh" #include "store-api.hh"
#include "derivations.hh" #include "derivations.hh"
@ -1694,7 +1695,7 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
{ {
PathSet context; PathSet context;
std::vector<std::string> s; std::vector<BackedStringView> s;
size_t sSize = 0; size_t sSize = 0;
NixInt n = 0; NixInt n = 0;
NixFloat nf = 0; NixFloat nf = 0;
@ -1705,7 +1706,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
const auto str = [&] { const auto str = [&] {
std::string result; std::string result;
result.reserve(sSize); result.reserve(sSize);
for (const auto & part : s) result += part; for (const auto & part : s) result += *part;
return result; return result;
}; };
/* c_str() is not str().c_str() because we want to create a string /* c_str() is not str().c_str() because we want to create a string
@ -1715,15 +1716,18 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
char * result = allocString(sSize + 1); char * result = allocString(sSize + 1);
char * tmp = result; char * tmp = result;
for (const auto & part : s) { for (const auto & part : s) {
memcpy(tmp, part.c_str(), part.size()); memcpy(tmp, part->data(), part->size());
tmp += part.size(); tmp += part->size();
} }
*tmp = 0; *tmp = 0;
return result; return result;
}; };
Value values[es->size()];
Value * vTmpP = values;
for (auto & [i_pos, i] : *es) { for (auto & [i_pos, i] : *es) {
Value vTmp; Value & vTmp = *vTmpP++;
i->eval(state, env, vTmp); i->eval(state, env, vTmp);
/* If the first element is a path, then the result will also /* If the first element is a path, then the result will also
@ -1756,9 +1760,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
/* skip canonization of first path, which would only be not /* skip canonization of first path, which would only be not
canonized in the first place if it's coming from a ./${foo} type canonized in the first place if it's coming from a ./${foo} type
path */ path */
s.emplace_back( auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first);
state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first)); sSize += part->size();
sSize += s.back().size(); s.emplace_back(std::move(part));
} }
first = false; first = false;
@ -1857,7 +1861,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
} }
string EvalState::forceString(Value & v, const Pos & pos) std::string_view EvalState::forceString(Value & v, const Pos & pos)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type() != nString) { if (v.type() != nString) {
@ -1866,7 +1870,7 @@ string EvalState::forceString(Value & v, const Pos & pos)
else else
throwTypeError("value is %1% while a string was expected", v); throwTypeError("value is %1% while a string was expected", v);
} }
return string(v.string.s); return v.string.s;
} }
@ -1901,17 +1905,17 @@ std::vector<std::pair<Path, std::string>> Value::getContext()
} }
string EvalState::forceString(Value & v, PathSet & context, const Pos & pos) std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos)
{ {
string s = forceString(v, pos); auto s = forceString(v, pos);
copyContext(v, context); copyContext(v, context);
return s; return s;
} }
string EvalState::forceStringNoCtx(Value & v, const Pos & pos) std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos)
{ {
string s = forceString(v, pos); auto s = forceString(v, pos);
if (v.string.context) { if (v.string.context) {
if (pos) if (pos)
throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')", throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')",
@ -1942,34 +1946,35 @@ std::optional<string> EvalState::tryAttrsToString(const Pos & pos, Value & v,
if (i != v.attrs->end()) { if (i != v.attrs->end()) {
Value v1; Value v1;
callFunction(*i->value, v, v1, pos); callFunction(*i->value, v, v1, pos);
return coerceToString(pos, v1, context, coerceMore, copyToStore); return coerceToString(pos, v1, context, coerceMore, copyToStore).toOwned();
} }
return {}; return {};
} }
string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
bool coerceMore, bool copyToStore, bool canonicalizePath) bool coerceMore, bool copyToStore, bool canonicalizePath)
{ {
forceValue(v, pos); forceValue(v, pos);
string s;
if (v.type() == nString) { if (v.type() == nString) {
copyContext(v, context); copyContext(v, context);
return v.string.s; return std::string_view(v.string.s);
} }
if (v.type() == nPath) { if (v.type() == nPath) {
Path path(canonicalizePath ? canonPath(v.path) : v.path); BackedStringView path(PathView(v.path));
return copyToStore ? copyPathToStore(context, path) : path; if (canonicalizePath)
path = canonPath(*path);
if (copyToStore)
path = copyPathToStore(context, std::move(path).toOwned());
return path;
} }
if (v.type() == nAttrs) { if (v.type() == nAttrs) {
auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore); auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore);
if (maybeString) { if (maybeString)
return *maybeString; return std::move(*maybeString);
}
auto i = v.attrs->find(sOutPath); auto i = v.attrs->find(sOutPath);
if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string"); if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string");
return coerceToString(pos, *i->value, context, coerceMore, copyToStore); return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
@ -1991,14 +1996,13 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
if (v.isList()) { if (v.isList()) {
string result; string result;
for (auto [n, v2] : enumerate(v.listItems())) { for (auto [n, v2] : enumerate(v.listItems())) {
result += coerceToString(pos, *v2, result += *coerceToString(pos, *v2, context, coerceMore, copyToStore);
context, coerceMore, copyToStore);
if (n < v.listSize() - 1 if (n < v.listSize() - 1
/* !!! not quite correct */ /* !!! not quite correct */
&& (!v2->isList() || v2->listSize() != 0)) && (!v2->isList() || v2->listSize() != 0))
result += " "; result += " ";
} }
return result; return std::move(result);
} }
} }
@ -2032,7 +2036,7 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context) Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
{ {
string path = coerceToString(pos, v, context, false, false); string path = coerceToString(pos, v, context, false, false).toOwned();
if (path == "" || path[0] != '/') if (path == "" || path[0] != '/')
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path); throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
return path; return path;

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "attr-set.hh" #include "attr-set.hh"
#include "types.hh"
#include "value.hh" #include "value.hh"
#include "nixexpr.hh" #include "nixexpr.hh"
#include "symbol-table.hh" #include "symbol-table.hh"
@ -201,8 +202,8 @@ public:
void resetFileCache(); void resetFileCache();
/* Look up a file in the search path. */ /* Look up a file in the search path. */
Path findFile(const string & path); Path findFile(const std::string_view path);
Path findFile(SearchPath & searchPath, const string & path, const Pos & pos = noPos); Path findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos = noPos);
/* If the specified search path element is a URI, download it. */ /* If the specified search path element is a URI, download it. */
std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem); std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem);
@ -236,9 +237,9 @@ public:
inline void forceList(Value & v); inline void forceList(Value & v);
inline void forceList(Value & v, const Pos & pos); inline void forceList(Value & v, const Pos & pos);
void forceFunction(Value & v, const Pos & pos); // either lambda or primop void forceFunction(Value & v, const Pos & pos); // either lambda or primop
string forceString(Value & v, const Pos & pos = noPos); std::string_view forceString(Value & v, const Pos & pos = noPos);
string forceString(Value & v, PathSet & context, const Pos & pos = noPos); std::string_view forceString(Value & v, PathSet & context, const Pos & pos = noPos);
string forceStringNoCtx(Value & v, const Pos & pos = noPos); std::string_view forceStringNoCtx(Value & v, const Pos & pos = noPos);
/* Return true iff the value `v' denotes a derivation (i.e. a /* Return true iff the value `v' denotes a derivation (i.e. a
set with attribute `type = "derivation"'). */ set with attribute `type = "derivation"'). */
@ -251,7 +252,7 @@ public:
string. If `coerceMore' is set, also converts nulls, integers, string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. If `copyToStore' is set, booleans and lists to a string. If `copyToStore' is set,
referenced paths are copied to the Nix store as a side effect. */ referenced paths are copied to the Nix store as a side effect. */
string coerceToString(const Pos & pos, Value & v, PathSet & context, BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context,
bool coerceMore = false, bool copyToStore = true, bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true); bool canonicalizePath = true);
@ -309,8 +310,8 @@ private:
friend struct ExprAttrs; friend struct ExprAttrs;
friend struct ExprLet; friend struct ExprLet;
Expr * parse(char * text, size_t length, FileOrigin origin, const Path & path, Expr * parse(char * text, size_t length, FileOrigin origin, const PathView path,
const Path & basePath, StaticEnv & staticEnv); const PathView basePath, StaticEnv & staticEnv);
public: public:

View file

@ -250,10 +250,12 @@ static Flake getFlake(
for (auto & setting : *nixConfig->value->attrs) { for (auto & setting : *nixConfig->value->attrs) {
forceTrivialValue(state, *setting.value, *setting.pos); forceTrivialValue(state, *setting.value, *setting.pos);
if (setting.value->type() == nString) if (setting.value->type() == nString)
flake.config.settings.insert({setting.name, state.forceStringNoCtx(*setting.value, *setting.pos)}); flake.config.settings.insert({setting.name, string(state.forceStringNoCtx(*setting.value, *setting.pos))});
else if (setting.value->type() == nPath) { else if (setting.value->type() == nPath) {
PathSet emptyContext = {}; PathSet emptyContext = {};
flake.config.settings.insert({setting.name, state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true)}); flake.config.settings.emplace(
setting.name,
state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true) .toOwned());
} }
else if (setting.value->type() == nInt) else if (setting.value->type() == nInt)
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)}); flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
@ -265,7 +267,7 @@ static Flake getFlake(
if (elem->type() != nString) if (elem->type() != nString)
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected", throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
setting.name, showType(*setting.value)); setting.name, showType(*setting.value));
ss.push_back(state.forceStringNoCtx(*elem, *setting.pos)); ss.emplace_back(state.forceStringNoCtx(*elem, *setting.pos));
} }
flake.config.settings.insert({setting.name, ss}); flake.config.settings.insert({setting.name, ss});
} }
@ -726,7 +728,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va
{ {
state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos); state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos);
auto flakeRefS = state.forceStringNoCtx(*args[0], pos); string flakeRefS(state.forceStringNoCtx(*args[0], pos));
auto flakeRef = parseFlakeRef(flakeRefS, {}, true); auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
if (evalSettings.pureEval && !flakeRef.input.isImmutable()) if (evalSettings.pureEval && !flakeRef.input.isImmutable())
throw Error("cannot call 'getFlake' on mutable flake reference '%s', at %s (use --impure to override)", flakeRefS, pos); throw Error("cannot call 'getFlake' on mutable flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);

View file

@ -104,7 +104,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
/* For each output... */ /* For each output... */
for (auto elem : i->value->listItems()) { for (auto elem : i->value->listItems()) {
/* Evaluate the corresponding set. */ /* Evaluate the corresponding set. */
string name = state->forceStringNoCtx(*elem, *i->pos); string name(state->forceStringNoCtx(*elem, *i->pos));
Bindings::iterator out = attrs->find(state->symbols.create(name)); Bindings::iterator out = attrs->find(state->symbols.create(name));
if (out == attrs->end()) continue; // FIXME: throw error? if (out == attrs->end()) continue; // FIXME: throw error?
state->forceAttrs(*out->value); state->forceAttrs(*out->value);

View file

@ -163,7 +163,7 @@ public:
} }
}; };
void parseJSON(EvalState & state, const string & s_, Value & v) void parseJSON(EvalState & state, const std::string_view & s_, Value & v)
{ {
JSONSax parser(state, v); JSONSax parser(state, v);
bool res = json::sax_parse(s_, &parser); bool res = json::sax_parse(s_, &parser);

View file

@ -8,6 +8,6 @@ namespace nix {
MakeError(JSONParseError, EvalError); MakeError(JSONParseError, EvalError);
void parseJSON(EvalState & state, const string & s, Value & v); void parseJSON(EvalState & state, const std::string_view & s, Value & v);
} }

View file

@ -598,7 +598,7 @@ namespace nix {
Expr * EvalState::parse(char * text, size_t length, FileOrigin origin, Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
const Path & path, const Path & basePath, StaticEnv & staticEnv) const PathView path, const PathView basePath, StaticEnv & staticEnv)
{ {
yyscan_t scanner; yyscan_t scanner;
ParseData data(*this); ParseData data(*this);
@ -709,24 +709,24 @@ void EvalState::addToSearchPath(const string & s)
} }
Path EvalState::findFile(const string & path) Path EvalState::findFile(const std::string_view path)
{ {
return findFile(searchPath, path); return findFile(searchPath, path);
} }
Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos & pos) Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos)
{ {
for (auto & i : searchPath) { for (auto & i : searchPath) {
std::string suffix; std::string suffix;
if (i.first.empty()) if (i.first.empty())
suffix = "/" + path; suffix = concatStrings("/", path);
else { else {
auto s = i.first.size(); auto s = i.first.size();
if (path.compare(0, s, i.first) != 0 || if (path.compare(0, s, i.first) != 0 ||
(path.size() > s && path[s] != '/')) (path.size() > s && path[s] != '/'))
continue; continue;
suffix = path.size() == s ? "" : "/" + string(path, s); suffix = path.size() == s ? "" : concatStrings("/", path.substr(s));
} }
auto r = resolveSearchPathElem(i); auto r = resolveSearchPathElem(i);
if (!r.first) continue; if (!r.first) continue;
@ -735,7 +735,7 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos
} }
if (hasPrefix(path, "nix/")) if (hasPrefix(path, "nix/"))
return corepkgsPrefix + path.substr(4); return concatStrings(corepkgsPrefix, path.substr(4));
throw ThrownError({ throw ThrownError({
.msg = hintfmt(evalSettings.pureEval .msg = hintfmt(evalSettings.pureEval

View file

@ -314,7 +314,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
{ {
auto path = realisePath(state, pos, *args[0]); auto path = realisePath(state, pos, *args[0]);
string sym = state.forceStringNoCtx(*args[1], pos); string sym(state.forceStringNoCtx(*args[1], pos));
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle) if (!handle)
@ -350,10 +350,11 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
}); });
} }
PathSet context; PathSet context;
auto program = state.coerceToString(pos, *elems[0], context, false, false); auto program = state.coerceToString(pos, *elems[0], context, false, false).toOwned();
Strings commandArgs; Strings commandArgs;
for (unsigned int i = 1; i < args[0]->listSize(); ++i) for (unsigned int i = 1; i < args[0]->listSize(); ++i) {
commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false)); commandArgs.push_back(state.coerceToString(pos, *elems[i], context, false, false).toOwned());
}
try { try {
auto _ = state.realiseContext(context); // FIXME: Handle CA derivations auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
} catch (InvalidPathError & e) { } catch (InvalidPathError & e) {
@ -706,7 +707,7 @@ static RegisterPrimOp primop_abort({
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v) .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
string s = state.coerceToString(pos, *args[0], context); string s = state.coerceToString(pos, *args[0], context).toOwned();
throw Abort("evaluation aborted with the following error message: '%1%'", s); throw Abort("evaluation aborted with the following error message: '%1%'", s);
} }
}); });
@ -724,7 +725,7 @@ static RegisterPrimOp primop_throw({
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v) .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
string s = state.coerceToString(pos, *args[0], context); string s = state.coerceToString(pos, *args[0], context).toOwned();
throw ThrownError(s); throw ThrownError(s);
} }
}); });
@ -736,7 +737,7 @@ static void prim_addErrorContext(EvalState & state, const Pos & pos, Value * * a
v = *args[1]; v = *args[1];
} catch (Error & e) { } catch (Error & e) {
PathSet context; PathSet context;
e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context)); e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context).toOwned());
throw; throw;
} }
} }
@ -825,7 +826,7 @@ static RegisterPrimOp primop_tryEval({
/* Return an environment variable. Use with care. */ /* Return an environment variable. Use with care. */
static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string name = state.forceStringNoCtx(*args[0], pos); string name(state.forceStringNoCtx(*args[0], pos));
v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or("")); v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or(""));
} }
@ -975,7 +976,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
const string & key = i->name; const string & key = i->name;
vomit("processing attribute '%1%'", key); vomit("processing attribute '%1%'", key);
auto handleHashMode = [&](const std::string & s) { auto handleHashMode = [&](const std::string_view s) {
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive; if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat; else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
else else
@ -1030,7 +1031,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
else if (i->name == state.sArgs) { else if (i->name == state.sArgs) {
state.forceList(*i->value, pos); state.forceList(*i->value, pos);
for (auto elem : i->value->listItems()) { for (auto elem : i->value->listItems()) {
string s = state.coerceToString(posDrvName, *elem, context, true); string s = state.coerceToString(posDrvName, *elem, context, true).toOwned();
drv.args.push_back(s); drv.args.push_back(s);
} }
} }
@ -1066,7 +1067,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
} }
} else { } else {
auto s = state.coerceToString(*i->pos, *i->value, context, true); auto s = state.coerceToString(*i->pos, *i->value, context, true).toOwned();
drv.env.emplace(key, s); drv.env.emplace(key, s);
if (i->name == state.sBuilder) drv.builder = std::move(s); if (i->name == state.sBuilder) drv.builder = std::move(s);
else if (i->name == state.sSystem) drv.platform = std::move(s); else if (i->name == state.sSystem) drv.platform = std::move(s);
@ -1399,7 +1400,7 @@ static RegisterPrimOp primop_pathExists({
static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
v.mkString(baseNameOf(state.coerceToString(pos, *args[0], context, false, false)), context); v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false)), context);
} }
static RegisterPrimOp primop_baseNameOf({ static RegisterPrimOp primop_baseNameOf({
@ -1419,7 +1420,8 @@ static RegisterPrimOp primop_baseNameOf({
static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
Path dir = dirOf(state.coerceToString(pos, *args[0], context, false, false)); auto path = state.coerceToString(pos, *args[0], context, false, false);
auto dir = dirOf(*path);
if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context); if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context);
} }
@ -1486,7 +1488,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
); );
PathSet context; PathSet context;
string path = state.coerceToString(pos, *i->value, context, false, false); string path = state.coerceToString(pos, *i->value, context, false, false).toOwned();
try { try {
auto rewrites = state.realiseContext(context); auto rewrites = state.realiseContext(context);
@ -1502,7 +1504,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
searchPath.emplace_back(prefix, path); searchPath.emplace_back(prefix, path);
} }
string path = state.forceStringNoCtx(*args[1], pos); auto path = state.forceStringNoCtx(*args[1], pos);
v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos))); v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos)));
} }
@ -1516,7 +1518,7 @@ static RegisterPrimOp primop_findFile(RegisterPrimOp::Info {
/* Return the cryptographic hash of a file in base-16. */ /* Return the cryptographic hash of a file in base-16. */
static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string type = state.forceStringNoCtx(*args[0], pos); auto type = state.forceStringNoCtx(*args[0], pos);
std::optional<HashType> ht = parseHashType(type); std::optional<HashType> ht = parseHashType(type);
if (!ht) if (!ht)
throw Error({ throw Error({
@ -1723,7 +1725,7 @@ static RegisterPrimOp primop_toJSON({
/* Parse a JSON string to a value. */ /* Parse a JSON string to a value. */
static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string s = state.forceStringNoCtx(*args[0], pos); auto s = state.forceStringNoCtx(*args[0], pos);
try { try {
parseJSON(state, s, v); parseJSON(state, s, v);
} catch (JSONParseError &e) { } catch (JSONParseError &e) {
@ -1752,8 +1754,8 @@ static RegisterPrimOp primop_fromJSON({
static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
string name = state.forceStringNoCtx(*args[0], pos); string name(state.forceStringNoCtx(*args[0], pos));
string contents = state.forceString(*args[1], context, pos); string contents(state.forceString(*args[1], context, pos));
StorePathSet refs; StorePathSet refs;
@ -2153,7 +2155,7 @@ static RegisterPrimOp primop_attrValues({
/* Dynamic version of the `.' operator. */ /* Dynamic version of the `.' operator. */
void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string attr = state.forceStringNoCtx(*args[0], pos); auto attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos);
Bindings::iterator i = getAttr( Bindings::iterator i = getAttr(
state, state,
@ -2183,7 +2185,7 @@ static RegisterPrimOp primop_getAttr({
/* Return position information of the specified attribute. */ /* Return position information of the specified attribute. */
static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string attr = state.forceStringNoCtx(*args[0], pos); auto attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos);
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr)); Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
if (i == args[1]->attrs->end()) if (i == args[1]->attrs->end())
@ -2201,7 +2203,7 @@ static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info {
/* Dynamic version of the `?' operator. */ /* Dynamic version of the `?' operator. */
static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string attr = state.forceStringNoCtx(*args[0], pos); auto attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos);
v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end()); v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end());
} }
@ -2300,7 +2302,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
pos pos
); );
string name = state.forceStringNoCtx(*j->value, *j->pos); auto name = state.forceStringNoCtx(*j->value, *j->pos);
Symbol sym = state.symbols.create(name); Symbol sym = state.symbols.create(name);
if (seen.insert(sym).second) { if (seen.insert(sym).second) {
@ -3032,7 +3034,7 @@ static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Val
for (auto vElem : args[1]->listItems()) { for (auto vElem : args[1]->listItems()) {
Value res; Value res;
state.callFunction(*args[0], *vElem, res, pos); state.callFunction(*args[0], *vElem, res, pos);
string name = state.forceStringNoCtx(res, pos); auto name = state.forceStringNoCtx(res, pos);
Symbol sym = state.symbols.create(name); Symbol sym = state.symbols.create(name);
auto vector = attrs.try_emplace(sym, ValueVector()).first; auto vector = attrs.try_emplace(sym, ValueVector()).first;
vector->second.push_back(vElem); vector->second.push_back(vElem);
@ -3288,8 +3290,8 @@ static RegisterPrimOp primop_lessThan({
static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
string s = state.coerceToString(pos, *args[0], context, true, false); auto s = state.coerceToString(pos, *args[0], context, true, false);
v.mkString(s, context); v.mkString(*s, context);
} }
static RegisterPrimOp primop_toString({ static RegisterPrimOp primop_toString({
@ -3325,7 +3327,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
int start = state.forceInt(*args[0], pos); int start = state.forceInt(*args[0], pos);
int len = state.forceInt(*args[1], pos); int len = state.forceInt(*args[1], pos);
PathSet context; PathSet context;
string s = state.coerceToString(pos, *args[2], context); auto s = state.coerceToString(pos, *args[2], context);
if (start < 0) if (start < 0)
throw EvalError({ throw EvalError({
@ -3333,7 +3335,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
.errPos = pos .errPos = pos
}); });
v.mkString((unsigned int) start >= s.size() ? "" : string(s, start, len), context); v.mkString((unsigned int) start >= s->size() ? "" : s->substr(start, len), context);
} }
static RegisterPrimOp primop_substring({ static RegisterPrimOp primop_substring({
@ -3359,8 +3361,8 @@ static RegisterPrimOp primop_substring({
static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
string s = state.coerceToString(pos, *args[0], context); auto s = state.coerceToString(pos, *args[0], context);
v.mkInt(s.size()); v.mkInt(s->size());
} }
static RegisterPrimOp primop_stringLength({ static RegisterPrimOp primop_stringLength({
@ -3376,7 +3378,7 @@ static RegisterPrimOp primop_stringLength({
/* Return the cryptographic hash of a string in base-16. */ /* Return the cryptographic hash of a string in base-16. */
static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string type = state.forceStringNoCtx(*args[0], pos); auto type = state.forceStringNoCtx(*args[0], pos);
std::optional<HashType> ht = parseHashType(type); std::optional<HashType> ht = parseHashType(type);
if (!ht) if (!ht)
throw Error({ throw Error({
@ -3385,7 +3387,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args,
}); });
PathSet context; // discarded PathSet context; // discarded
string s = state.forceString(*args[1], context, pos); auto s = state.forceString(*args[1], context, pos);
v.mkString(hashString(*ht, s).to_string(Base16, false)); v.mkString(hashString(*ht, s).to_string(Base16, false));
} }
@ -3403,7 +3405,18 @@ static RegisterPrimOp primop_hashString({
struct RegexCache struct RegexCache
{ {
std::unordered_map<std::string, std::regex> cache; // TODO use C++20 transparent comparison when available
std::unordered_map<std::string_view, std::regex> cache;
std::list<std::string> keys;
std::regex get(std::string_view re)
{
auto it = cache.find(re);
if (it != cache.end())
return it->second;
keys.emplace_back(re);
return cache.emplace(keys.back(), std::regex(keys.back(), std::regex::extended)).first->second;
}
}; };
std::shared_ptr<RegexCache> makeRegexCache() std::shared_ptr<RegexCache> makeRegexCache()
@ -3417,15 +3430,13 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
try { try {
auto regex = state.regexCache->cache.find(re); auto regex = state.regexCache->get(re);
if (regex == state.regexCache->cache.end())
regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first;
PathSet context; PathSet context;
const std::string str = state.forceString(*args[1], context, pos); const auto str = state.forceString(*args[1], context, pos);
std::smatch match; std::cmatch match;
if (!std::regex_match(str, match, regex->second)) { if (!std::regex_match(str.begin(), str.end(), match, regex)) {
v.mkNull(); v.mkNull();
return; return;
} }
@ -3500,15 +3511,13 @@ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
try { try {
auto regex = state.regexCache->cache.find(re); auto regex = state.regexCache->get(re);
if (regex == state.regexCache->cache.end())
regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first;
PathSet context; PathSet context;
const std::string str = state.forceString(*args[1], context, pos); const auto str = state.forceString(*args[1], context, pos);
auto begin = std::sregex_iterator(str.begin(), str.end(), regex->second); auto begin = std::cregex_iterator(str.begin(), str.end(), regex);
auto end = std::sregex_iterator(); auto end = std::cregex_iterator();
// Any matches results are surrounded by non-matching results. // Any matches results are surrounded by non-matching results.
const size_t len = std::distance(begin, end); const size_t len = std::distance(begin, end);
@ -3520,9 +3529,9 @@ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
return; return;
} }
for (std::sregex_iterator i = begin; i != end; ++i) { for (auto i = begin; i != end; ++i) {
assert(idx <= 2 * len + 1 - 3); assert(idx <= 2 * len + 1 - 3);
std::smatch match = *i; auto match = *i;
// Add a string for non-matched characters. // Add a string for non-matched characters.
(v.listElems()[idx++] = state.allocValue())->mkString(match.prefix().str()); (v.listElems()[idx++] = state.allocValue())->mkString(match.prefix().str());
@ -3613,7 +3622,7 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * *
for (auto elem : args[1]->listItems()) { for (auto elem : args[1]->listItems()) {
if (first) first = false; else res += sep; if (first) first = false; else res += sep;
res += state.coerceToString(pos, *elem, context); res += *state.coerceToString(pos, *elem, context);
} }
v.mkString(res, context); v.mkString(res, context);
@ -3643,14 +3652,14 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
vector<string> from; vector<string> from;
from.reserve(args[0]->listSize()); from.reserve(args[0]->listSize());
for (auto elem : args[0]->listItems()) for (auto elem : args[0]->listItems())
from.push_back(state.forceString(*elem, pos)); from.emplace_back(state.forceString(*elem, pos));
vector<std::pair<string, PathSet>> to; vector<std::pair<string, PathSet>> to;
to.reserve(args[1]->listSize()); to.reserve(args[1]->listSize());
for (auto elem : args[1]->listItems()) { for (auto elem : args[1]->listItems()) {
PathSet ctx; PathSet ctx;
auto s = state.forceString(*elem, ctx, pos); auto s = state.forceString(*elem, ctx, pos);
to.push_back(std::make_pair(std::move(s), std::move(ctx))); to.emplace_back(s, std::move(ctx));
} }
PathSet context; PathSet context;
@ -3712,7 +3721,7 @@ static RegisterPrimOp primop_replaceStrings({
static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string name = state.forceStringNoCtx(*args[0], pos); auto name = state.forceStringNoCtx(*args[0], pos);
DrvName parsed(name); DrvName parsed(name);
auto attrs = state.buildBindings(2); auto attrs = state.buildBindings(2);
attrs.alloc(state.sName).mkString(parsed.name); attrs.alloc(state.sName).mkString(parsed.name);
@ -3736,8 +3745,8 @@ static RegisterPrimOp primop_parseDrvName({
static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string version1 = state.forceStringNoCtx(*args[0], pos); auto version1 = state.forceStringNoCtx(*args[0], pos);
string version2 = state.forceStringNoCtx(*args[1], pos); auto version2 = state.forceStringNoCtx(*args[1], pos);
v.mkInt(compareVersions(version1, version2)); v.mkInt(compareVersions(version1, version2));
} }
@ -3756,14 +3765,14 @@ static RegisterPrimOp primop_compareVersions({
static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string version = state.forceStringNoCtx(*args[0], pos); auto version = state.forceStringNoCtx(*args[0], pos);
auto iter = version.cbegin(); auto iter = version.cbegin();
Strings components; Strings components;
while (iter != version.cend()) { while (iter != version.cend()) {
auto component = nextComponent(iter, version.cend()); auto component = nextComponent(iter, version.cend());
if (component.empty()) if (component.empty())
break; break;
components.emplace_back(std::move(component)); components.emplace_back(component);
} }
state.mkList(v, components.size()); state.mkList(v, components.size());
for (const auto & [n, component] : enumerate(components)) for (const auto & [n, component] : enumerate(components))

View file

@ -7,7 +7,8 @@ namespace nix {
static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
v.mkString(state.coerceToString(pos, *args[0], context)); auto s = state.coerceToString(pos, *args[0], context);
v.mkString(*s);
} }
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext); static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
@ -32,13 +33,13 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
string s = state.coerceToString(pos, *args[0], context); auto s = state.coerceToString(pos, *args[0], context);
PathSet context2; PathSet context2;
for (auto & p : context) for (auto & p : context)
context2.insert(p.at(0) == '=' ? string(p, 1) : p); context2.insert(p.at(0) == '=' ? string(p, 1) : p);
v.mkString(s, context2); v.mkString(*s, context2);
} }
static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency); static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
@ -180,7 +181,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
} }
for (auto elem : iter->value->listItems()) { for (auto elem : iter->value->listItems()) {
auto name = state.forceStringNoCtx(*elem, *iter->pos); auto name = state.forceStringNoCtx(*elem, *iter->pos);
context.insert("!" + name + "!" + string(i.name)); context.insert(concatStrings("!", name, "!", i.name));
} }
} }
} }

View file

@ -12,7 +12,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
std::string url; std::string url;
std::optional<Hash> rev; std::optional<Hash> rev;
std::optional<std::string> ref; std::optional<std::string> ref;
std::string name = "source"; std::string_view name = "source";
PathSet context; PathSet context;
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
@ -22,14 +22,14 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
string n(attr.name); std::string_view n(attr.name);
if (n == "url") if (n == "url")
url = state.coerceToString(*attr.pos, *attr.value, context, false, false); url = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
else if (n == "rev") { else if (n == "rev") {
// Ugly: unlike fetchGit, here the "rev" attribute can // Ugly: unlike fetchGit, here the "rev" attribute can
// be both a revision or a branch/tag name. // be both a revision or a branch/tag name.
auto value = state.forceStringNoCtx(*attr.value, *attr.pos); auto value = state.forceStringNoCtx(*attr.value, *attr.pos);
if (std::regex_match(value, revRegex)) if (std::regex_match(value.begin(), value.end(), revRegex))
rev = Hash::parseAny(value, htSHA1); rev = Hash::parseAny(value, htSHA1);
else else
ref = value; ref = value;
@ -50,7 +50,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
}); });
} else } else
url = state.coerceToString(pos, *args[0], context, false, false); url = state.coerceToString(pos, *args[0], context, false, false).toOwned();
// FIXME: git externals probably can be used to bypass the URI // FIXME: git externals probably can be used to bypass the URI
// whitelist. Ah well. // whitelist. Ah well.
@ -62,7 +62,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
fetchers::Attrs attrs; fetchers::Attrs attrs;
attrs.insert_or_assign("type", "hg"); attrs.insert_or_assign("type", "hg");
attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url); attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url);
attrs.insert_or_assign("name", name); attrs.insert_or_assign("name", string(name));
if (ref) attrs.insert_or_assign("ref", *ref); if (ref) attrs.insert_or_assign("ref", *ref);
if (rev) attrs.insert_or_assign("rev", rev->gitRev()); if (rev) attrs.insert_or_assign("rev", rev->gitRev());
auto input = fetchers::Input::fromAttrs(std::move(attrs)); auto input = fetchers::Input::fromAttrs(std::move(attrs));

View file

@ -125,7 +125,7 @@ static void fetchTree(
if (attr.name == state.sType) continue; if (attr.name == state.sType) continue;
state.forceValue(*attr.value, *attr.pos); state.forceValue(*attr.value, *attr.pos);
if (attr.value->type() == nPath || attr.value->type() == nString) { if (attr.value->type() == nPath || attr.value->type() == nString) {
auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false); auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
attrs.emplace(attr.name, attrs.emplace(attr.name,
attr.name == "url" attr.name == "url"
? type == "git" ? type == "git"
@ -151,7 +151,7 @@ static void fetchTree(
input = fetchers::Input::fromAttrs(std::move(attrs)); input = fetchers::Input::fromAttrs(std::move(attrs));
} else { } else {
auto url = state.coerceToString(pos, *args[0], context, false, false); auto url = state.coerceToString(pos, *args[0], context, false, false).toOwned();
if (type == "git") { if (type == "git") {
fetchers::Attrs attrs; fetchers::Attrs attrs;

View file

@ -9,7 +9,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
{ {
auto toml = state.forceStringNoCtx(*args[0], pos); auto toml = state.forceStringNoCtx(*args[0], pos);
std::istringstream tomlStream(toml); std::istringstream tomlStream(string{toml});
std::function<void(Value &, toml::value)> visit; std::function<void(Value &, toml::value)> visit;

View file

@ -699,10 +699,10 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
} }
std::string hashPlaceholder(const std::string & outputName) std::string hashPlaceholder(const std::string_view outputName)
{ {
// FIXME: memoize? // FIXME: memoize?
return "/" + hashString(htSHA256, "nix-output:" + outputName).to_string(Base32, false); return "/" + hashString(htSHA256, concatStrings("nix-output:", outputName)).to_string(Base32, false);
} }
std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName) std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName)

View file

@ -236,7 +236,7 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
It is used as a placeholder to allow derivations to refer to their It is used as a placeholder to allow derivations to refer to their
own outputs without needing to use the hash of a derivation in own outputs without needing to use the hash of a derivation in
itself, making the hash near-impossible to calculate. */ itself, making the hash near-impossible to calculate. */
std::string hashPlaceholder(const std::string & outputName); std::string hashPlaceholder(const std::string_view outputName);
/* This creates an opaque and almost certainly unique string /* This creates an opaque and almost certainly unique string
deterministically from a derivation path and output name. deterministically from a derivation path and output name.

View file

@ -56,8 +56,8 @@ bool DrvName::matches(const DrvName & n)
} }
string nextComponent(string::const_iterator & p, std::string_view nextComponent(std::string_view::const_iterator & p,
const string::const_iterator end) const std::string_view::const_iterator end)
{ {
/* Skip any dots and dashes (component separators). */ /* Skip any dots and dashes (component separators). */
while (p != end && (*p == '.' || *p == '-')) ++p; while (p != end && (*p == '.' || *p == '-')) ++p;
@ -67,18 +67,18 @@ string nextComponent(string::const_iterator & p,
/* If the first character is a digit, consume the longest sequence /* If the first character is a digit, consume the longest sequence
of digits. Otherwise, consume the longest sequence of of digits. Otherwise, consume the longest sequence of
non-digit, non-separator characters. */ non-digit, non-separator characters. */
string s; auto s = p;
if (isdigit(*p)) if (isdigit(*p))
while (p != end && isdigit(*p)) s += *p++; while (p != end && isdigit(*p)) p++;
else else
while (p != end && (!isdigit(*p) && *p != '.' && *p != '-')) while (p != end && (!isdigit(*p) && *p != '.' && *p != '-'))
s += *p++; p++;
return s; return {s, size_t(p - s)};
} }
static bool componentsLT(const string & c1, const string & c2) static bool componentsLT(const std::string_view c1, const std::string_view c2)
{ {
auto n1 = string2Int<int>(c1); auto n1 = string2Int<int>(c1);
auto n2 = string2Int<int>(c2); auto n2 = string2Int<int>(c2);
@ -94,14 +94,14 @@ static bool componentsLT(const string & c1, const string & c2)
} }
int compareVersions(const string & v1, const string & v2) int compareVersions(const std::string_view v1, const std::string_view v2)
{ {
string::const_iterator p1 = v1.begin(); auto p1 = v1.begin();
string::const_iterator p2 = v2.begin(); auto p2 = v2.begin();
while (p1 != v1.end() || p2 != v2.end()) { while (p1 != v1.end() || p2 != v2.end()) {
string c1 = nextComponent(p1, v1.end()); auto c1 = nextComponent(p1, v1.end());
string c2 = nextComponent(p2, v2.end()); auto c2 = nextComponent(p2, v2.end());
if (componentsLT(c1, c2)) return -1; if (componentsLT(c1, c2)) return -1;
else if (componentsLT(c2, c1)) return 1; else if (componentsLT(c2, c1)) return 1;
} }

View file

@ -27,9 +27,9 @@ private:
typedef list<DrvName> DrvNames; typedef list<DrvName> DrvNames;
string nextComponent(string::const_iterator & p, std::string_view nextComponent(std::string_view::const_iterator & p,
const string::const_iterator end); const std::string_view::const_iterator end);
int compareVersions(const string & v1, const string & v2); int compareVersions(const std::string_view v1, const std::string_view v2);
DrvNames drvNamesFromArgs(const Strings & opArgs); DrvNames drvNamesFromArgs(const Strings & opArgs);
} }

View file

@ -26,7 +26,7 @@ static void makeWritable(const Path & path)
struct MakeReadOnly struct MakeReadOnly
{ {
Path path; Path path;
MakeReadOnly(const Path & path) : path(path) { } MakeReadOnly(const PathView path) : path(path) { }
~MakeReadOnly() ~MakeReadOnly()
{ {
try { try {
@ -205,12 +205,13 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
/* Make the containing directory writable, but only if it's not /* Make the containing directory writable, but only if it's not
the store itself (we don't want or need to mess with its the store itself (we don't want or need to mess with its
permissions). */ permissions). */
bool mustToggle = dirOf(path) != realStoreDir.get(); const Path dirOfPath(dirOf(path));
if (mustToggle) makeWritable(dirOf(path)); bool mustToggle = dirOfPath != realStoreDir.get();
if (mustToggle) makeWritable(dirOfPath);
/* When we're done, make the directory read-only again and reset /* When we're done, make the directory read-only again and reset
its timestamp back to 0. */ its timestamp back to 0. */
MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : ""); MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : "");
Path tempLink = (format("%1%/.tmp-link-%2%-%3%") Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
% realStoreDir % getpid() % random()).str(); % realStoreDir % getpid() % random()).str();

View file

@ -170,7 +170,7 @@ std::string writeStructuredAttrsShell(const nlohmann::json & json)
auto handleSimpleType = [](const nlohmann::json & value) -> std::optional<std::string> { auto handleSimpleType = [](const nlohmann::json & value) -> std::optional<std::string> {
if (value.is_string()) if (value.is_string())
return shellEscape(value); return shellEscape(value.get<std::string_view>());
if (value.is_number()) { if (value.is_number()) {
auto f = value.get<float>(); auto f = value.get<float>();

View file

@ -259,7 +259,7 @@ Hash::Hash(std::string_view rest, HashType type, bool isSRI)
throw BadHash("hash '%s' has wrong length for hash type '%s'", rest, printHashType(this->type)); throw BadHash("hash '%s' has wrong length for hash type '%s'", rest, printHashType(this->type));
} }
Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht) Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashType> ht)
{ {
if (hashStr.empty()) { if (hashStr.empty()) {
if (!ht) if (!ht)

View file

@ -107,7 +107,7 @@ public:
}; };
/* Helper that defaults empty hashes to the 0 hash. */ /* Helper that defaults empty hashes to the 0 hash. */
Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht); Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashType> ht);
/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */ /* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
string printHash16or32(const Hash & hash); string printHash16or32(const Hash & hash);

View file

@ -6,6 +6,7 @@
#include <set> #include <set>
#include <string> #include <string>
#include <map> #include <map>
#include <variant>
#include <vector> #include <vector>
namespace nix { namespace nix {
@ -47,4 +48,63 @@ struct Explicit {
} }
}; };
/* This wants to be a little bit like rust's Cow type.
Some parts of the evaluator benefit greatly from being able to reuse
existing allocations for strings, but have to be able to also use
newly allocated storage for values.
We do not define implicit conversions, even with ref qualifiers,
since those can easily become ambiguous to the reader and can degrade
into copying behaviour we want to avoid. */
class BackedStringView {
private:
std::variant<std::string, std::string_view> data;
/* Needed to introduce a temporary since operator-> must return
a pointer. Without this we'd need to store the view object
even when we already own a string. */
class Ptr {
private:
std::string_view view;
public:
Ptr(std::string_view view): view(view) {}
const std::string_view * operator->() const { return &view; }
};
public:
BackedStringView(std::string && s): data(std::move(s)) {}
BackedStringView(std::string_view sv): data(sv) {}
template<size_t N>
BackedStringView(const char (& lit)[N]): data(std::string_view(lit)) {}
BackedStringView(const BackedStringView &) = delete;
BackedStringView & operator=(const BackedStringView &) = delete;
/* We only want move operations defined since the sole purpose of
this type is to avoid copies. */
BackedStringView(BackedStringView && other) = default;
BackedStringView & operator=(BackedStringView && other) = default;
bool isOwned() const
{
return std::holds_alternative<std::string>(data);
}
std::string toOwned() &&
{
return isOwned()
? std::move(std::get<std::string>(data))
: std::string(std::get<std::string_view>(data));
}
std::string_view operator*() const
{
return isOwned()
? std::get<std::string>(data)
: std::get<std::string_view>(data);
}
Ptr operator->() const { return Ptr(**this); }
};
} }

View file

@ -81,7 +81,7 @@ void replaceEnv(std::map<std::string, std::string> newEnv)
} }
Path absPath(Path path, std::optional<Path> dir, bool resolveSymlinks) Path absPath(Path path, std::optional<PathView> dir, bool resolveSymlinks)
{ {
if (path[0] != '/') { if (path[0] != '/') {
if (!dir) { if (!dir) {
@ -95,12 +95,12 @@ Path absPath(Path path, std::optional<Path> dir, bool resolveSymlinks)
if (!getcwd(buf, sizeof(buf))) if (!getcwd(buf, sizeof(buf)))
#endif #endif
throw SysError("cannot get cwd"); throw SysError("cannot get cwd");
dir = buf; path = concatStrings(buf, "/", path);
#ifdef __GNU__ #ifdef __GNU__
free(buf); free(buf);
#endif #endif
} } else
path = *dir + "/" + path; path = concatStrings(*dir, "/", path);
} }
return canonPath(path, resolveSymlinks); return canonPath(path, resolveSymlinks);
} }
@ -172,7 +172,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
} }
Path dirOf(const Path & path) Path dirOf(const PathView path)
{ {
Path::size_type pos = path.rfind('/'); Path::size_type pos = path.rfind('/');
if (pos == string::npos) if (pos == string::npos)
@ -1344,9 +1344,11 @@ std::string toLower(const std::string & s)
} }
std::string shellEscape(const std::string & s) std::string shellEscape(const std::string_view s)
{ {
std::string r = "'"; std::string r;
r.reserve(s.size() + 2);
r += "'";
for (auto & i : s) for (auto & i : s)
if (i == '\'') r += "'\\''"; else r += i; if (i == '\'') r += "'\\''"; else r += i;
r += '\''; r += '\'';
@ -1751,7 +1753,7 @@ void bind(int fd, const std::string & path)
if (path.size() + 1 >= sizeof(addr.sun_path)) { if (path.size() + 1 >= sizeof(addr.sun_path)) {
Pid pid = startProcess([&]() { Pid pid = startProcess([&]() {
auto dir = dirOf(path); Path dir = dirOf(path);
if (chdir(dir.c_str()) == -1) if (chdir(dir.c_str()) == -1)
throw SysError("chdir to '%s' failed", dir); throw SysError("chdir to '%s' failed", dir);
std::string base(baseNameOf(path)); std::string base(baseNameOf(path));
@ -1780,7 +1782,7 @@ void connect(int fd, const std::string & path)
if (path.size() + 1 >= sizeof(addr.sun_path)) { if (path.size() + 1 >= sizeof(addr.sun_path)) {
Pid pid = startProcess([&]() { Pid pid = startProcess([&]() {
auto dir = dirOf(path); Path dir = dirOf(path);
if (chdir(dir.c_str()) == -1) if (chdir(dir.c_str()) == -1)
throw SysError("chdir to '%s' failed", dir); throw SysError("chdir to '%s' failed", dir);
std::string base(baseNameOf(path)); std::string base(baseNameOf(path));

View file

@ -49,7 +49,7 @@ void clearEnv();
specified directory, or the current directory otherwise. The path specified directory, or the current directory otherwise. The path
is also canonicalised. */ is also canonicalised. */
Path absPath(Path path, Path absPath(Path path,
std::optional<Path> dir = {}, std::optional<PathView> dir = {},
bool resolveSymlinks = false); bool resolveSymlinks = false);
/* Canonicalise a path by removing all `.' or `..' components and /* Canonicalise a path by removing all `.' or `..' components and
@ -62,7 +62,7 @@ Path canonPath(PathView path, bool resolveSymlinks = false);
everything before the final `/'. If the path is the root or an everything before the final `/'. If the path is the root or an
immediate child thereof (e.g., `/foo'), this means `/' immediate child thereof (e.g., `/foo'), this means `/'
is returned.*/ is returned.*/
Path dirOf(const Path & path); Path dirOf(const PathView path);
/* Return the base name of the given canonical path, i.e., everything /* Return the base name of the given canonical path, i.e., everything
following the final `/' (trailing slashes are removed). */ following the final `/' (trailing slashes are removed). */
@ -148,6 +148,9 @@ Path getDataDir();
/* Create a directory and all its parents, if necessary. Returns the /* Create a directory and all its parents, if necessary. Returns the
list of created directories, in order of creation. */ list of created directories, in order of creation. */
Paths createDirs(const Path & path); Paths createDirs(const Path & path);
inline Paths createDirs(PathView path) {
return createDirs(Path(path));
}
/* Create a symlink. */ /* Create a symlink. */
void createSymlink(const Path & target, const Path & link, void createSymlink(const Path & target, const Path & link,
@ -187,6 +190,7 @@ public:
void cancel(); void cancel();
void reset(const Path & p, bool recursive = true); void reset(const Path & p, bool recursive = true);
operator Path() const { return path; } operator Path() const { return path; }
operator PathView() const { return path; }
}; };
@ -491,7 +495,7 @@ std::string toLower(const std::string & s);
/* Escape a string as a shell word. */ /* Escape a string as a shell word. */
std::string shellEscape(const std::string & s); std::string shellEscape(const std::string_view s);
/* Exception handling in destructors: print an error message, then /* Exception handling in destructors: print an error message, then

View file

@ -472,9 +472,11 @@ struct CmdDevelop : Common, MixEnvironment
else { else {
script = "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n" + script; script = "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n" + script;
if (developSettings.bashPrompt != "") if (developSettings.bashPrompt != "")
script += fmt("[ -n \"$PS1\" ] && PS1=%s;\n", shellEscape(developSettings.bashPrompt)); script += fmt("[ -n \"$PS1\" ] && PS1=%s;\n",
shellEscape(developSettings.bashPrompt.get()));
if (developSettings.bashPromptSuffix != "") if (developSettings.bashPromptSuffix != "")
script += fmt("[ -n \"$PS1\" ] && PS1+=%s;\n", shellEscape(developSettings.bashPromptSuffix)); script += fmt("[ -n \"$PS1\" ] && PS1+=%s;\n",
shellEscape(developSettings.bashPromptSuffix.get()));
} }
writeFull(rcFileFd.get(), script); writeFull(rcFileFd.get(), script);

View file

@ -107,7 +107,7 @@ struct CmdEval : MixJSON, InstallableCommand
else if (raw) { else if (raw) {
stopProgressBar(); stopProgressBar();
std::cout << state->coerceToString(noPos, *v, context); std::cout << *state->coerceToString(noPos, *v, context);
} }
else if (json) { else if (json) {

View file

@ -38,7 +38,7 @@ string resolveMirrorUrl(EvalState & state, string url)
if (mirrorList->value->listSize() < 1) if (mirrorList->value->listSize() < 1)
throw Error("mirror URL '%s' did not expand to anything", url); throw Error("mirror URL '%s' did not expand to anything", url);
auto mirror = state.forceString(*mirrorList->value->listElems()[0]); string mirror(state.forceString(*mirrorList->value->listElems()[0]));
return mirror + (hasSuffix(mirror, "/") ? "" : "/") + string(s, p + 1); return mirror + (hasSuffix(mirror, "/") ? "" : "/") + string(s, p + 1);
} }

View file

@ -463,7 +463,7 @@ bool NixRepl::processLine(string line)
if (v.type() == nPath || v.type() == nString) { if (v.type() == nPath || v.type() == nString) {
PathSet context; PathSet context;
auto filename = state->coerceToString(noPos, v, context); auto filename = state->coerceToString(noPos, v, context);
pos.file = state->symbols.create(filename); pos.file = state->symbols.create(*filename);
} else if (v.isLambda()) { } else if (v.isLambda()) {
pos = v.lambda.fun->pos; pos = v.lambda.fun->pos;
} else { } else {

View file

@ -107,7 +107,7 @@ Path resolveSymlink(const Path & path)
auto target = readLink(path); auto target = readLink(path);
return hasPrefix(target, "/") return hasPrefix(target, "/")
? target ? target
: dirOf(path) + "/" + target; : concatStrings(dirOf(path), "/", target);
} }
std::set<string> resolveTree(const Path & path, PathSet & deps) std::set<string> resolveTree(const Path & path, PathSet & deps)