~stepbrobd/nix

30c4f5eb51c31f1beaef0f78f410404b48899664 — Robert Hensing a month ago dbcd4cd + a353a99
Merge pull request #11682 from NaN-git/opt-str

Remove superfluous `std::string` copy operations
M src/libcmd/repl.cc => src/libcmd/repl.cc +2 -2
@@ 645,7 645,7 @@ ProcessLineResult NixRepl::processLine(std::string line)

            logger->cout(trim(renderMarkdownToTerminal(markdown)));
        } else if (fallbackPos) {
            std::stringstream ss;
            std::ostringstream ss;
            ss << "Attribute `" << fallbackName << "`\n\n";
            ss << "  … defined at " << state->positions[fallbackPos] << "\n\n";
            if (fallbackDoc) {


@@ 654,7 654,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
                ss << "No documentation found.\n\n";
            }

            auto markdown = ss.str();
            auto markdown = toView(ss);
            logger->cout(trim(renderMarkdownToTerminal(markdown)));

        } else

M src/libexpr/eval.cc => src/libexpr/eval.cc +5 -12
@@ 539,7 539,7 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
    if (v.isLambda()) {
        auto exprLambda = v.payload.lambda.fun;

        std::stringstream s(std::ios_base::out);
        std::ostringstream s;
        std::string name;
        auto pos = positions[exprLambda->getPos()];
        std::string docStr;


@@ 571,17 571,12 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)

        s << docStr;

        s << '\0'; // for making a c string below
        std::string ss = s.str();

        return Doc {
            .pos = pos,
            .name = name,
            .arity = 0, // FIXME: figure out how deep by syntax only? It's not semantically useful though...
            .args = {},
            .doc =
                // FIXME: this leaks; make the field std::string?
                strdup(ss.data()),
            .doc = makeImmutableString(toView(s)), // NOTE: memory leak when compiled without GC
        };
    }
    if (isFunctor(v)) {


@@ 1805,11 1800,9 @@ void ExprIf::eval(EvalState & state, Env & env, Value & v)
void ExprAssert::eval(EvalState & state, Env & env, Value & v)
{
    if (!state.evalBool(env, cond, pos, "in the condition of the assert statement")) {
        auto exprStr = ({
            std::ostringstream out;
            cond->show(state.symbols, out);
            out.str();
        });
        std::ostringstream out;
        cond->show(state.symbols, out);
        auto exprStr = toView(out);

        if (auto eq = dynamic_cast<ExprOpEq *>(cond)) {
            try {

M src/libexpr/get-drvs.cc => src/libexpr/get-drvs.cc +5 -4
@@ 374,11 374,12 @@ static void getDerivations(EvalState & state, Value & vIn,
           bound to the attribute with the "lower" name should take
           precedence). */
        for (auto & i : v.attrs()->lexicographicOrder(state.symbols)) {
            std::string_view symbol{state.symbols[i->name]};
            try {
                debug("evaluating attribute '%1%'", state.symbols[i->name]);
                if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex))
                debug("evaluating attribute '%1%'", symbol);
                if (!std::regex_match(symbol.begin(), symbol.end(), attrRegex))
                    continue;
                std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]);
                std::string pathPrefix2 = addToPath(pathPrefix, symbol);
                if (combineChannels)
                    getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
                else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) {


@@ 392,7 393,7 @@ static void getDerivations(EvalState & state, Value & vIn,
                    }
                }
            } catch (Error & e) {
                e.addTrace(state.positions[i->pos], "while evaluating the attribute '%s'", state.symbols[i->name]);
                e.addTrace(state.positions[i->pos], "while evaluating the attribute '%s'", symbol);
                throw;
            }
        }

