diff --git a/include/internal/catch_string.cpp b/include/internal/catch_string.cpp index b18f5d1b..73d9d77a 100644 --- a/include/internal/catch_string.cpp +++ b/include/internal/catch_string.cpp @@ -64,6 +64,9 @@ namespace Catch { auto String::size() const noexcept -> size_type { return m_data->size; } + auto String::numberOfCharacters() const noexcept -> size_type { + return StringRef( *this ).numberOfCharacters(); + } auto String::c_str() const noexcept -> char const* { return m_data->chars; } diff --git a/include/internal/catch_string.h b/include/internal/catch_string.h index 5fb477a4..04552037 100644 --- a/include/internal/catch_string.h +++ b/include/internal/catch_string.h @@ -40,6 +40,8 @@ namespace Catch { auto empty() const noexcept -> bool; auto size() const noexcept -> size_type; + auto numberOfCharacters() const noexcept -> size_type; + auto c_str() const noexcept -> char const*; }; diff --git a/include/internal/catch_stringref.cpp b/include/internal/catch_stringref.cpp index 9572c56f..44db6858 100644 --- a/include/internal/catch_stringref.cpp +++ b/include/internal/catch_stringref.cpp @@ -136,7 +136,23 @@ namespace Catch { auto StringRef::size() const noexcept -> size_type { return m_size; } - + auto StringRef::numberOfCharacters() const noexcept -> size_type { + size_type noChars = m_size; + // Make adjustments for uft encodings + for( size_type i=0; i < m_size; ++i ) { + char c = m_start[i]; + if( ( c & 0b11000000 ) == 0b11000000 ) { + if( ( c & 0b11100000 ) == 0b11000000 ) + noChars--; + else if( ( c & 0b11110000 ) == 0b11100000 ) + noChars-=2; + else if( ( c & 0b11111000 ) == 0b11110000 ) + noChars-=3; + } + } + return noChars; + } + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> String { StringBuilder buf; buf.reserve( lhs.size() + rhs.size() ); diff --git a/include/internal/catch_stringref.h b/include/internal/catch_stringref.h index d7f1efbe..55a99aee 100644 --- a/include/internal/catch_stringref.h +++ b/include/internal/catch_stringref.h @@ -56,6 +56,7 @@ namespace Catch { public: // named queries auto empty() const noexcept -> bool; auto size() const noexcept -> size_type; + auto numberOfCharacters() const noexcept -> size_type; auto c_str() const -> char const*; public: // substrings and searches diff --git a/include/reporters/catch_reporter_console.cpp b/include/reporters/catch_reporter_console.cpp index 8ba14f5f..33259041 100644 --- a/include/reporters/catch_reporter_console.cpp +++ b/include/reporters/catch_reporter_console.cpp @@ -12,6 +12,7 @@ #include "../internal/catch_console_colour.hpp" #include "../internal/catch_version.h" #include "../internal/catch_text.h" +#include "../internal/catch_stringref.h" #include #include @@ -82,6 +83,8 @@ namespace { friend TablePrinter& operator << ( TablePrinter& tp, ColumnBreak ) { auto colStr = tp.m_oss.str(); + // This takes account of utf8 encodings + auto strSize = Catch::StringRef( colStr.c_str(), colStr.size() ).numberOfCharacters(); tp.m_oss.str(""); tp.open(); if( tp.m_currentColumn == static_cast(tp.m_columnInfos.size()-1) ) { @@ -93,8 +96,8 @@ namespace { tp.m_currentColumn++; auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; - auto padding = ( colStr.size()+2 < static_cast( colInfo.width ) ) - ? std::string( colInfo.width-(colStr.size()+2), ' ' ) + auto padding = ( strSize+2 < static_cast( colInfo.width ) ) + ? std::string( colInfo.width-(strSize+2), ' ' ) : std::string(); if( colInfo.justification == ColumnInfo::Left ) tp.m_os << " " << colStr << padding << " |"; @@ -112,6 +115,79 @@ namespace { return tp; } }; + + class Duration { + enum class Unit { + Auto, + Nanoseconds, + Microseconds, + Milliseconds, + Seconds, + Minutes + }; + static const uint64_t s_nanosecondsInAMicrosecond = 1000; + static const uint64_t s_nanosecondsInAMillisecond = 1000*s_nanosecondsInAMicrosecond; + static const uint64_t s_nanosecondsInASecond = 1000*s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = 60*s_nanosecondsInASecond; + + uint64_t m_inNanoseconds; + Unit m_units; + + public: + Duration( uint64_t inNanoseconds, Unit units = Unit::Auto ) + : m_inNanoseconds( inNanoseconds ), + m_units( units ) + { + if( m_units == Unit::Auto ) { + if( m_inNanoseconds < s_nanosecondsInAMicrosecond ) + m_units = Unit::Nanoseconds; + else if( m_inNanoseconds < s_nanosecondsInAMillisecond ) + m_units = Unit::Microseconds; + else if( m_inNanoseconds < s_nanosecondsInASecond ) + m_units = Unit::Milliseconds; + else if( m_inNanoseconds < s_nanosecondsInAMinute ) + m_units = Unit::Seconds; + else + m_units = Unit::Minutes; + } + + } + + auto value() const -> double { + switch( m_units ) { + case Unit::Microseconds: + return m_inNanoseconds / static_cast( s_nanosecondsInAMicrosecond ); + case Unit::Milliseconds: + return m_inNanoseconds / static_cast( s_nanosecondsInAMillisecond ); + case Unit::Seconds: + return m_inNanoseconds / static_cast( s_nanosecondsInASecond ); + case Unit::Minutes: + return m_inNanoseconds / static_cast( s_nanosecondsInAMinute ); + default: + return static_cast( m_inNanoseconds ); + } + } + auto unitsAsString() const -> std::string { + switch( m_units ) { + case Unit::Nanoseconds: + return "ns"; + case Unit::Microseconds: + return "µs"; + case Unit::Milliseconds: + return "ms"; + case Unit::Seconds: + return "s"; + case Unit::Minutes: + return "m"; + default: + return "** internal error **"; + } + + } + friend auto operator << ( std::ostream& os, Duration const& duration ) -> std::ostream& { + return os << duration.value() << " " << duration.unitsAsString(); + } + }; } namespace Catch { @@ -123,10 +199,10 @@ namespace Catch { : StreamingReporterBase( config ), m_tablePrinter( config.stream(), { - { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH-38, ColumnInfo::Left }, + { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH-42, ColumnInfo::Left }, { "iters", 8, ColumnInfo::Right }, - { "elapsed ns", 12, ColumnInfo::Right }, - { "average", 12, ColumnInfo::Right } + { "elapsed ns", 14, ColumnInfo::Right }, + { "average", 14, ColumnInfo::Right } } ) {} ~ConsoleReporter() override; @@ -199,11 +275,11 @@ namespace Catch { } } void benchmarkEnded( BenchmarkStats const& stats ) override { - // !TBD: report average times in natural units? + Duration average( stats.elapsedTimeInNanoseconds/stats.iterations ); m_tablePrinter << stats.iterations << ColumnBreak() << stats.elapsedTimeInNanoseconds << ColumnBreak() - << stats.elapsedTimeInNanoseconds/stats.iterations << " ns" << ColumnBreak(); + << average << ColumnBreak(); } void testCaseEnded( TestCaseStats const& _testCaseStats ) override {