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

summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorMartin Hořeňovský2025-08-21 23:36:22 +0200
committerMartin Hořeňovský2025-08-22 17:00:40 +0200
commitfb2e4fbe413824a5cad4101c0e94cb10b7828f0d (patch)
tree8da4e08aeeea0d85f71f5db767a832085a9f8666 /src
parent78a9518a2847f82cb65124d65eaf0b0be8d90cdc (diff)
downloadcatch3-fb2e4fbe413824a5cad4101c0e94cb10b7828f0d.tar.gz
Improve performance of writing JSON values
The old code was exceedingly simple, as it went char-by-char and decided whether to write it to the output stream as-is, or escaped. This caused a _lot_ of stream writes of individual characters. The new code instead looks for characters that need escaping, and bulk-writes the non-escaped characters in between them. This leads to about the same performance for strings that comprise of only escaped characters, and 3-10x improvement for strings without any escaping needed. In practice, we should expect the former rather than the latter, but this is still nice improvement.
Diffstat (limited to 'src')
-rw-r--r--src/catch2/internal/catch_jsonwriter.cpp61
1 files changed, 39 insertions, 22 deletions
diff --git a/src/catch2/internal/catch_jsonwriter.cpp b/src/catch2/internal/catch_jsonwriter.cpp
index 1a96e348..6915c9c3 100644
--- a/src/catch2/internal/catch_jsonwriter.cpp
+++ b/src/catch2/internal/catch_jsonwriter.cpp
@@ -7,8 +7,36 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_jsonwriter.hpp>
+#include <catch2/internal/catch_unreachable.hpp>
namespace Catch {
+
+ namespace {
+ static bool needsEscape( char c ) {
+ return c == '"' || c == '\\' || c == '\b' || c == '\f' ||
+ c == '\n' || c == '\r' || c == '\t';
+ }
+
+ static Catch::StringRef makeEscapeStringRef( char c ) {
+ if ( c == '"' ) {
+ return "\\\""_sr;
+ } else if ( c == '\\' ) {
+ return "\\\\"_sr;
+ } else if ( c == '\b' ) {
+ return "\\b"_sr;
+ } else if ( c == '\f' ) {
+ return "\\f"_sr;
+ } else if ( c == '\n' ) {
+ return "\\n"_sr;
+ } else if ( c == '\r' ) {
+ return "\\r"_sr;
+ } else if ( c == '\t' ) {
+ return "\\t"_sr;
+ }
+ Catch::Detail::Unreachable();
+ }
+ } // namespace
+
void JsonUtils::indent( std::ostream& os, std::uint64_t level ) {
for ( std::uint64_t i = 0; i < level; ++i ) {
os << " ";
@@ -118,30 +146,19 @@ namespace Catch {
void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) {
if ( quote ) { m_os << '"'; }
- for (char c : value) {
- // Escape list taken from https://www.json.org/json-en.html,
- // string definition.
- // Note that while forward slash _can_ be escaped, it does
- // not have to be, if JSON is not further embedded somewhere
- // where forward slash is meaningful.
- if ( c == '"' ) {
- m_os << "\\\"";
- } else if ( c == '\\' ) {
- m_os << "\\\\";
- } else if ( c == '\b' ) {
- m_os << "\\b";
- } else if ( c == '\f' ) {
- m_os << "\\f";
- } else if ( c == '\n' ) {
- m_os << "\\n";
- } else if ( c == '\r' ) {
- m_os << "\\r";
- } else if ( c == '\t' ) {
- m_os << "\\t";
- } else {
- m_os << c;
+ size_t current_start = 0;
+ for ( size_t i = 0; i < value.size(); ++i ) {
+ if ( needsEscape( value[i] ) ) {
+ if ( current_start < i ) {
+ m_os << value.substr( current_start, i - current_start );
+ }
+ m_os << makeEscapeStringRef( value[i] );
+ current_start = i + 1;
}
}
+ if ( current_start < value.size() ) {
+ m_os << value.substr( current_start, value.size() - current_start );
+ }
if ( quote ) { m_os << '"'; }
}