M src/libexpr/primops.cc => src/libexpr/primops.cc +13 -6
@@ 40,6 40,13 @@ namespace nix {
 * Miscellaneous
 *************************************************************/

static inline Value * mkString(EvalState & state, const std::csub_match & match)
{
    Value * v = state.allocValue();
    v->mkString({match.first, match.second});
    return v;
}

StringMap EvalState::realiseContext(const NixStringContext & context, StorePathSet * maybePathsOut, bool isIFD)
{
    std::vector<DerivedPath::Built> drvs;


@@ 2129,7 2136,7 @@ static void prim_toXML(EvalState & state, const PosIdx pos, Value * * args, Valu
    std::ostringstream out;
    NixStringContext context;
    printValueAsXML(state, true, false, *args[0], out, context, pos);
    v.mkString(out.str(), context);
    v.mkString(toView(out), context);
}

static RegisterPrimOp primop_toXML({


@@ 2237,7 2244,7 @@ static void prim_toJSON(EvalState & state, const PosIdx pos, Value * * args, Val
    std::ostringstream out;
    NixStringContext context;
    printValueAsJSON(state, true, *args[0], pos, out, context);
    v.mkString(out.str(), context);
    v.mkString(toView(out), context);
}

static RegisterPrimOp primop_toJSON({


@@ 4268,7 4275,7 @@ void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v)
            if (!match[i + 1].matched)
                v2 = &state.vNull;
            else
                (v2 = state.allocValue())->mkString(match[i + 1].str());
                v2 = mkString(state, match[i + 1]);
        v.mkList(list);

    } catch (std::regex_error & e) {


@@ 4352,7 4359,7 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v)
            auto match = *i;

            // Add a string for non-matched characters.
            (list[idx++] = state.allocValue())->mkString(match.prefix().str());
            list[idx++] = mkString(state, match.prefix());

            // Add a list for matched substrings.
            const size_t slen = match.size() - 1;


@@ 4363,14 4370,14 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v)
                if (!match[si + 1].matched)
                    v2 = &state.vNull;
                else
                    (v2 = state.allocValue())->mkString(match[si + 1].str());
                    v2 = mkString(state, match[si + 1]);
            }

            (list[idx++] = state.allocValue())->mkList(list2);

            // Add a string for non-matched suffix characters.
            if (idx == 2 * len)
                (list[idx++] = state.allocValue())->mkString(match.suffix().str());
                list[idx++] = mkString(state, match.suffix());
        }

        assert(idx == 2 * len + 1);

M src/libexpr/primops/fromTOML.cc => src/libexpr/primops/fromTOML.cc +1 -1
@@ 66,7 66,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
                        attrs.alloc("_type").mkString("timestamp");
                        std::ostringstream s;
                        s << t;
                        attrs.alloc("value").mkString(s.str());
                        attrs.alloc("value").mkString(toView(s));
                        v.mkAttrs(attrs);
                    } else {
                        throw std::runtime_error("Dates and times are not supported");

M src/libexpr/print.cc => src/libexpr/print.cc +1 -1
@@ 460,7 460,7 @@ private:

                std::ostringstream s;
                s << state.positions[v.payload.lambda.fun->pos];
                output << " @ " << filterANSIEscapes(s.str());
                output << " @ " << filterANSIEscapes(toView(s));
            }
        } else if (v.isPrimOp()) {
            if (v.primOp())

M src/libmain/progress-bar.cc => src/libmain/progress-bar.cc +2 -2
@@ 158,10 158,10 @@ public:
    {
        auto state(state_.lock());

        std::stringstream oss;
        std::ostringstream oss;
        showErrorInfo(oss, ei, loggerSettings.showTrace.get());

        log(*state, ei.level, oss.str());
        log(*state, ei.level, toView(oss));
    }

    void log(State & state, Verbosity lvl, std::string_view s)

M src/libstore/daemon.cc => src/libstore/daemon.cc +2 -2
@@ 90,11 90,11 @@ struct TunnelLogger : public Logger
    {
        if (ei.level > verbosity) return;

        std::stringstream oss;
        std::ostringstream oss;
        showErrorInfo(oss, ei, false);

        StringSink buf;
        buf << STDERR_NEXT << oss.str();
        buf << STDERR_NEXT << toView(oss);
        enqueueMsg(buf.s);
    }


M src/libstore/outputs-spec.cc => src/libstore/outputs-spec.cc +3 -4
@@ 30,16 30,15 @@ std::optional<OutputsSpec> OutputsSpec::parseOpt(std::string_view s)
{
    static std::regex regex(std::string { outputSpecRegexStr });

    std::smatch match;
    std::string s2 { s }; // until some improves std::regex
    if (!std::regex_match(s2, match, regex))
    std::cmatch match;
    if (!std::regex_match(s.cbegin(), s.cend(), match, regex))
        return std::nullopt;

    if (match[1].matched)
        return { OutputsSpec::All {} };

    if (match[2].matched)
        return OutputsSpec::Names { tokenizeString<StringSet>(match[2].str(), ",") };
        return OutputsSpec::Names { tokenizeString<StringSet>({match[2].first, match[2].second}, ",") };

    assert(false);
}

M src/libutil/args.cc => src/libutil/args.cc +1 -1
@@ 293,7 293,7 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang)
                    // We match one space after `nix` so that we preserve indentation.
                    // No space is necessary for an empty line. An empty line has basically no effect.
                    if (std::regex_match(line, match, std::regex("^#!\\s*nix(:? |$)(.*)$")))
                        shebangContent += match[2].str() + "\n";
                        shebangContent += std::string_view{match[2].first, match[2].second} + "\n";
                }
                for (const auto & word : parseShebangContent(shebangContent)) {
                    cmdline.push_back(word);

M src/libutil/logging.cc => src/libutil/logging.cc +2 -2
@@ 85,10 85,10 @@ public:

    void logEI(const ErrorInfo & ei) override
    {
        std::stringstream oss;
        std::ostringstream oss;
        showErrorInfo(oss, ei, loggerSettings.showTrace.get());

        log(ei.level, oss.str());
        log(ei.level, toView(oss));
    }

    void startActivity(ActivityId act, Verbosity lvl, ActivityType type,

M src/libutil/strings.cc => src/libutil/strings.cc +15 -0
@@ 6,6 6,21 @@

namespace nix {

struct view_stringbuf : public std::stringbuf
{
    inline std::string_view toView()
    {
        auto begin = pbase();
        return {begin, begin + pubseekoff(0, std::ios_base::cur, std::ios_base::out)};
    }
};

std::string_view toView(const std::ostringstream & os)
{
    auto buf = static_cast<view_stringbuf *>(os.rdbuf());
    return buf->toView();
}

template std::list<std::string> tokenizeString(std::string_view s, std::string_view separators);
template std::set<std::string> tokenizeString(std::string_view s, std::string_view separators);
template std::vector<std::string> tokenizeString(std::string_view s, std::string_view separators);

M src/libutil/strings.hh => src/libutil/strings.hh +5 -0
@@ 8,6 8,11 @@

namespace nix {

/*
 * workaround for unavailable view() method (C++20) of std::ostringstream under MacOS with clang-16
 */
std::string_view toView(const std::ostringstream & os);

/**
 * String tokenizer.
 *

M src/nix-build/nix-build.cc => src/nix-build/nix-build.cc +5 -5
@@ 36,7 36,7 @@ extern char * * environ __attribute__((weak));
/* Recreate the effect of the perl shellwords function, breaking up a
 * string into arguments like a shell word, including escapes
 */
static std::vector<std::string> shellwords(const std::string & s)
static std::vector<std::string> shellwords(std::string_view s)
{
    std::regex whitespace("^\\s+");
    auto begin = s.cbegin();


@@ 51,7 51,7 @@ static std::vector<std::string> shellwords(const std::string & s)
    auto it = begin;
    for (; it != s.cend(); ++it) {
        if (st == sBegin) {
            std::smatch match;
            std::cmatch match;
            if (regex_search(it, s.cend(), match, whitespace)) {
                cur.append(begin, it);
                res.push_back(cur);


@@ 173,7 173,7 @@ static void main_nix_build(int argc, char * * argv)
                    line = chomp(line);
                    std::smatch match;
                    if (std::regex_match(line, match, std::regex("^#!\\s*nix-shell\\s+(.*)$")))
                        for (const auto & word : shellwords(match[1].str()))
                        for (const auto & word : shellwords({match[1].first, match[1].second}))
                            args.push_back(word);
                }
            }


@@ 260,9 260,9 @@ static void main_nix_build(int argc, char * * argv)
                // read the shebang to understand which packages to read from. Since
                // this is handled via nix-shell -p, we wrap our ruby script execution
                // in ruby -e 'load' which ignores the shebangs.
                envCommand = fmt("exec %1% %2% -e 'load(ARGV.shift)' -- %3% %4%", execArgs, interpreter, shellEscape(script), joined.str());
                envCommand = fmt("exec %1% %2% -e 'load(ARGV.shift)' -- %3% %4%", execArgs, interpreter, shellEscape(script), toView(joined));
            } else {
                envCommand = fmt("exec %1% %2% %3% %4%", execArgs, interpreter, shellEscape(script), joined.str());
                envCommand = fmt("exec %1% %2% %3% %4%", execArgs, interpreter, shellEscape(script), toView(joined));
            }
        }


M src/nix-env/user-env.cc => src/nix-env/user-env.cc +1 -3
@@ 111,9 111,7 @@ bool createUserEnv(EvalState & state, PackageInfos & elems,
    auto manifestFile = ({
        std::ostringstream str;
        printAmbiguous(manifest, state.symbols, str, nullptr, std::numeric_limits<int>::max());
        // TODO with C++20 we can use str.view() instead and avoid copy.
        std::string str2 = str.str();
        StringSource source { str2 };
        StringSource source { toView(str) };
        state.store->addToStoreFromDump(
            source, "env-manifest.nix", FileSerialisationMethod::Flat, ContentAddressMethod::Raw::Text, HashAlgorithm::SHA256, references);
    });

M src/nix/config-check.cc => src/nix/config-check.cc +9 -9
@@ 26,17 26,17 @@ std::string formatProtocol(unsigned int proto)
    return "unknown";
}

bool checkPass(const std::string & msg) {
bool checkPass(std::string_view msg) {
    notice(ANSI_GREEN "[PASS] " ANSI_NORMAL + msg);
    return true;
}

bool checkFail(const std::string & msg) {
bool checkFail(std::string_view msg) {
    notice(ANSI_RED "[FAIL] " ANSI_NORMAL + msg);
    return false;
}

void checkInfo(const std::string & msg) {
void checkInfo(std::string_view msg) {
    notice(ANSI_BLUE "[INFO] " ANSI_NORMAL + msg);
}



@@ 87,11 87,11 @@ struct CmdConfigCheck : StoreCommand
        }

        if (dirs.size() != 1) {
            std::stringstream ss;
            std::ostringstream ss;
            ss << "Multiple versions of nix found in PATH:\n";
            for (auto & dir : dirs)
                ss << "  " << dir << "\n";
            return checkFail(ss.str());
            return checkFail(toView(ss));
        }

        return checkPass("PATH contains only one nix version.");


@@ 125,14 125,14 @@ struct CmdConfigCheck : StoreCommand
        }

        if (!dirs.empty()) {
            std::stringstream ss;
            std::ostringstream ss;
            ss << "Found profiles outside of " << settings.nixStateDir << "/profiles.\n"
               << "The generation this profile points to might not have a gcroot and could be\n"
               << "garbage collected, resulting in broken symlinks.\n\n";
            for (auto & dir : dirs)
                ss << "  " << dir << "\n";
            ss << "\n";
            return checkFail(ss.str());
            return checkFail(toView(ss));
        }

        return checkPass("All profiles are gcroots.");


@@ 145,13 145,13 @@ struct CmdConfigCheck : StoreCommand
            : PROTOCOL_VERSION;

        if (clientProto != storeProto) {
            std::stringstream ss;
            std::ostringstream ss;
            ss << "Warning: protocol version of this client does not match the store.\n"
               << "While this is not necessarily a problem it's recommended to keep the client in\n"
               << "sync with the daemon.\n\n"
               << "Client protocol: " << formatProtocol(clientProto) << "\n"
               << "Store protocol: " << formatProtocol(storeProto) << "\n\n";
            return checkFail(ss.str());
            return checkFail(toView(ss));
        }

        return checkPass("Client protocol matches store protocol.");