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

summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorMartin Hořeňovský2026-09-09 20:15:30 +0200
committerMartin Hořeňovský2026-09-11 11:21:22 +0200
commit5e47f48ecc68ff48c3c085d93502559038141693 (patch)
tree8593f3aaf7cb63cb3bc036012b3891afbe49cca1 /src
parentab13a28d125b50a458667e972ed56400af7ad931 (diff)
downloadcatch3-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.cpp2
-rw-r--r--src/catch2/internal/catch_wildcard_pattern.cpp60
-rw-r--r--src/catch2/internal/catch_wildcard_pattern.hpp4
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;