Optimisation

Signed-off-by: JCW <a1q123456@users.noreply.github.com>
This commit is contained in:
JCW
2025-09-02 00:07:17 +01:00
parent dbb14191cb
commit f7c043bd04
3 changed files with 153 additions and 29 deletions

View File

@@ -82,73 +82,77 @@ class SimpleJsonWriter
public: public:
explicit SimpleJsonWriter(std::ostringstream& stream) : stream_(stream) explicit SimpleJsonWriter(std::ostringstream& stream) : stream_(stream)
{ {
stream_.imbue(std::locale::classic());
} }
void void
startObject() const startObject() const
{ {
stream_ << "{"; stream_.put('{');
} }
void void
endObject() const endObject() const
{ {
stream_.seekp(-1, std::ios_base::end); stream_.seekp(-1, std::ios_base::end);
stream_ << "},"; stream_.write("},", 2);
} }
void void
writeKey(std::string_view key) const writeKey(std::string_view key) const
{ {
writeString(key); writeString(key);
stream_.seekp(-1, std::ios_base::end); stream_.seekp(-1, std::ios_base::end);
stream_ << ":"; stream_.put(':');
} }
void void
startArray() const startArray() const
{ {
stream_ << "["; stream_.put('[');
} }
void void
endArray() const endArray() const
{ {
stream_.seekp(-1, std::ios_base::end); stream_.seekp(-1, std::ios_base::end);
stream_ << "],"; stream_.write("],", 2);
} }
void void
writeString(std::string_view str) const writeString(std::string_view str) const
{ {
stream_ << "\""; stream_.put('"');
escape(str, stream_); escape(str, stream_);
stream_ << "\","; stream_.write("\",", 2);
} }
void std::string_view
writeInt(std::int64_t val) const writeInt(std::int64_t val) const
{ {
stream_ << val << ","; return pushNumber(val, stream_);
} }
void std::string_view
writeUInt(std::uint64_t val) const writeUInt(std::uint64_t val) const
{ {
stream_ << val << ","; return pushNumber(val, stream_);
} }
void std::string_view
writeDouble(double val) const writeDouble(double val) const
{ {
stream_ << val << ","; return pushNumber(val, stream_);
} }
void std::string_view
writeBool(bool val) const writeBool(bool val) const
{ {
stream_ << (val ? "true," : "false,"); auto str = val ? "true," : "false,";
stream_.write(str, std::strlen(str));
return str;
} }
void void
writeNull() const writeNull() const
{ {
stream_ << "null" << ","; stream_.write("null", std::strlen("null"));
stream_.put(',');
} }
void void
writeRaw(std::string_view str) const writeRaw(std::string_view str) const
{ {
stream_ << str; stream_.write(str.data(), str.length());
} }
[[nodiscard]] std::string [[nodiscard]] std::string
@@ -160,11 +164,69 @@ public:
} }
private: private:
template <typename T>
static std::string_view
pushNumber(T val, std::ostringstream& stream)
{
static char buffer[128];
auto result = std::to_chars(std::begin(buffer), std::end(buffer), val);
*result.ptr = ',';
auto len = result.ptr - std::begin(buffer);
stream.write(buffer, len + 1);
return {buffer, static_cast<size_t>(len)};
}
static void static void
escape(std::string_view str, std::ostringstream& os) escape(std::string_view str, std::ostringstream& os)
{ {
// TODO: Support it static constexpr char HEX[] = "0123456789ABCDEF";
os << str;
const char* p = str.data();
const char* end = p + str.size();
const char* chunk = p;
while (p < end) {
auto c = static_cast<unsigned char>(*p);
// JSON requires escaping for <0x20 and the two specials below.
bool needsEscape = (c < 0x20) || (c == '"') || (c == '\\');
if (!needsEscape) {
++p;
continue;
}
// Flush the preceding safe run in one go.
if (chunk != p)
os.write(chunk, p - chunk);
switch (c) {
case '"': os.write("\\\"", 2); break;
case '\\': os.write("\\\\", 2); break;
case '\b': os.write("\\b", 2); break;
case '\f': os.write("\\f", 2); break;
case '\n': os.write("\\n", 2); break;
case '\r': os.write("\\r", 2); break;
case '\t': os.write("\\t", 2); break;
default: {
// Other C0 controls -> \u00XX (JSON compliant)
char buf[6]{
'\\','u','0','0',
HEX[(c >> 4) & 0xF],
HEX[c & 0xF]
};
os.write(buf, 6);
break;
}
}
++p;
chunk = p;
}
// Flush trailing safe run
if (chunk != p)
os.write(chunk, p - chunk);
} }
std::ostringstream& stream_; std::ostringstream& stream_;
@@ -769,6 +831,13 @@ using logwstream = basic_logstream<wchar_t>;
namespace ripple::log { namespace ripple::log {
namespace detail { namespace detail {
template <typename T>
concept CanToChars = requires (T val)
{
{ to_chars(std::declval<char*>(), std::declval<char*>(), val) } -> std::convertible_to<std::to_chars_result>;
};
template <typename T> template <typename T>
void void
setJsonValue( setJsonValue(
@@ -781,34 +850,35 @@ setJsonValue(
writer.writeKey(name); writer.writeKey(name);
if constexpr (std::is_integral_v<ValueType>) if constexpr (std::is_integral_v<ValueType>)
{ {
std::string_view sv;
if constexpr (std::is_signed_v<ValueType>) if constexpr (std::is_signed_v<ValueType>)
{ {
writer.writeInt(value); sv = writer.writeInt(value);
} }
else else
{ {
writer.writeUInt(value); sv = writer.writeUInt(value);
} }
if (outStream) if (outStream)
{ {
(*outStream) << value; outStream->write(sv.data(), sv.size());
} }
} }
else if constexpr (std::is_floating_point_v<ValueType>) else if constexpr (std::is_floating_point_v<ValueType>)
{ {
writer.writeDouble(value); auto sv = writer.writeDouble(value);
if (outStream) if (outStream)
{ {
(*outStream) << value; outStream->write(sv.data(), sv.size());
} }
} }
else if constexpr (std::is_same_v<ValueType, bool>) else if constexpr (std::is_same_v<ValueType, bool>)
{ {
writer.writeBool(value); auto sv = writer.writeBool(value);
if (outStream) if (outStream)
{ {
(*outStream) << value; outStream->write(sv.data(), sv.size());
} }
} }
else if constexpr ( else if constexpr (
@@ -818,7 +888,7 @@ setJsonValue(
writer.writeString(value); writer.writeString(value);
if (outStream) if (outStream)
{ {
(*outStream) << value; outStream->write(value, std::strlen(value));
} }
} }
else if constexpr (std::is_same_v<ValueType, std::string>) else if constexpr (std::is_same_v<ValueType, std::string>)
@@ -826,12 +896,30 @@ setJsonValue(
writer.writeString(value); writer.writeString(value);
if (outStream) if (outStream)
{ {
(*outStream) << value; outStream->write(value.c_str(), value.length());
} }
} }
else else
{ {
if constexpr (CanToChars<ValueType>)
{
char buffer[1024];
std::to_chars_result result = to_chars(std::begin(buffer), std::end(buffer), value);
if (result.ec == std::errc{})
{
std::string_view sv;
sv = {std::begin(buffer), result.ptr};
writer.writeString(sv);
if (outStream)
{
outStream->write(sv.data(), sv.size());
}
return;
}
}
std::ostringstream oss; std::ostringstream oss;
oss.imbue(std::locale::classic());
oss << value; oss << value;
auto str = oss.str(); auto str = oss.str();
@@ -840,7 +928,7 @@ setJsonValue(
if (outStream) if (outStream)
{ {
(*outStream) << str; outStream->write(str.c_str(), static_cast<std::streamsize>(str.size()));
} }
} }
} }

View File

@@ -131,6 +131,7 @@ Journal::JsonLogContext::reset(
ThreadIdStringInitializer() ThreadIdStringInitializer()
{ {
std::stringstream threadIdStream; std::stringstream threadIdStream;
threadIdStream.imbue(std::locale::classic());
threadIdStream << std::this_thread::get_id(); threadIdStream << std::this_thread::get_id();
value = threadIdStream.str(); value = threadIdStream.str();
} }

View File

@@ -209,6 +209,41 @@ TEST_CASE("Global attributes inheritable")
beast::Journal::disableStructuredJournal(); beast::Journal::disableStructuredJournal();
} }
TEST_CASE("Test JsonWriter")
{
{
std::ostringstream stream;
beast::SimpleJsonWriter writer{stream};
writer.writeString("\n");
CHECK(writer.str() == "\"\\n\"");
}
{
std::ostringstream stream;
beast::SimpleJsonWriter writer{stream};
writer.writeString("\t");
CHECK(writer.str() == "\"\\t\"");
}
{
std::ostringstream stream;
beast::SimpleJsonWriter writer{stream};
writer.writeString(std::string_view{"\0", 1});
CHECK(writer.str() == "\"\\u0000\"");
}
{
std::ostringstream stream;
beast::SimpleJsonWriter writer{stream};
writer.writeString("\"\\");
CHECK(writer.str() == "\"\\\"\\\\\"");
}
}
/** /**
* @brief sink for writing all log messages to a stringstream * @brief sink for writing all log messages to a stringstream
*/ */