From af070744aecf944598c8655f5460779a63e66b40 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 27 Feb 2017 22:10:57 +0100 Subject: [PATCH] :hammer: integrating numtostr into serializer class By merging numtostr into serializer, we can write directly to the output stream. As a consequence, all stream calls are now unformatted. --- src/json.hpp | 220 ++++++++++++++++++++-------------------------- src/json.hpp.re2c | 216 +++++++++++++++++++-------------------------- 2 files changed, 185 insertions(+), 251 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index a73b5048..3e5a45cf 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6196,6 +6196,9 @@ class basic_json /// @{ private: + /*! + @brief wrapper around the serialization functions + */ class serializer { public: @@ -6223,7 +6226,7 @@ class basic_json void dump(const basic_json& val, const bool pretty_print, const unsigned int indent_step, - const unsigned int current_indent = 0) const + const unsigned int current_indent = 0) { switch (val.m_type) { @@ -6377,19 +6380,19 @@ class basic_json case value_t::number_integer: { - o << numtostr(val.m_value.number_integer).c_str(); + x_write(val.m_value.number_integer); return; } case value_t::number_unsigned: { - o << numtostr(val.m_value.number_unsigned).c_str(); + x_write(val.m_value.number_unsigned); return; } case value_t::number_float: { - o << numtostr(val.m_value.number_float).c_str(); + x_write(val.m_value.number_float); return; } @@ -6569,154 +6572,120 @@ class basic_json return result; } - /*! - @brief locale-independent serialization for built-in arithmetic types - */ - struct numtostr + template + void x_write(NumberType x) { - public: - template - numtostr(NumberType value) + // special case for "0" + if (x == 0) { - x_write(value, std::is_integral()); + o.put('0'); + return; } - const char* c_str() const + const bool is_negative = x < 0; + size_t i = 0; + + // spare 1 byte for '\0' + while (x != 0 and i < m_buf.size() - 1) { - return m_buf.data(); + const auto digit = std::labs(static_cast(x % 10)); + m_buf[i++] = static_cast('0' + digit); + x /= 10; } - private: - /// a (hopefully) large enough character buffer - std::array < char, 64 > m_buf{{}}; + // make sure the number has been processed completely + assert(x == 0); - template - void x_write(NumberType x, /*is_integral=*/std::true_type) + if (is_negative) { - // special case for "0" - if (x == 0) - { - m_buf[0] = '0'; - return; - } - - const bool is_negative = x < 0; - size_t i = 0; - - // spare 1 byte for '\0' - while (x != 0 and i < m_buf.size() - 1) - { - const auto digit = std::labs(static_cast(x % 10)); - m_buf[i++] = static_cast('0' + digit); - x /= 10; - } - - // make sure the number has been processed completely - assert(x == 0); - - if (is_negative) - { - // make sure there is capacity for the '-' - assert(i < m_buf.size() - 2); - m_buf[i++] = '-'; - } - - std::reverse(m_buf.begin(), m_buf.begin() + i); + // make sure there is capacity for the '-' + assert(i < m_buf.size() - 2); + m_buf[i++] = '-'; } - template - void x_write(NumberType x, /*is_integral=*/std::false_type) + std::reverse(m_buf.begin(), m_buf.begin() + i); + o.write(m_buf.data(), static_cast(i)); + } + + void x_write(number_float_t x) + { + // special case for 0.0 and -0.0 + if (x == 0) { - // special case for 0.0 and -0.0 - if (x == 0) + if (std::signbit(x)) { - size_t i = 0; - if (std::signbit(x)) - { - m_buf[i++] = '-'; - } - m_buf[i++] = '0'; - m_buf[i++] = '.'; - m_buf[i] = '0'; - return; + o.write("-0.0", 4); } - - // get number of digits for a text -> float -> text round-trip - static constexpr auto d = std::numeric_limits::digits10; - - // the actual conversion - const auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x); - - // negative value indicates an error - assert(written_bytes > 0); - // check if buffer was large enough - assert(static_cast(written_bytes) < m_buf.size()); - - // read information from locale - const auto loc = localeconv(); - assert(loc != nullptr); - const char thousands_sep = !loc->thousands_sep ? '\0' - : loc->thousands_sep[0]; - - const char decimal_point = !loc->decimal_point ? '\0' - : loc->decimal_point[0]; - - // erase thousands separator - if (thousands_sep != '\0') + else { - const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep); - std::fill(end, m_buf.end(), '\0'); + o.write("0.0", 3); } + return; + } - // convert decimal point to '.' - if (decimal_point != '\0' and decimal_point != '.') - { - for (auto& c : m_buf) - { - if (c == decimal_point) - { - c = '.'; - break; - } - } - } + // get number of digits for a text -> float -> text round-trip + static constexpr auto d = std::numeric_limits::digits10; - // determine if need to append ".0" - size_t i = 0; - bool value_is_int_like = true; - for (i = 0; i < m_buf.size(); ++i) + // the actual conversion + auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x); + + // negative value indicates an error + assert(written_bytes > 0); + // check if buffer was large enough + assert(static_cast(written_bytes) < m_buf.size()); + + // read information from locale + const auto loc = localeconv(); + assert(loc != nullptr); + const char thousands_sep = !loc->thousands_sep ? '\0' + : loc->thousands_sep[0]; + + const char decimal_point = !loc->decimal_point ? '\0' + : loc->decimal_point[0]; + + // erase thousands separator + if (thousands_sep != '\0') + { + const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep); + std::fill(end, m_buf.end(), '\0'); + written_bytes -= (m_buf.end() - end); + } + + // convert decimal point to '.' + if (decimal_point != '\0' and decimal_point != '.') + { + for (auto& c : m_buf) { - // break when end of number is reached - if (m_buf[i] == '\0') + if (c == decimal_point) { + c = '.'; break; } - - // check if we find non-int character - value_is_int_like = value_is_int_like and m_buf[i] != '.' and - m_buf[i] != 'e' and m_buf[i] != 'E'; - } - - if (value_is_int_like) - { - // there must be 2 bytes left for ".0" - assert((i + 2) < m_buf.size()); - // we write to the end of the number - assert(m_buf[i] == '\0'); - assert(m_buf[i - 1] != '\0'); - - // add ".0" - m_buf[i] = '.'; - m_buf[i + 1] = '0'; - - // the resulting string is properly terminated - assert(m_buf[i + 2] == '\0'); } } - }; + + // determine if need to append ".0" + bool value_is_int_like = true; + for (size_t i = 0; i < static_cast(written_bytes); ++i) + { + // check if we find non-int character + value_is_int_like = value_is_int_like and m_buf[i] != '.' and + m_buf[i] != 'e'; + } + + o.write(m_buf.data(), static_cast(written_bytes)); + + if (value_is_int_like) + { + o.write(".0", 2); + } + } private: std::ostream& o; + + /// a (hopefully) large enough character buffer + std::array < char, 64 > m_buf{{}}; }; public: @@ -6754,7 +6723,6 @@ class basic_json // do the actual serialization serializer s(o); s.dump(j, pretty_print, static_cast(indentation)); - return o; } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 01f6bbfe..e648b41b 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -6226,7 +6226,7 @@ class basic_json void dump(const basic_json& val, const bool pretty_print, const unsigned int indent_step, - const unsigned int current_indent = 0) const + const unsigned int current_indent = 0) { switch (val.m_type) { @@ -6380,19 +6380,19 @@ class basic_json case value_t::number_integer: { - o << numtostr(val.m_value.number_integer).c_str(); + x_write(val.m_value.number_integer); return; } case value_t::number_unsigned: { - o << numtostr(val.m_value.number_unsigned).c_str(); + x_write(val.m_value.number_unsigned); return; } case value_t::number_float: { - o << numtostr(val.m_value.number_float).c_str(); + x_write(val.m_value.number_float); return; } @@ -6572,154 +6572,120 @@ class basic_json return result; } - /*! - @brief locale-independent serialization for built-in arithmetic types - */ - struct numtostr + template + void x_write(NumberType x) { - public: - template - numtostr(NumberType value) + // special case for "0" + if (x == 0) { - x_write(value, std::is_integral()); + o.put('0'); + return; } - const char* c_str() const + const bool is_negative = x < 0; + size_t i = 0; + + // spare 1 byte for '\0' + while (x != 0 and i < m_buf.size() - 1) { - return m_buf.data(); + const auto digit = std::labs(static_cast(x % 10)); + m_buf[i++] = static_cast('0' + digit); + x /= 10; } - private: - /// a (hopefully) large enough character buffer - std::array < char, 64 > m_buf{{}}; + // make sure the number has been processed completely + assert(x == 0); - template - void x_write(NumberType x, /*is_integral=*/std::true_type) + if (is_negative) { - // special case for "0" - if (x == 0) - { - m_buf[0] = '0'; - return; - } - - const bool is_negative = x < 0; - size_t i = 0; - - // spare 1 byte for '\0' - while (x != 0 and i < m_buf.size() - 1) - { - const auto digit = std::labs(static_cast(x % 10)); - m_buf[i++] = static_cast('0' + digit); - x /= 10; - } - - // make sure the number has been processed completely - assert(x == 0); - - if (is_negative) - { - // make sure there is capacity for the '-' - assert(i < m_buf.size() - 2); - m_buf[i++] = '-'; - } - - std::reverse(m_buf.begin(), m_buf.begin() + i); + // make sure there is capacity for the '-' + assert(i < m_buf.size() - 2); + m_buf[i++] = '-'; } - template - void x_write(NumberType x, /*is_integral=*/std::false_type) + std::reverse(m_buf.begin(), m_buf.begin() + i); + o.write(m_buf.data(), static_cast(i)); + } + + void x_write(number_float_t x) + { + // special case for 0.0 and -0.0 + if (x == 0) { - // special case for 0.0 and -0.0 - if (x == 0) + if (std::signbit(x)) { - size_t i = 0; - if (std::signbit(x)) - { - m_buf[i++] = '-'; - } - m_buf[i++] = '0'; - m_buf[i++] = '.'; - m_buf[i] = '0'; - return; + o.write("-0.0", 4); } - - // get number of digits for a text -> float -> text round-trip - static constexpr auto d = std::numeric_limits::digits10; - - // the actual conversion - const auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x); - - // negative value indicates an error - assert(written_bytes > 0); - // check if buffer was large enough - assert(static_cast(written_bytes) < m_buf.size()); - - // read information from locale - const auto loc = localeconv(); - assert(loc != nullptr); - const char thousands_sep = !loc->thousands_sep ? '\0' - : loc->thousands_sep[0]; - - const char decimal_point = !loc->decimal_point ? '\0' - : loc->decimal_point[0]; - - // erase thousands separator - if (thousands_sep != '\0') + else { - const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep); - std::fill(end, m_buf.end(), '\0'); + o.write("0.0", 3); } + return; + } - // convert decimal point to '.' - if (decimal_point != '\0' and decimal_point != '.') - { - for (auto& c : m_buf) - { - if (c == decimal_point) - { - c = '.'; - break; - } - } - } + // get number of digits for a text -> float -> text round-trip + static constexpr auto d = std::numeric_limits::digits10; - // determine if need to append ".0" - size_t i = 0; - bool value_is_int_like = true; - for (i = 0; i < m_buf.size(); ++i) + // the actual conversion + auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x); + + // negative value indicates an error + assert(written_bytes > 0); + // check if buffer was large enough + assert(static_cast(written_bytes) < m_buf.size()); + + // read information from locale + const auto loc = localeconv(); + assert(loc != nullptr); + const char thousands_sep = !loc->thousands_sep ? '\0' + : loc->thousands_sep[0]; + + const char decimal_point = !loc->decimal_point ? '\0' + : loc->decimal_point[0]; + + // erase thousands separator + if (thousands_sep != '\0') + { + const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep); + std::fill(end, m_buf.end(), '\0'); + written_bytes -= (m_buf.end() - end); + } + + // convert decimal point to '.' + if (decimal_point != '\0' and decimal_point != '.') + { + for (auto& c : m_buf) { - // break when end of number is reached - if (m_buf[i] == '\0') + if (c == decimal_point) { + c = '.'; break; } - - // check if we find non-int character - value_is_int_like = value_is_int_like and m_buf[i] != '.' and - m_buf[i] != 'e' and m_buf[i] != 'E'; - } - - if (value_is_int_like) - { - // there must be 2 bytes left for ".0" - assert((i + 2) < m_buf.size()); - // we write to the end of the number - assert(m_buf[i] == '\0'); - assert(m_buf[i - 1] != '\0'); - - // add ".0" - m_buf[i] = '.'; - m_buf[i + 1] = '0'; - - // the resulting string is properly terminated - assert(m_buf[i + 2] == '\0'); } } - }; + + // determine if need to append ".0" + bool value_is_int_like = true; + for (size_t i = 0; i < static_cast(written_bytes); ++i) + { + // check if we find non-int character + value_is_int_like = value_is_int_like and m_buf[i] != '.' and + m_buf[i] != 'e'; + } + + o.write(m_buf.data(), static_cast(written_bytes)); + + if (value_is_int_like) + { + o.write(".0", 2); + } + } private: std::ostream& o; + + /// a (hopefully) large enough character buffer + std::array < char, 64 > m_buf{{}}; }; public: