diff options
| author | Martin Hořeňovský | 2026-09-09 20:15:30 +0200 |
|---|---|---|
| committer | Martin Hořeňovský | 2026-09-11 11:21:22 +0200 |
| commit | 5e47f48ecc68ff48c3c085d93502559038141693 (patch) | |
| tree | 8593f3aaf7cb63cb3bc036012b3891afbe49cca1 /src | |
| parent | ab13a28d125b50a458667e972ed56400af7ad931 (diff) | |
| download | catch3-5e47f48ecc68ff48c3c085d93502559038141693.tar.gz | |
Avoid per-input alloc+copy in WildcardPattern::matches
The old implementation always forced copy (and likely alloc) for
each input when matching it. The new implementation uses the
case-insensitive comparators to avoid all that.
This provides interesting performance improvement for selecting
individual test, doing something like `./tests/SelfTest "ABC*"`
is almost 10% faster.
Diffstat (limited to 'src')
| -rw-r--r-- | src/catch2/catch_test_spec.cpp | 2 | ||||
| -rw-r--r-- | src/catch2/internal/catch_wildcard_pattern.cpp | 60 | ||||
| -rw-r--r-- | src/catch2/internal/catch_wildcard_pattern.hpp | 4 |
3 files changed, 43 insertions, 23 deletions
diff --git a/src/catch2/catch_test_spec.cpp b/src/catch2/catch_test_spec.cpp index f7c202df..0b33ecb3 100644 --- a/src/catch2/catch_test_spec.cpp +++ b/src/catch2/catch_test_spec.cpp @@ -32,7 +32,7 @@ namespace Catch { TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString ) : Pattern( filterString ) - , m_wildcardPattern( toLower( name ), CaseSensitive::No ) + , m_wildcardPattern( name, CaseSensitive::No ) {} bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { diff --git a/src/catch2/internal/catch_wildcard_pattern.cpp b/src/catch2/internal/catch_wildcard_pattern.cpp index 09c5a405..c6b1b1c5 100644 --- a/src/catch2/internal/catch_wildcard_pattern.cpp +++ b/src/catch2/internal/catch_wildcard_pattern.cpp @@ -8,40 +8,60 @@ #include <catch2/internal/catch_wildcard_pattern.hpp> #include <catch2/internal/catch_enforce.hpp> #include <catch2/internal/catch_string_manip.hpp> +#include <catch2/internal/catch_stringref.hpp> +#include <catch2/internal/catch_case_insensitive_comparisons.hpp> namespace Catch { WildcardPattern::WildcardPattern( std::string const& pattern, CaseSensitive caseSensitivity ) : m_caseSensitivity( caseSensitivity ), - m_pattern( normaliseString( pattern ) ) + m_pattern( trim(pattern) ) { if( startsWith( m_pattern, '*' ) ) { - m_pattern = m_pattern.substr( 1 ); - m_wildcard = WildcardAtStart; + m_pattern = m_pattern.erase( 0, 1 ); // effectively pop_front + m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtStart ); } if( endsWith( m_pattern, '*' ) ) { - m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); + m_pattern.pop_back(); m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd ); } } - bool WildcardPattern::matches( std::string const& str ) const { - switch( m_wildcard ) { - case NoWildcard: - return m_pattern == normaliseString( str ); - case WildcardAtStart: - return endsWith( normaliseString( str ), m_pattern ); - case WildcardAtEnd: - return startsWith( normaliseString( str ), m_pattern ); - case WildcardAtBothEnds: - return contains( normaliseString( str ), m_pattern ); - default: - CATCH_INTERNAL_ERROR( "Unknown enum" ); - } - } + bool WildcardPattern::matches( StringRef input ) const { + input = trim( input ); + if ( input.size() < m_pattern.size() ) { return false; } + + auto casedMatch = [&]( StringRef lhs, StringRef rhs ) { + if ( m_caseSensitivity == CaseSensitive::Yes ) { + return lhs == rhs; + } else { + Detail::CaseInsensitiveEqualTo eq; + return eq( lhs, rhs ); + } + }; - std::string WildcardPattern::normaliseString( std::string const& str ) const { - return trim( m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str ); + switch ( m_wildcard ) { + case NoWildcard: + return casedMatch(input, m_pattern); + case WildcardAtStart: + return casedMatch( + input.substr( input.size() - m_pattern.size(), input.size() ), + m_pattern ); + case WildcardAtEnd: + return casedMatch( input.substr( 0, m_pattern.size() ), m_pattern ); + case WildcardAtBothEnds: { + // TBD: This has terrible performance, but it is also the least + // common use case. Once the StringContains matcher gets + // efficient implementation, we should reuse it here. + const size_t lastOffset = input.size() - m_pattern.size(); + for ( size_t offset = 0; offset <= lastOffset; ++offset ) { + if ( casedMatch( input.substr(offset, m_pattern.size()), m_pattern ) ) { return true; } + } + return false; + } + default: + CATCH_INTERNAL_ERROR( "Unknown enum" ); + } } } diff --git a/src/catch2/internal/catch_wildcard_pattern.hpp b/src/catch2/internal/catch_wildcard_pattern.hpp index 4f410859..2e286368 100644 --- a/src/catch2/internal/catch_wildcard_pattern.hpp +++ b/src/catch2/internal/catch_wildcard_pattern.hpp @@ -9,6 +9,7 @@ #define CATCH_WILDCARD_PATTERN_HPP_INCLUDED #include <catch2/catch_case_sensitive.hpp> +#include <catch2/internal/catch_stringref.hpp> #include <string> @@ -25,10 +26,9 @@ namespace Catch public: WildcardPattern( std::string const& pattern, CaseSensitive caseSensitivity ); - bool matches( std::string const& str ) const; + bool matches( StringRef str ) const; private: - std::string normaliseString( std::string const& str ) const; CaseSensitive m_caseSensitivity; WildcardPosition m_wildcard = NoWildcard; std::string m_pattern; |