From 9c4919ff34a78dfd0dd4307518716e60399ad37a Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 27 Feb 2017 16:19:07 +0100 Subject: [PATCH] :rewind: ":zap: micro-optimizations for dump()" This reverts commit 909b439b03dd1aa048eeaef82c5abfce85c6a56a. For some strange reason, the test suite crashes when compiled with GCC. --- src/json.hpp | 72 ++++++++++++++++++++++++++++++----------------- src/json.hpp.re2c | 72 ++++++++++++++++++++++++++++++----------------- 2 files changed, 92 insertions(+), 52 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 0f452706..1094aade 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8252,9 +8252,14 @@ class basic_json { public: template - numtostr(NumberType value, std::ostream& o) + numtostr(NumberType value) { - x_write(value, std::is_integral(), o); + x_write(value, std::is_integral()); + } + + const char* c_str() const + { + return m_buf.data(); } private: @@ -8262,12 +8267,12 @@ class basic_json std::array < char, 64 > m_buf{{}}; template - void x_write(NumberType x, /*is_integral=*/std::true_type, std::ostream& o) + void x_write(NumberType x, /*is_integral=*/std::true_type) { // special case for "0" if (x == 0) { - o.put('0'); + m_buf[0] = '0'; return; } @@ -8293,31 +8298,30 @@ class basic_json } std::reverse(m_buf.begin(), m_buf.begin() + i); - o.write(m_buf.data(), static_cast(i)); } template - void x_write(NumberType x, /*is_integral=*/std::false_type, std::ostream& o) + void x_write(NumberType x, /*is_integral=*/std::false_type) { // special case for 0.0 and -0.0 if (x == 0) { + size_t i = 0; if (std::signbit(x)) { - o.write("-0.0", 4); - } - else - { - o.write("0.0", 3); + m_buf[i++] = '-'; } + m_buf[i++] = '0'; + m_buf[i++] = '.'; + m_buf[i] = '0'; return; } // get number of digits for a text -> float -> text round-trip static constexpr auto d = std::numeric_limits::digits10; - // the actual conversion; store how many bytes have been written - auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x); + // 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); @@ -8338,7 +8342,6 @@ class basic_json { 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 '.' @@ -8354,19 +8357,36 @@ class basic_json } } - o.write(m_buf.data(), static_cast(written_bytes)); - // determine if need to append ".0" - const bool value_is_int_like = std::all_of(m_buf.begin(), - m_buf.begin() + written_bytes + 1, - [](char c) + size_t i = 0; + bool value_is_int_like = true; + for (i = 0; i < m_buf.size(); ++i) { - // we use %g above, so there cannot be an 'E' character - return c != '.' and c != 'e'; - }); + // break when end of number is reached + if (m_buf[i] == '\0') + { + 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) { - o.write(".0", 2); + // 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'); } } }; @@ -8546,19 +8566,19 @@ class basic_json case value_t::number_integer: { - numtostr(m_value.number_integer, o); + o << numtostr(m_value.number_integer).c_str(); return; } case value_t::number_unsigned: { - numtostr(m_value.number_unsigned, o); + o << numtostr(m_value.number_unsigned).c_str(); return; } case value_t::number_float: { - numtostr(m_value.number_float, o); + o << numtostr(m_value.number_float).c_str(); return; } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index b4b9e3d2..a686a1a7 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8252,9 +8252,14 @@ class basic_json { public: template - numtostr(NumberType value, std::ostream& o) + numtostr(NumberType value) { - x_write(value, std::is_integral(), o); + x_write(value, std::is_integral()); + } + + const char* c_str() const + { + return m_buf.data(); } private: @@ -8262,12 +8267,12 @@ class basic_json std::array < char, 64 > m_buf{{}}; template - void x_write(NumberType x, /*is_integral=*/std::true_type, std::ostream& o) + void x_write(NumberType x, /*is_integral=*/std::true_type) { // special case for "0" if (x == 0) { - o.put('0'); + m_buf[0] = '0'; return; } @@ -8293,31 +8298,30 @@ class basic_json } std::reverse(m_buf.begin(), m_buf.begin() + i); - o.write(m_buf.data(), static_cast(i)); } template - void x_write(NumberType x, /*is_integral=*/std::false_type, std::ostream& o) + void x_write(NumberType x, /*is_integral=*/std::false_type) { // special case for 0.0 and -0.0 if (x == 0) { + size_t i = 0; if (std::signbit(x)) { - o.write("-0.0", 4); - } - else - { - o.write("0.0", 3); + m_buf[i++] = '-'; } + m_buf[i++] = '0'; + m_buf[i++] = '.'; + m_buf[i] = '0'; return; } // get number of digits for a text -> float -> text round-trip static constexpr auto d = std::numeric_limits::digits10; - // the actual conversion; store how many bytes have been written - auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x); + // 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); @@ -8338,7 +8342,6 @@ class basic_json { 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 '.' @@ -8354,19 +8357,36 @@ class basic_json } } - o.write(m_buf.data(), static_cast(written_bytes)); - // determine if need to append ".0" - const bool value_is_int_like = std::all_of(m_buf.begin(), - m_buf.begin() + written_bytes + 1, - [](char c) + size_t i = 0; + bool value_is_int_like = true; + for (i = 0; i < m_buf.size(); ++i) { - // we use %g above, so there cannot be an 'E' character - return c != '.' and c != 'e'; - }); + // break when end of number is reached + if (m_buf[i] == '\0') + { + 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) { - o.write(".0", 2); + // 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'); } } }; @@ -8546,19 +8566,19 @@ class basic_json case value_t::number_integer: { - numtostr(m_value.number_integer, o); + o << numtostr(m_value.number_integer).c_str(); return; } case value_t::number_unsigned: { - numtostr(m_value.number_unsigned, o); + o << numtostr(m_value.number_unsigned).c_str(); return; } case value_t::number_float: { - numtostr(m_value.number_float, o); + o << numtostr(m_value.number_float).c_str(); return; }