From af070744aecf944598c8655f5460779a63e66b40 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
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<typename NumberType>
+        void x_write(NumberType x)
         {
-          public:
-            template<typename NumberType>
-            numtostr(NumberType value)
+            // special case for "0"
+            if (x == 0)
             {
-                x_write(value, std::is_integral<NumberType>());
+                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<long>(x % 10));
+                m_buf[i++] = static_cast<char>('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<typename NumberType>
-            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<long>(x % 10));
-                    m_buf[i++] = static_cast<char>('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<typename NumberType>
-            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<std::streamsize>(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<NumberType>::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<size_t>(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<number_float_t>::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<size_t>(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<size_t>(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<std::streamsize>(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<unsigned int>(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<typename NumberType>
+        void x_write(NumberType x)
         {
-          public:
-            template<typename NumberType>
-            numtostr(NumberType value)
+            // special case for "0"
+            if (x == 0)
             {
-                x_write(value, std::is_integral<NumberType>());
+                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<long>(x % 10));
+                m_buf[i++] = static_cast<char>('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<typename NumberType>
-            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<long>(x % 10));
-                    m_buf[i++] = static_cast<char>('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<typename NumberType>
-            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<std::streamsize>(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<NumberType>::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<size_t>(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<number_float_t>::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<size_t>(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<size_t>(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<std::streamsize>(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: