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);
unsigned int lineno;
try {
lineno = std::stoi(std::string(pos, colon + 1));
lineno = std::stoi(std::string(pos, colon + 1, string::npos));
} catch (std::invalid_argument & e) {
throw ParseError("cannot parse line number '%s'", pos);
}

View file

@ -1,5 +1,6 @@
#include "eval.hh"
#include "hash.hh"
#include "types.hh"
#include "util.hh"
#include "store-api.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)
{
PathSet context;
std::vector<std::string> s;
std::vector<BackedStringView> s;
size_t sSize = 0;
NixInt n = 0;
NixFloat nf = 0;
@ -1705,7 +1706,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
const auto str = [&] {
std::string result;
result.reserve(sSize);
for (const auto & part : s) result += part;
for (const auto & part : s) result += *part;
return result;
};
/* 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 * tmp = result;
for (const auto & part : s) {
memcpy(tmp, part.c_str(), part.size());
tmp += part.size();
memcpy(tmp, part->data(), part->size());
tmp += part->size();
}
*tmp = 0;
return result;
};
Value values[es->size()];
Value * vTmpP = values;
for (auto & [i_pos, i] : *es) {
Value vTmp;
Value & vTmp = *vTmpP++;
i->eval(state, env, vTmp);
/* 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
canonized in the first place if it's coming from a ./${foo} type
path */
s.emplace_back(
state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first));
sSize += s.back().size();
auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first);
sSize += part->size();
s.emplace_back(std::move(part));
}
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);
if (v.type() != nString) {
@ -1866,7 +1870,7 @@ string EvalState::forceString(Value & v, const Pos & pos)
else
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);
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 (pos)
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()) {
Value v1;
callFunction(*i->value, v, v1, pos);
return coerceToString(pos, v1, context, coerceMore, copyToStore);
return coerceToString(pos, v1, context, coerceMore, copyToStore).toOwned();
}
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)
{
forceValue(v, pos);
string s;
if (v.type() == nString) {
copyContext(v, context);
return v.string.s;
return std::string_view(v.string.s);
}
if (v.type() == nPath) {
Path path(canonicalizePath ? canonPath(v.path) : v.path);
return copyToStore ? copyPathToStore(context, path) : path;
BackedStringView path(PathView(v.path));
if (canonicalizePath)
path = canonPath(*path);
if (copyToStore)
path = copyPathToStore(context, std::move(path).toOwned());
return path;
}
if (v.type() == nAttrs) {
auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore);
if (maybeString) {
return *maybeString;
}
if (maybeString)
return std::move(*maybeString);
auto i = v.attrs->find(sOutPath);
if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string");
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()) {
string result;
for (auto [n, v2] : enumerate(v.listItems())) {
result += coerceToString(pos, *v2,
context, coerceMore, copyToStore);
result += *coerceToString(pos, *v2, context, coerceMore, copyToStore);
if (n < v.listSize() - 1
/* !!! not quite correct */
&& (!v2->isList() || v2->listSize() != 0))
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)
{
string path = coerceToString(pos, v, context, false, false);
string path = coerceToString(pos, v, context, false, false).toOwned();
if (path == "" || path[0] != '/')
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
return path;

View file

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

View file

@ -250,10 +250,12 @@ static Flake getFlake(
for (auto & setting : *nixConfig->value->attrs) {
forceTrivialValue(state, *setting.value, *setting.pos);
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) {
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)
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
@ -265,7 +267,7 @@ static Flake getFlake(
if (elem->type() != nString)
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
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});
}
@ -726,7 +728,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va
{
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);
if (evalSettings.pureEval && !flakeRef.input.isImmutable())
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 (auto elem : i->value->listItems()) {
/* 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));
if (out == attrs->end()) continue; // FIXME: throw error?
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);
bool res = json::sax_parse(s_, &parser);

View file

@ -8,6 +8,6 @@ namespace nix {
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,
const Path & path, const Path & basePath, StaticEnv & staticEnv)
const PathView path, const PathView basePath, StaticEnv & staticEnv)
{
yyscan_t scanner;
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);
}
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) {
std::string suffix;
if (i.first.empty())
suffix = "/" + path;
suffix = concatStrings("/", path);
else {
auto s = i.first.size();
if (path.compare(0, s, i.first) != 0 ||
(path.size() > s && path[s] != '/'))
continue;
suffix = path.size() == s ? "" : "/" + string(path, s);
suffix = path.size() == s ? "" : concatStrings("/", path.substr(s));
}
auto r = resolveSearchPathElem(i);
if (!r.first) continue;
@ -735,7 +735,7 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos
}
if (hasPrefix(path, "nix/"))
return corepkgsPrefix + path.substr(4);
return concatStrings(corepkgsPrefix, path.substr(4));
throw ThrownError({
.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]);
string sym = state.forceStringNoCtx(*args[1], pos);
string sym(state.forceStringNoCtx(*args[1], pos));
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle)
@ -350,10 +350,11 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
});
}
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;
for (unsigned int i = 1; i < args[0]->listSize(); ++i)
commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false));
for (unsigned int i = 1; i < args[0]->listSize(); ++i) {
commandArgs.push_back(state.coerceToString(pos, *elems[i], context, false, false).toOwned());
}
try {
auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
} catch (InvalidPathError & e) {
@ -706,7 +707,7 @@ static RegisterPrimOp primop_abort({
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
{
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);
}
});
@ -724,7 +725,7 @@ static RegisterPrimOp primop_throw({
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
string s = state.coerceToString(pos, *args[0], context);
string s = state.coerceToString(pos, *args[0], context).toOwned();
throw ThrownError(s);
}
});
@ -736,7 +737,7 @@ static void prim_addErrorContext(EvalState & state, const Pos & pos, Value * * a
v = *args[1];
} catch (Error & e) {
PathSet context;
e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context));
e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context).toOwned());
throw;
}
}
@ -825,7 +826,7 @@ static RegisterPrimOp primop_tryEval({
/* Return an environment variable. Use with care. */
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(""));
}
@ -975,7 +976,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
const string & key = i->name;
vomit("processing attribute '%1%'", key);
auto handleHashMode = [&](const std::string & s) {
auto handleHashMode = [&](const std::string_view s) {
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
else
@ -1030,7 +1031,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
else if (i->name == state.sArgs) {
state.forceList(*i->value, pos);
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);
}
}
@ -1066,7 +1067,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
}
} 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);
if (i->name == state.sBuilder) drv.builder = 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)
{
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({
@ -1419,7 +1420,8 @@ static RegisterPrimOp primop_baseNameOf({
static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
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);
}
@ -1486,7 +1488,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
);
PathSet context;
string path = state.coerceToString(pos, *i->value, context, false, false);
string path = state.coerceToString(pos, *i->value, context, false, false).toOwned();
try {
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);
}
string path = state.forceStringNoCtx(*args[1], pos);
auto path = state.forceStringNoCtx(*args[1], 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. */
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);
if (!ht)
throw Error({
@ -1723,7 +1725,7 @@ static RegisterPrimOp primop_toJSON({
/* Parse a JSON string to a value. */
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 {
parseJSON(state, s, v);
} catch (JSONParseError &e) {
@ -1752,8 +1754,8 @@ static RegisterPrimOp primop_fromJSON({
static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
string name = state.forceStringNoCtx(*args[0], pos);
string contents = state.forceString(*args[1], context, pos);
string name(state.forceStringNoCtx(*args[0], pos));
string contents(state.forceString(*args[1], context, pos));
StorePathSet refs;
@ -2153,7 +2155,7 @@ static RegisterPrimOp primop_attrValues({
/* Dynamic version of the `.' operator. */
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);
Bindings::iterator i = getAttr(
state,
@ -2183,7 +2185,7 @@ static RegisterPrimOp primop_getAttr({
/* Return position information of the specified attribute. */
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);
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
if (i == args[1]->attrs->end())
@ -2201,7 +2203,7 @@ static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info {
/* Dynamic version of the `?' operator. */
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);
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
);
string name = state.forceStringNoCtx(*j->value, *j->pos);
auto name = state.forceStringNoCtx(*j->value, *j->pos);
Symbol sym = state.symbols.create(name);
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()) {
Value res;
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);
auto vector = attrs.try_emplace(sym, ValueVector()).first;
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)
{
PathSet context;
string s = state.coerceToString(pos, *args[0], context, true, false);
v.mkString(s, context);
auto s = state.coerceToString(pos, *args[0], context, true, false);
v.mkString(*s, context);
}
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 len = state.forceInt(*args[1], pos);
PathSet context;
string s = state.coerceToString(pos, *args[2], context);
auto s = state.coerceToString(pos, *args[2], context);
if (start < 0)
throw EvalError({
@ -3333,7 +3335,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
.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({
@ -3359,8 +3361,8 @@ static RegisterPrimOp primop_substring({
static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
string s = state.coerceToString(pos, *args[0], context);
v.mkInt(s.size());
auto s = state.coerceToString(pos, *args[0], context);
v.mkInt(s->size());
}
static RegisterPrimOp primop_stringLength({
@ -3376,7 +3378,7 @@ static RegisterPrimOp primop_stringLength({
/* Return the cryptographic hash of a string in base-16. */
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);
if (!ht)
throw Error({
@ -3385,7 +3387,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args,
});
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));
}
@ -3403,7 +3405,18 @@ static RegisterPrimOp primop_hashString({
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()
@ -3417,15 +3430,13 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
try {
auto regex = state.regexCache->cache.find(re);
if (regex == state.regexCache->cache.end())
regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first;
auto regex = state.regexCache->get(re);
PathSet context;
const std::string str = state.forceString(*args[1], context, pos);
const auto str = state.forceString(*args[1], context, pos);
std::smatch match;
if (!std::regex_match(str, match, regex->second)) {
std::cmatch match;
if (!std::regex_match(str.begin(), str.end(), match, regex)) {
v.mkNull();
return;
}
@ -3500,15 +3511,13 @@ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
try {
auto regex = state.regexCache->cache.find(re);
if (regex == state.regexCache->cache.end())
regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first;
auto regex = state.regexCache->get(re);
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 end = std::sregex_iterator();
auto begin = std::cregex_iterator(str.begin(), str.end(), regex);
auto end = std::cregex_iterator();
// Any matches results are surrounded by non-matching results.
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;
}
for (std::sregex_iterator i = begin; i != end; ++i) {
for (auto i = begin; i != end; ++i) {
assert(idx <= 2 * len + 1 - 3);
std::smatch match = *i;
auto match = *i;
// Add a string for non-matched characters.
(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()) {
if (first) first = false; else res += sep;
res += state.coerceToString(pos, *elem, context);
res += *state.coerceToString(pos, *elem, context);
}
v.mkString(res, context);
@ -3643,14 +3652,14 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
vector<string> from;
from.reserve(args[0]->listSize());
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;
to.reserve(args[1]->listSize());
for (auto elem : args[1]->listItems()) {
PathSet ctx;
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;
@ -3712,7 +3721,7 @@ static RegisterPrimOp primop_replaceStrings({
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);
auto attrs = state.buildBindings(2);
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)
{
string version1 = state.forceStringNoCtx(*args[0], pos);
string version2 = state.forceStringNoCtx(*args[1], pos);
auto version1 = state.forceStringNoCtx(*args[0], pos);
auto version2 = state.forceStringNoCtx(*args[1], pos);
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)
{
string version = state.forceStringNoCtx(*args[0], pos);
auto version = state.forceStringNoCtx(*args[0], pos);
auto iter = version.cbegin();
Strings components;
while (iter != version.cend()) {
auto component = nextComponent(iter, version.cend());
if (component.empty())
break;
components.emplace_back(std::move(component));
components.emplace_back(component);
}
state.mkList(v, components.size());
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)
{
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);
@ -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)
{
PathSet context;
string s = state.coerceToString(pos, *args[0], context);
auto s = state.coerceToString(pos, *args[0], context);
PathSet context2;
for (auto & p : context)
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);
@ -180,7 +181,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
}
for (auto elem : iter->value->listItems()) {
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::optional<Hash> rev;
std::optional<std::string> ref;
std::string name = "source";
std::string_view name = "source";
PathSet context;
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);
for (auto & attr : *args[0]->attrs) {
string n(attr.name);
std::string_view n(attr.name);
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") {
// Ugly: unlike fetchGit, here the "rev" attribute can
// be both a revision or a branch/tag name.
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);
else
ref = value;
@ -50,7 +50,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
});
} 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
// whitelist. Ah well.
@ -62,7 +62,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
fetchers::Attrs attrs;
attrs.insert_or_assign("type", "hg");
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 (rev) attrs.insert_or_assign("rev", rev->gitRev());
auto input = fetchers::Input::fromAttrs(std::move(attrs));

View file

@ -125,7 +125,7 @@ static void fetchTree(
if (attr.name == state.sType) continue;
state.forceValue(*attr.value, *attr.pos);
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,
attr.name == "url"
? type == "git"
@ -151,7 +151,7 @@ static void fetchTree(
input = fetchers::Input::fromAttrs(std::move(attrs));
} 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") {
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);
std::istringstream tomlStream(toml);
std::istringstream tomlStream(string{toml});
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?
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)

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
own outputs without needing to use the hash of a derivation in
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
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,
const string::const_iterator end)
std::string_view nextComponent(std::string_view::const_iterator & p,
const std::string_view::const_iterator end)
{
/* Skip any dots and dashes (component separators). */
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
of digits. Otherwise, consume the longest sequence of
non-digit, non-separator characters. */
string s;
auto s = p;
if (isdigit(*p))
while (p != end && isdigit(*p)) s += *p++;
while (p != end && isdigit(*p)) p++;
else
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 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();
string::const_iterator p2 = v2.begin();
auto p1 = v1.begin();
auto p2 = v2.begin();
while (p1 != v1.end() || p2 != v2.end()) {
string c1 = nextComponent(p1, v1.end());
string c2 = nextComponent(p2, v2.end());
auto c1 = nextComponent(p1, v1.end());
auto c2 = nextComponent(p2, v2.end());
if (componentsLT(c1, c2)) return -1;
else if (componentsLT(c2, c1)) return 1;
}

View file

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

View file

@ -26,7 +26,7 @@ static void makeWritable(const Path & path)
struct MakeReadOnly
{
Path path;
MakeReadOnly(const Path & path) : path(path) { }
MakeReadOnly(const PathView path) : path(path) { }
~MakeReadOnly()
{
try {
@ -205,12 +205,13 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
/* Make the containing directory writable, but only if it's not
the store itself (we don't want or need to mess with its
permissions). */
bool mustToggle = dirOf(path) != realStoreDir.get();
if (mustToggle) makeWritable(dirOf(path));
const Path dirOfPath(dirOf(path));
bool mustToggle = dirOfPath != realStoreDir.get();
if (mustToggle) makeWritable(dirOfPath);
/* When we're done, make the directory read-only again and reset
its timestamp back to 0. */
MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : "");
Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
% 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> {
if (value.is_string())
return shellEscape(value);
return shellEscape(value.get<std::string_view>());
if (value.is_number()) {
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));
}
Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht)
Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashType> ht)
{
if (hashStr.empty()) {
if (!ht)

View file

@ -107,7 +107,7 @@ public:
};
/* 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. */
string printHash16or32(const Hash & hash);

View file

@ -6,6 +6,7 @@
#include <set>
#include <string>
#include <map>
#include <variant>
#include <vector>
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 (!dir) {
@ -95,12 +95,12 @@ Path absPath(Path path, std::optional<Path> dir, bool resolveSymlinks)
if (!getcwd(buf, sizeof(buf)))
#endif
throw SysError("cannot get cwd");
dir = buf;
path = concatStrings(buf, "/", path);
#ifdef __GNU__
free(buf);
#endif
}
path = *dir + "/" + path;
} else
path = concatStrings(*dir, "/", path);
}
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('/');
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)
if (i == '\'') r += "'\\''"; else r += i;
r += '\'';
@ -1751,7 +1753,7 @@ void bind(int fd, const std::string & path)
if (path.size() + 1 >= sizeof(addr.sun_path)) {
Pid pid = startProcess([&]() {
auto dir = dirOf(path);
Path dir = dirOf(path);
if (chdir(dir.c_str()) == -1)
throw SysError("chdir to '%s' failed", dir);
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)) {
Pid pid = startProcess([&]() {
auto dir = dirOf(path);
Path dir = dirOf(path);
if (chdir(dir.c_str()) == -1)
throw SysError("chdir to '%s' failed", dir);
std::string base(baseNameOf(path));

View file

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

View file

@ -472,9 +472,11 @@ struct CmdDevelop : Common, MixEnvironment
else {
script = "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n" + script;
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 != "")
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);

View file

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

View file

@ -38,7 +38,7 @@ string resolveMirrorUrl(EvalState & state, string url)
if (mirrorList->value->listSize() < 1)
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);
}

View file

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

View file

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