Skip to content

Commit

Permalink
Don't include <regex> in header files
Browse files Browse the repository at this point in the history
This reduces compilation time by ~15 seconds (CPU time).

Issue #4045.
  • Loading branch information
edolstra committed Sep 21, 2020
1 parent cbe0bb2 commit e8e1d42
Show file tree
Hide file tree
Showing 16 changed files with 96 additions and 53 deletions.
1 change: 1 addition & 0 deletions src/libexpr/eval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
, sEpsilon(symbols.create(""))
, repair(NoRepair)
, store(store)
, regexCache(makeRegexCache())
, baseEnv(allocEnv(128))
, staticBaseEnv(false, 0)
{
Expand Down
8 changes: 6 additions & 2 deletions src/libexpr/eval.hh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include "symbol-table.hh"
#include "config.hh"

#include <regex>
#include <map>
#include <optional>
#include <unordered_map>
Expand Down Expand Up @@ -65,6 +64,11 @@ typedef std::list<SearchPathElem> SearchPath;
void initGC();


struct RegexCache;

std::shared_ptr<RegexCache> makeRegexCache();


class EvalState
{
public:
Expand Down Expand Up @@ -120,7 +124,7 @@ private:
std::unordered_map<Path, Path> resolvedPaths;

/* Cache used by prim_match(). */
std::unordered_map<std::string, std::regex> regexCache;
std::shared_ptr<RegexCache> regexCache;

public:

Expand Down
1 change: 1 addition & 0 deletions src/libexpr/flake/flakeref.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "flakeref.hh"
#include "store-api.hh"
#include "url.hh"
#include "url-parts.hh"
#include "fetchers.hh"
#include "registry.hh"

Expand Down
1 change: 1 addition & 0 deletions src/libexpr/flake/lockfile.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "lockfile.hh"
#include "store-api.hh"
#include "url-parts.hh"

#include <nlohmann/json.hpp>

Expand Down
18 changes: 13 additions & 5 deletions src/libexpr/primops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3085,17 +3085,25 @@ static RegisterPrimOp primop_hashString({
.fun = prim_hashString,
});

/* Match a regular expression against a string and return either
‘null’ or a list containing substring matches. */
struct RegexCache
{
std::unordered_map<std::string, std::regex> cache;
};

std::shared_ptr<RegexCache> makeRegexCache()
{
return std::make_shared<RegexCache>();
}

void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
auto re = state.forceStringNoCtx(*args[0], pos);

try {

auto regex = state.regexCache.find(re);
if (regex == state.regexCache.end())
regex = state.regexCache.emplace(re, std::regex(re, std::regex::extended)).first;
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;

PathSet context;
const std::string str = state.forceString(*args[1], context, pos);
Expand Down
3 changes: 1 addition & 2 deletions src/libexpr/primops/fetchMercurial.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
#include "store-api.hh"
#include "fetchers.hh"
#include "url.hh"

#include <regex>
#include "url-parts.hh"

namespace nix {

Expand Down
1 change: 1 addition & 0 deletions src/libfetchers/git.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "globals.hh"
#include "tarfile.hh"
#include "store-api.hh"
#include "url-parts.hh"

#include <sys/time.h>

Expand Down
1 change: 1 addition & 0 deletions src/libfetchers/github.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "fetchers.hh"
#include "globals.hh"
#include "store-api.hh"
#include "url-parts.hh"

#include <nlohmann/json.hpp>

Expand Down
1 change: 1 addition & 0 deletions src/libfetchers/indirect.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "fetchers.hh"
#include "url-parts.hh"

namespace nix::fetchers {

Expand Down
1 change: 1 addition & 0 deletions src/libfetchers/mercurial.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "globals.hh"
#include "tarfile.hh"
#include "store-api.hh"
#include "url-parts.hh"

#include <sys/time.h>

Expand Down
21 changes: 18 additions & 3 deletions src/libstore/names.cc
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
#include "names.hh"
#include "util.hh"

#include <regex>


namespace nix {


struct Regex
{
std::regex regex;
};


DrvName::DrvName()
{
name = "";
Expand All @@ -30,11 +38,18 @@ DrvName::DrvName(std::string_view s) : hits(0)
}


DrvName::~DrvName()
{ }


bool DrvName::matches(DrvName & n)
{
if (name != "*") {
if (!regex) regex = std::unique_ptr<std::regex>(new std::regex(name, std::regex::extended));
if (!std::regex_match(n.name, *regex)) return false;
if (!regex) {
regex = std::make_unique<Regex>();
regex->regex = std::regex(name, std::regex::extended);
}
if (!std::regex_match(n.name, regex->regex)) return false;
}
if (version != "" && version != n.version) return false;
return true;
Expand Down Expand Up @@ -99,7 +114,7 @@ DrvNames drvNamesFromArgs(const Strings & opArgs)
{
DrvNames result;
for (auto & i : opArgs)
result.push_back(DrvName(i));
result.emplace_back(i);
return result;
}

Expand Down
7 changes: 5 additions & 2 deletions src/libstore/names.hh
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
#include <memory>

#include "types.hh"
#include <regex>

namespace nix {

struct Regex;

struct DrvName
{
string fullName;
Expand All @@ -16,10 +17,12 @@ struct DrvName

DrvName();
DrvName(std::string_view s);
~DrvName();

bool matches(DrvName & n);

private:
std::unique_ptr<std::regex> regex;
std::unique_ptr<Regex> regex;
};

typedef list<DrvName> DrvNames;
Expand Down
44 changes: 44 additions & 0 deletions src/libutil/url-parts.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once

#include <string>
#include <regex>

namespace nix {

// URI stuff.
const static std::string pctEncoded = "(?:%[0-9a-fA-F][0-9a-fA-F])";
const static std::string schemeRegex = "(?:[a-z+.-]+)";
const static std::string ipv6AddressRegex = "(?:\\[[0-9a-fA-F:]+\\])";
const static std::string unreservedRegex = "(?:[a-zA-Z0-9-._~])";
const static std::string subdelimsRegex = "(?:[!$&'\"()*+,;=])";
const static std::string hostnameRegex = "(?:(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + ")*)";
const static std::string hostRegex = "(?:" + ipv6AddressRegex + "|" + hostnameRegex + ")";
const static std::string userRegex = "(?:(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|:)*)";
const static std::string authorityRegex = "(?:" + userRegex + "@)?" + hostRegex + "(?::[0-9]+)?";
const static std::string pcharRegex = "(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|[:@])";
const static std::string queryRegex = "(?:" + pcharRegex + "|[/? \"])*";
const static std::string segmentRegex = "(?:" + pcharRegex + "+)";
const static std::string absPathRegex = "(?:(?:/" + segmentRegex + ")*/?)";
const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)";

// A Git ref (i.e. branch or tag name).
const static std::string refRegexS = "[a-zA-Z0-9][a-zA-Z0-9_.-]*"; // FIXME: check
extern std::regex refRegex;

// Instead of defining what a good Git Ref is, we define what a bad Git Ref is
// This is because of the definition of a ref in refs.c in https://github.com/git/git
// See tests/fetchGitRefs.sh for the full definition
const static std::string badGitRefRegexS = "//|^[./]|/\\.|\\.\\.|[[:cntrl:][:space:]:?^~\[]|\\\\|\\*|\\.lock$|\\.lock/|@\\{|[/.]$|^@$|^$";
extern std::regex badGitRefRegex;

// A Git revision (a SHA-1 commit hash).
const static std::string revRegexS = "[0-9a-fA-F]{40}";
extern std::regex revRegex;

// A ref or revision, or a ref followed by a revision.
const static std::string refAndOrRevRegex = "(?:(" + revRegexS + ")|(?:(" + refRegexS + ")(?:/(" + revRegexS + "))?))";

const static std::string flakeIdRegexS = "[a-zA-Z][a-zA-Z0-9_-]*";
extern std::regex flakeIdRegex;

}
1 change: 1 addition & 0 deletions src/libutil/url.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "url.hh"
#include "url-parts.hh"
#include "util.hh"

namespace nix {
Expand Down
38 changes: 0 additions & 38 deletions src/libutil/url.hh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

#include "error.hh"

#include <regex>

namespace nix {

struct ParsedURL
Expand All @@ -29,40 +27,4 @@ std::map<std::string, std::string> decodeQuery(const std::string & query);

ParsedURL parseURL(const std::string & url);

// URI stuff.
const static std::string pctEncoded = "(?:%[0-9a-fA-F][0-9a-fA-F])";
const static std::string schemeRegex = "(?:[a-z+.-]+)";
const static std::string ipv6AddressRegex = "(?:\\[[0-9a-fA-F:]+\\])";
const static std::string unreservedRegex = "(?:[a-zA-Z0-9-._~])";
const static std::string subdelimsRegex = "(?:[!$&'\"()*+,;=])";
const static std::string hostnameRegex = "(?:(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + ")*)";
const static std::string hostRegex = "(?:" + ipv6AddressRegex + "|" + hostnameRegex + ")";
const static std::string userRegex = "(?:(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|:)*)";
const static std::string authorityRegex = "(?:" + userRegex + "@)?" + hostRegex + "(?::[0-9]+)?";
const static std::string pcharRegex = "(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|[:@])";
const static std::string queryRegex = "(?:" + pcharRegex + "|[/? \"])*";
const static std::string segmentRegex = "(?:" + pcharRegex + "+)";
const static std::string absPathRegex = "(?:(?:/" + segmentRegex + ")*/?)";
const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)";

// A Git ref (i.e. branch or tag name).
const static std::string refRegexS = "[a-zA-Z0-9][a-zA-Z0-9_.-]*"; // FIXME: check
extern std::regex refRegex;

// Instead of defining what a good Git Ref is, we define what a bad Git Ref is
// This is because of the definition of a ref in refs.c in https://github.com/git/git
// See tests/fetchGitRefs.sh for the full definition
const static std::string badGitRefRegexS = "//|^[./]|/\\.|\\.\\.|[[:cntrl:][:space:]:?^~\[]|\\\\|\\*|\\.lock$|\\.lock/|@\\{|[/.]$|^@$|^$";
extern std::regex badGitRefRegex;

// A Git revision (a SHA-1 commit hash).
const static std::string revRegexS = "[0-9a-fA-F]{40}";
extern std::regex revRegex;

// A ref or revision, or a ref followed by a revision.
const static std::string refAndOrRevRegex = "(?:(" + revRegexS + ")|(?:(" + refRegexS + ")(?:/(" + revRegexS + "))?))";

const static std::string flakeIdRegexS = "[a-zA-Z][a-zA-Z0-9_-]*";
extern std::regex flakeIdRegex;

}
2 changes: 1 addition & 1 deletion src/nix-env/nix-env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
{
DrvNames selectors = drvNamesFromArgs(args);
if (selectors.empty())
selectors.push_back(DrvName("*"));
selectors.emplace_back("*");

DrvInfos elems;
set<unsigned int> done;
Expand Down

0 comments on commit e8e1d42

Please sign in to comment.