From 3b98a0166f7b7196eba2ad518174d1a77165166d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ho=C5=99e=C5=88ovsk=C3=BD?= Date: Sun, 15 Jan 2017 09:41:33 +0100 Subject: [PATCH] Various small string usage performance improvements. * Empty strings are now direct constructed as `std::string()`, not as empty string literals. * `startsWith` and `endsWith` no longer construct new a string. This should be an improvement for libstdc++ when using older standards, as it doesn't use SSO but COW and thus even short strings are expensive to first create. * Various places now use char literal instead of string literals containing single char. ** `startsWith` and `endsWith` now also have overload that takes single char. Generally the performance improvements under VS2015 are small, as going from short string to char is mostly meaningless because of SSO (Catch doesn't push string handling that hard) and previous commit removed most string handling if tests pass, which is the expect case. --- include/internal/catch_commandline.hpp | 6 +++--- include/internal/catch_common.h | 3 +++ include/internal/catch_common.hpp | 21 ++++++++++++++----- include/internal/catch_list.hpp | 6 +++--- include/internal/catch_matchers.hpp | 2 +- .../internal/catch_reporter_registrars.hpp | 2 +- include/internal/catch_result_builder.h | 2 +- include/internal/catch_run_context.hpp | 12 +++++------ include/internal/catch_tag_alias_registry.hpp | 4 ++-- include/internal/catch_test_case_info.hpp | 2 +- .../catch_test_case_registry_impl.hpp | 6 +++--- include/internal/catch_tostring.hpp | 8 +++---- include/internal/catch_wildcard_pattern.hpp | 4 ++-- include/internal/catch_xmlwriter.hpp | 2 +- 14 files changed, 47 insertions(+), 33 deletions(-) diff --git a/include/internal/catch_commandline.hpp b/include/internal/catch_commandline.hpp index 89eced34..e7ee6e82 100644 --- a/include/internal/catch_commandline.hpp +++ b/include/internal/catch_commandline.hpp @@ -85,10 +85,10 @@ namespace Catch { std::string line; while( std::getline( f, line ) ) { line = trim(line); - if( !line.empty() && !startsWith( line, "#" ) ) { - if( !startsWith( line, "\"" ) ) + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) line = "\"" + line + "\""; - addTestOrTags( config, line + "," ); + addTestOrTags( config, line + ',' ); } } } diff --git a/include/internal/catch_common.h b/include/internal/catch_common.h index 9e742bc4..43f46a98 100644 --- a/include/internal/catch_common.h +++ b/include/internal/catch_common.h @@ -79,7 +79,10 @@ namespace Catch { } bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); + bool contains( std::string const& s, std::string const& infix ); bool contains( std::string const& s, std::string const& infix ); void toLowerInPlace( std::string& s ); std::string toLower( std::string const& s ); diff --git a/include/internal/catch_common.hpp b/include/internal/catch_common.hpp index 7ad5b1ca..f4006c72 100644 --- a/include/internal/catch_common.hpp +++ b/include/internal/catch_common.hpp @@ -10,17 +10,28 @@ #include "catch_common.h" +#include + namespace Catch { bool startsWith( std::string const& s, std::string const& prefix ) { - return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith( std::string const& s, char prefix ) { + return !s.empty() && s.front() == prefix; } bool endsWith( std::string const& s, std::string const& suffix ) { - return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s.back() == suffix; } bool contains( std::string const& s, std::string const& infix ) { return s.find( infix ) != std::string::npos; } + bool contains( std::string const& s, char infix ) { + return s.find(infix); + } char toLowerCh(char c) { return static_cast( ::tolower( c ) ); } @@ -37,7 +48,7 @@ namespace Catch { std::string::size_type start = str.find_first_not_of( whitespaceChars ); std::string::size_type end = str.find_last_not_of( whitespaceChars ); - return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); } bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { @@ -95,9 +106,9 @@ namespace Catch { std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { #ifndef __GNUG__ - os << info.file << "(" << info.line << ")"; + os << info.file << '(' << info.line << ')'; #else - os << info.file << ":" << info.line; + os << info.file << ':' << info.line; #endif return os; } diff --git a/include/internal/catch_list.hpp b/include/internal/catch_list.hpp index f83827ee..72f38bba 100644 --- a/include/internal/catch_list.hpp +++ b/include/internal/catch_list.hpp @@ -51,9 +51,9 @@ namespace Catch { } if( !config.testSpec().hasFilters() ) - Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl; + Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl; else - Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; + Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl; return matchedTests; } @@ -68,7 +68,7 @@ namespace Catch { ++it ) { matchedTests++; TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - if( startsWith( testCaseInfo.name, "#" ) ) + if( startsWith( testCaseInfo.name, '#' ) ) Catch::cout() << "\"" << testCaseInfo.name << "\"" << std::endl; else Catch::cout() << testCaseInfo.name << std::endl; diff --git a/include/internal/catch_matchers.hpp b/include/internal/catch_matchers.hpp index ab8fec15..61234c45 100644 --- a/include/internal/catch_matchers.hpp +++ b/include/internal/catch_matchers.hpp @@ -184,7 +184,7 @@ namespace Matchers { { return m_caseSensitivity == CaseSensitive::No ? " (case insensitive)" - : ""; + : std::string(); } CaseSensitive::Choice m_caseSensitivity; std::string m_str; diff --git a/include/internal/catch_reporter_registrars.hpp b/include/internal/catch_reporter_registrars.hpp index 7bd7b610..4d5557e1 100644 --- a/include/internal/catch_reporter_registrars.hpp +++ b/include/internal/catch_reporter_registrars.hpp @@ -74,7 +74,7 @@ namespace Catch { return new T( config ); } virtual std::string getDescription() const { - return ""; + return std::string(); } }; diff --git a/include/internal/catch_result_builder.h b/include/internal/catch_result_builder.h index f646685a..dfdffd5f 100644 --- a/include/internal/catch_result_builder.h +++ b/include/internal/catch_result_builder.h @@ -25,7 +25,7 @@ namespace Catch { oss << other.oss.str(); } CopyableStream& operator=( CopyableStream const& other ) { - oss.str(""); + oss.str(std::string()); oss << other.oss.str(); return *this; } diff --git a/include/internal/catch_run_context.hpp b/include/internal/catch_run_context.hpp index d37bdba1..30929625 100644 --- a/include/internal/catch_run_context.hpp +++ b/include/internal/catch_run_context.hpp @@ -146,7 +146,7 @@ namespace Catch { m_messages.clear(); // Reset working state - m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); + m_lastAssertionInfo = AssertionInfo( std::string(), m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); m_lastResult = result; } @@ -217,7 +217,7 @@ namespace Catch { virtual std::string getCurrentTestName() const { return m_activeTestCase ? m_activeTestCase->getTestCaseInfo().name - : ""; + : std::string(); } virtual const AssertionResult* getLastResult() const { @@ -247,11 +247,11 @@ namespace Catch { deltaTotals.testCases.failed = 1; m_reporter->testCaseEnded( TestCaseStats( testInfo, deltaTotals, - "", - "", + std::string(), + std::string(), false ) ); m_totals.testCases.failed++; - testGroupEnded( "", m_totals, 1, 1 ); + testGroupEnded( std::string(), m_totals, 1, 1 ); m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); } @@ -270,7 +270,7 @@ namespace Catch { Counts prevAssertions = m_totals.assertions; double duration = 0; try { - m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); + m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, std::string(), ResultDisposition::Normal ); seedRng( *m_config ); diff --git a/include/internal/catch_tag_alias_registry.hpp b/include/internal/catch_tag_alias_registry.hpp index e5ad11b2..a1fa56b9 100644 --- a/include/internal/catch_tag_alias_registry.hpp +++ b/include/internal/catch_tag_alias_registry.hpp @@ -43,7 +43,7 @@ namespace Catch { void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { - if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) { + if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) { std::ostringstream oss; oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; throw std::domain_error( oss.str().c_str() ); @@ -51,7 +51,7 @@ namespace Catch { if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { std::ostringstream oss; oss << "error: tag alias, \"" << alias << "\" already registered.\n" - << "\tFirst seen at " << find(alias)->lineInfo << "\n" + << "\tFirst seen at " << find(alias)->lineInfo << '\n' << "\tRedefined at " << lineInfo; throw std::domain_error( oss.str().c_str() ); } diff --git a/include/internal/catch_test_case_info.hpp b/include/internal/catch_test_case_info.hpp index 90f53411..d3212902 100644 --- a/include/internal/catch_test_case_info.hpp +++ b/include/internal/catch_test_case_info.hpp @@ -16,7 +16,7 @@ namespace Catch { inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { - if( startsWith( tag, "." ) || + if( startsWith( tag, '.' ) || tag == "hide" || tag == "!hide" ) return TestCaseInfo::IsHidden; diff --git a/include/internal/catch_test_case_registry_impl.hpp b/include/internal/catch_test_case_registry_impl.hpp index 33169b26..0565b54a 100644 --- a/include/internal/catch_test_case_registry_impl.hpp +++ b/include/internal/catch_test_case_registry_impl.hpp @@ -78,7 +78,7 @@ namespace Catch { ss << Colour( Colour::Red ) << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n' << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; throw std::runtime_error(ss.str()); @@ -110,7 +110,7 @@ namespace Catch { virtual void registerTest( TestCase const& testCase ) { std::string name = testCase.getTestCaseInfo().name; - if( name == "" ) { + if( name.empty() ) { std::ostringstream oss; oss << "Anonymous test case " << ++m_unnamedCount; return registerTest( testCase.withName( oss.str() ) ); @@ -159,7 +159,7 @@ namespace Catch { inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { std::string className = classOrQualifiedMethodName; - if( startsWith( className, "&" ) ) + if( startsWith( className, '&' ) ) { std::size_t lastColons = className.rfind( "::" ); std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); diff --git a/include/internal/catch_tostring.hpp b/include/internal/catch_tostring.hpp index 0a20ee2d..0c4ed62f 100644 --- a/include/internal/catch_tostring.hpp +++ b/include/internal/catch_tostring.hpp @@ -102,7 +102,7 @@ std::string toString( int value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } @@ -110,7 +110,7 @@ std::string toString( unsigned long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } @@ -164,14 +164,14 @@ std::string toString( long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } std::string toString( unsigned long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } #endif diff --git a/include/internal/catch_wildcard_pattern.hpp b/include/internal/catch_wildcard_pattern.hpp index cd8b07e1..6ce66a4d 100644 --- a/include/internal/catch_wildcard_pattern.hpp +++ b/include/internal/catch_wildcard_pattern.hpp @@ -27,11 +27,11 @@ namespace Catch m_wildcard( NoWildcard ), m_pattern( adjustCase( pattern ) ) { - if( startsWith( m_pattern, "*" ) ) { + if( startsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 1 ); m_wildcard = WildcardAtStart; } - if( endsWith( m_pattern, "*" ) ) { + if( endsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); } diff --git a/include/internal/catch_xmlwriter.hpp b/include/internal/catch_xmlwriter.hpp index c181e10e..17f7064e 100644 --- a/include/internal/catch_xmlwriter.hpp +++ b/include/internal/catch_xmlwriter.hpp @@ -202,7 +202,7 @@ namespace Catch { XmlWriter& writeBlankLine() { ensureTagClosed(); - stream() << "\n"; + stream() << '\n'; return *this; }