Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMartin Hořeňovský2023-12-27 16:19:49 +0100
committerMartin Hořeňovský2023-12-27 16:24:33 +0100
commit5d637d4c6b72dc21e2e198894ba0527729fba535 (patch)
tree9c64d803f63d6ad2f18c9552af0a9521e3f1f0d8
parentcd3c7ebe873c3da91ce57c6b2221ba95ee7f93e6 (diff)
downloadcatch3-5d637d4c6b72dc21e2e198894ba0527729fba535.tar.gz
Args and TokenStream use StringRefs instead of std::strings
Because TokenStream is copied around a lot, moving it to use `StringRef` removes _a lot_ of allocations per `Opt` in the parser. Args are not copied around much, but changing them as well makes it obvious that they do not participate in the ownership. The changes add up to removing ~180 allocations for "empty" invocation of the test binary. (`./tests/ExtraTests/NoTests --allow-running-no-tests -o /dev/null` is down to 317 allocs)
-rw-r--r--src/catch2/internal/catch_clara.cpp37
-rw-r--r--src/catch2/internal/catch_clara.hpp21
-rw-r--r--src/catch2/internal/catch_stringref.hpp2
3 files changed, 38 insertions, 22 deletions
diff --git a/src/catch2/internal/catch_clara.cpp b/src/catch2/internal/catch_clara.cpp
index b4f5d054..68603923 100644
--- a/src/catch2/internal/catch_clara.cpp
+++ b/src/catch2/internal/catch_clara.cpp
@@ -37,6 +37,19 @@ namespace {
return optName;
}
+ static size_t find_first_separator(Catch::StringRef sr) {
+ auto is_separator = []( char c ) {
+ return c == ' ' || c == ':' || c == '=';
+ };
+ size_t pos = 0;
+ while (pos < sr.size()) {
+ if (is_separator(sr[pos])) { return pos; }
+ ++pos;
+ }
+
+ return Catch::StringRef::npos;
+ }
+
} // namespace
namespace Catch {
@@ -52,23 +65,23 @@ namespace Catch {
}
if ( it != itEnd ) {
- auto const& next = *it;
+ StringRef next = *it;
if ( isOptPrefix( next[0] ) ) {
- auto delimiterPos = next.find_first_of( " :=" );
- if ( delimiterPos != std::string::npos ) {
+ auto delimiterPos = find_first_separator(next);
+ if ( delimiterPos != StringRef::npos ) {
m_tokenBuffer.push_back(
{ TokenType::Option,
next.substr( 0, delimiterPos ) } );
m_tokenBuffer.push_back(
{ TokenType::Argument,
- next.substr( delimiterPos + 1 ) } );
+ next.substr( delimiterPos + 1, next.size() ) } );
} else {
if ( next[1] != '-' && next.size() > 2 ) {
- std::string opt = "- ";
+ // Combined short args, e.g. "-ab" for "-a -b"
for ( size_t i = 1; i < next.size(); ++i ) {
- opt[1] = next[i];
m_tokenBuffer.push_back(
- { TokenType::Option, opt } );
+ { TokenType::Option,
+ next.substr( i, 1 ) } );
}
} else {
m_tokenBuffer.push_back(
@@ -128,7 +141,7 @@ namespace Catch {
size_t ParserBase::cardinality() const { return 1; }
InternalParseResult ParserBase::parse( Args const& args ) const {
- return parse( args.exeName(), TokenStream( args ) );
+ return parse( static_cast<std::string>(args.exeName()), TokenStream( args ) );
}
ParseState::ParseState( ParseResultType type,
@@ -166,7 +179,7 @@ namespace Catch {
auto valueRef =
static_cast<Detail::BoundValueRefBase*>(m_ref.get());
- auto result = valueRef->setValue(remainingTokens->token);
+ auto result = valueRef->setValue(static_cast<std::string>(token.token));
if (!result)
return Detail::InternalParseResult(result);
else
@@ -192,7 +205,7 @@ namespace Catch {
return { oss.str(), m_description };
}
- bool Opt::isMatch(std::string const& optToken) const {
+ bool Opt::isMatch(StringRef optToken) const {
auto normalisedToken = normaliseOpt(optToken);
for (auto const& name : m_optNames) {
if (normaliseOpt(name) == normalisedToken)
@@ -237,7 +250,7 @@ namespace Catch {
return Detail::InternalParseResult::runtimeError(
"Expected argument following " +
token.token);
- const auto result = valueRef->setValue(argToken.token);
+ const auto result = valueRef->setValue(static_cast<std::string>(argToken.token));
if (!result)
return Detail::InternalParseResult(result);
if (result.value() ==
@@ -433,7 +446,7 @@ namespace Catch {
Args::Args(int argc, char const* const* argv) :
m_exeName(argv[0]), m_args(argv + 1, argv + argc) {}
- Args::Args(std::initializer_list<std::string> args) :
+ Args::Args(std::initializer_list<StringRef> args) :
m_exeName(*args.begin()),
m_args(args.begin() + 1, args.end()) {}
diff --git a/src/catch2/internal/catch_clara.hpp b/src/catch2/internal/catch_clara.hpp
index a5c5e4e4..608f7274 100644
--- a/src/catch2/internal/catch_clara.hpp
+++ b/src/catch2/internal/catch_clara.hpp
@@ -102,17 +102,16 @@ namespace Catch {
enum class TokenType { Option, Argument };
struct Token {
TokenType type;
- std::string token;
+ StringRef token;
};
// Abstracts iterators into args as a stream of tokens, with option
// arguments uniformly handled
class TokenStream {
- using Iterator = std::vector<std::string>::const_iterator;
+ using Iterator = std::vector<StringRef>::const_iterator;
Iterator it;
Iterator itEnd;
std::vector<Token> m_tokenBuffer;
-
void loadBuffer();
public:
@@ -582,7 +581,7 @@ namespace Catch {
Detail::HelpColumns getHelpColumns() const;
- bool isMatch(std::string const& optToken) const;
+ bool isMatch(StringRef optToken) const;
using ParserBase::parse;
@@ -676,18 +675,20 @@ namespace Catch {
Detail::TokenStream const& tokens) const override;
};
- // Transport for raw args (copied from main args, or supplied via
- // init list for testing)
+ /**
+ * Wrapper over argc + argv, assumes that the inputs outlive it
+ */
class Args {
friend Detail::TokenStream;
- std::string m_exeName;
- std::vector<std::string> m_args;
+ StringRef m_exeName;
+ std::vector<StringRef> m_args;
public:
Args(int argc, char const* const* argv);
- Args(std::initializer_list<std::string> args);
+ // Helper constructor for testing
+ Args(std::initializer_list<StringRef> args);
- std::string const& exeName() const { return m_exeName; }
+ StringRef exeName() const { return m_exeName; }
};
diff --git a/src/catch2/internal/catch_stringref.hpp b/src/catch2/internal/catch_stringref.hpp
index 93f25171..4b9212bf 100644
--- a/src/catch2/internal/catch_stringref.hpp
+++ b/src/catch2/internal/catch_stringref.hpp
@@ -25,6 +25,8 @@ namespace Catch {
using size_type = std::size_t;
using const_iterator = const char*;
+ static constexpr size_type npos{ static_cast<size_type>( -1 ) };
+
private:
static constexpr char const* const s_empty = "";