Skip to content

Commit

Permalink
JSONWriter deals in StringRefs instead of std::strings
Browse files Browse the repository at this point in the history
Together with liberal use of `_sr` UDL to compile-time convert
string literals into StringRefs, this will reduce the number of
allocation and remove most of the strcpy calls inherent in
converting string lits into `std::string`s.
  • Loading branch information
horenmar committed Nov 16, 2023
1 parent 733b901 commit 6aed5da
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 127 deletions.
43 changes: 38 additions & 5 deletions src/catch2/internal/catch_jsonwriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace Catch {
JsonObjectWriter::JsonObjectWriter( std::ostream& os,
std::uint64_t indent_level ):
m_os{ os }, m_indent_level{ indent_level } {
m_os << "{";
m_os << '{';
}
JsonObjectWriter::JsonObjectWriter( JsonObjectWriter&& source ):
m_os{ source.m_os },
Expand All @@ -47,11 +47,11 @@ namespace Catch {
m_os << '}';
}

JsonValueWriter JsonObjectWriter::write( std::string const& key ) {
JsonValueWriter JsonObjectWriter::write( StringRef key ) {
JsonUtils::appendCommaNewline(
m_os, m_should_comma, m_indent_level + 1 );

m_os << '"' << key << '"' << ": ";
m_os << '"' << key << "\": ";
return JsonValueWriter{ m_os, m_indent_level + 1 };
}

Expand All @@ -60,7 +60,7 @@ namespace Catch {
JsonArrayWriter::JsonArrayWriter( std::ostream& os,
std::uint64_t indent_level ):
m_os{ os }, m_indent_level{ indent_level } {
m_os << "[";
m_os << '[';
}
JsonArrayWriter::JsonArrayWriter( JsonArrayWriter&& source ):
m_os{ source.m_os },
Expand Down Expand Up @@ -108,8 +108,41 @@ namespace Catch {
return JsonArrayWriter{ m_os, m_indent_level };
}

void JsonValueWriter::write( Catch::StringRef value ) && {
writeImpl( value, true );
}

void JsonValueWriter::write( bool value ) && {
writeImpl( value ? "true" : "false", false );
writeImpl( value ? "true"_sr : "false"_sr, false );
}

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;
}
}
if ( quote ) { m_os << '"'; }
}

} // namespace Catch
45 changes: 13 additions & 32 deletions src/catch2/internal/catch_jsonwriter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,45 +37,26 @@ namespace Catch {
void write( T const& value ) && {
writeImpl( value, !std::is_arithmetic<T>::value );
}

void write( StringRef value ) &&;
void write( bool value ) &&;

private:
template <typename T>
void writeImpl( StringRef value, bool quote );

// Without this SFINAE, this overload is a better match
// for `std::string`, `char const*`, `char const[N]` args.
// While it would still work, it would cause code bloat
// and multiple iteration over the strings
template <typename T,
typename = typename std::enable_if_t<
!std::is_convertible<T, StringRef>::value>>
void writeImpl( T const& value, bool quote_value ) {
if ( quote_value ) { m_os << '"'; }
m_sstream << value;
while ( true ) {
char c = m_sstream.get();
if ( m_sstream.eof() ) { break; }

// see https://www.json.org/json-en.html, string definition for the escape list
if ( c == '"' ) {
m_os << "\\\"";
} else if ( c == '\\' ) {
m_os << "\\\\";
// 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.
} 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;
}
}
if ( quote_value ) { m_os << '"'; }
writeImpl( m_sstream.str(), quote_value );
}

std::ostream& m_os;
std::stringstream m_sstream{};
std::stringstream m_sstream;
std::uint64_t m_indent_level;
};

Expand All @@ -89,7 +70,7 @@ namespace Catch {

~JsonObjectWriter();

JsonValueWriter write( std::string const& key );
JsonValueWriter write( StringRef key );

private:
std::ostream& m_os;
Expand Down
Loading

0 comments on commit 6aed5da

Please sign in to comment.