From 9ff0cc0f0212cb1e85b83ce5d2907ba9531b6cae Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sat, 25 Feb 2017 16:43:15 +0100
Subject: [PATCH 01/23] :memo: updated documentation

---
 README.md                | 2 +-
 doc/examples/README.link | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 1334180b..8dea44aa 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 [![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json)
 [![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json)
 [![Codacy Badge](https://api.codacy.com/project/badge/Grade/f3732b3327e34358a0e9d1fe9f661f08)](https://www.codacy.com/app/nlohmann/json?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=nlohmann/json&amp;utm_campaign=Badge_Grade)
-[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/4NEU6ZZMoM9lpIex)
+[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/nv9fOg0XVVhWmFFy)
 [![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json)
 [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT)
 [![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases)
diff --git a/doc/examples/README.link b/doc/examples/README.link
index d0168aab..a774c8ff 100644
--- a/doc/examples/README.link
+++ b/doc/examples/README.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/4NEU6ZZMoM9lpIex"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/nv9fOg0XVVhWmFFy"><b>online</b></a>
\ No newline at end of file

From f1cd15ce7e66518007101e73bb91c87b27c13512 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sun, 26 Feb 2017 11:22:18 +0100
Subject: [PATCH 02/23] :zap: avoid copying a string

---
 src/json.hpp      | 2 +-
 src/json.hpp.re2c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index 6dfc1831..6be0a2bf 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -12477,7 +12477,7 @@ basic_json_parser_74:
         // the valid JSON Patch operations
         enum class patch_operations {add, remove, replace, move, copy, test, invalid};
 
-        const auto get_op = [](const std::string op)
+        const auto get_op = [](const std::string & op)
         {
             if (op == "add")
             {
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index a42daba6..0d6e9dab 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -11511,7 +11511,7 @@ class basic_json
         // the valid JSON Patch operations
         enum class patch_operations {add, remove, replace, move, copy, test, invalid};
 
-        const auto get_op = [](const std::string op)
+        const auto get_op = [](const std::string & op)
         {
             if (op == "add")
             {

From d1b30250d6c75cc5ad521a7ddb2bf18563b4721c Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sun, 26 Feb 2017 11:50:52 +0100
Subject: [PATCH 03/23] :white_check_mark: added missing tests

---
 test/src/unit-constructor1.cpp | 9 +++++++++
 test/src/unit-conversions.cpp  | 4 ++++
 test/src/unit-regression.cpp   | 5 ++++-
 3 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp
index 6bba7019..b3354b58 100644
--- a/test/src/unit-constructor1.cpp
+++ b/test/src/unit-constructor1.cpp
@@ -699,6 +699,15 @@ TEST_CASE("constructors")
             json j(n);
             CHECK(j.type() == json::value_t::number_float);
         }
+
+        SECTION("infinity")
+        {
+            // infinity is stored as null
+            // should change in the future: https://github.com/nlohmann/json/issues/388
+            json::number_float_t n(std::numeric_limits<json::number_float_t>::infinity());
+            json j(n);
+            CHECK(j.type() == json::value_t::null);
+        }
     }
 
     SECTION("create a floating-point number (implicit)")
diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp
index 106e37de..e4545fbe 100644
--- a/test/src/unit-conversions.cpp
+++ b/test/src/unit-conversions.cpp
@@ -182,6 +182,10 @@ TEST_CASE("value conversion")
                 std::vector<float> v;
                 CHECK_THROWS_AS(nlohmann::from_json(j, v), std::logic_error);
                 CHECK(v.capacity() == j.size());
+
+                // make sure all values are properly copied
+                std::vector<int> v2 = json({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
+                CHECK(v2.size() == 10);
             }
 #endif
         }
diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp
index 83bb371a..7980371b 100644
--- a/test/src/unit-regression.cpp
+++ b/test/src/unit-regression.cpp
@@ -63,7 +63,7 @@ TEST_CASE("regression tests")
 
     SECTION("pull request #71 - handle enum type")
     {
-        enum { t = 0, u = 1};
+        enum { t = 0, u = 102};
         json j = json::array();
         j.push_back(t);
 
@@ -73,6 +73,9 @@ TEST_CASE("regression tests")
         auto anon_enum_value = j2.get<decltype(u)>();
         CHECK(u == anon_enum_value);
 
+        // check if the actual value was stored
+        CHECK(j2 == 102);
+
         static_assert(std::is_same<decltype(anon_enum_value), decltype(u)>::value, "");
 
         j.push_back(json::object(

From bf4d744d1a31b83eaaf7ae8ffa6778400d8d3340 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sun, 26 Feb 2017 14:34:58 +0100
Subject: [PATCH 04/23] :white_check_mark: more tests for meta() call

---
 test/src/unit-meta.cpp | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/test/src/unit-meta.cpp b/test/src/unit-meta.cpp
index 8c614a6c..2e4cce06 100644
--- a/test/src/unit-meta.cpp
+++ b/test/src/unit-meta.cpp
@@ -33,10 +33,14 @@ using nlohmann::json;
 
 TEST_CASE("version information")
 {
-    SECTION("version()")
+    SECTION("meta()")
     {
-        CHECK(json::meta()["name"] == "JSON for Modern C++");
-        CHECK(json::meta()["version"] == json(
+        json j = json::meta();
+
+        CHECK(j["name"] == "JSON for Modern C++");
+        CHECK(j["copyright"] == "(C) 2013-2017 Niels Lohmann");
+        CHECK(j["url"] == "https://github.com/nlohmann/json");
+        CHECK(j["version"] == json(
         {
             {"string", "2.1.1"},
             {"major", 2},

From ae155c47347d8412c11b5888727a76b4b24fcd3a Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sun, 26 Feb 2017 14:45:41 +0100
Subject: [PATCH 05/23] :lipstick: cleanup

---
 src/json.hpp      | 5 +----
 src/json.hpp.re2c | 5 +----
 2 files changed, 2 insertions(+), 8 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index 6be0a2bf..7d2433d4 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -1136,10 +1136,7 @@ class basic_json
         result["url"] = "https://github.com/nlohmann/json";
         result["version"] =
         {
-            {"string", "2.1.1"},
-            {"major", 2},
-            {"minor", 1},
-            {"patch", 1}
+            {"string", "2.1.1"}, {"major", 2}, {"minor", 1}, {"patch", 1}
         };
 
 #ifdef _WIN32
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 0d6e9dab..5fa876d7 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -1136,10 +1136,7 @@ class basic_json
         result["url"] = "https://github.com/nlohmann/json";
         result["version"] =
         {
-            {"string", "2.1.1"},
-            {"major", 2},
-            {"minor", 1},
-            {"patch", 1}
+            {"string", "2.1.1"}, {"major", 2}, {"minor", 1}, {"patch", 1}
         };
 
 #ifdef _WIN32

From bd0326cbc108326f7ebc5f0af049a60aa5a1c87d Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sun, 26 Feb 2017 16:55:54 +0100
Subject: [PATCH 06/23] :zap: micro-optimizations for dump()

Added separate code paths for normal output and pritty-printed output.
This allowed to remove most of the ifs along the way. Benchmarks and
cachegrind suggest a 10% performance improvement.
---
 src/json.hpp      | 96 ++++++++++++++++++++++++++---------------------
 src/json.hpp.re2c | 96 ++++++++++++++++++++++++++---------------------
 2 files changed, 108 insertions(+), 84 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index 7d2433d4..3c41b41a 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -8414,9 +8414,6 @@ class basic_json
               const unsigned int indent_step,
               const unsigned int current_indent = 0) const
     {
-        // variable to hold indentation for recursive calls
-        unsigned int new_indent = current_indent;
-
         switch (m_type)
         {
             case value_t::object:
@@ -8427,35 +8424,43 @@ class basic_json
                     return;
                 }
 
-                o << "{";
-
-                // increase indentation
                 if (pretty_print)
                 {
-                    new_indent += indent_step;
-                    o << "\n";
-                }
+                    o << "{\n";
 
-                for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i)
-                {
-                    if (i != m_value.object->cbegin())
+                    // variable to hold indentation for recursive calls
+                    const auto new_indent = current_indent + indent_step;
+                    const std::string indent_string = string_t(new_indent, ' ');
+
+                    for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i)
                     {
-                        o << (pretty_print ? ",\n" : ",");
+                        if (i != m_value.object->cbegin())
+                        {
+                            o << ",\n";
+                        }
+                        o << indent_string << '\"' << escape_string(i->first) << "\": ";
+                        i->second.dump(o, true, indent_step, new_indent);
                     }
-                    o << string_t(new_indent, ' ') << "\""
-                      << escape_string(i->first) << "\":"
-                      << (pretty_print ? " " : "");
-                    i->second.dump(o, pretty_print, indent_step, new_indent);
-                }
 
-                // decrease indentation
-                if (pretty_print)
+                    o << '\n' << string_t(current_indent, ' ') + '}';
+                }
+                else
                 {
-                    new_indent -= indent_step;
-                    o << "\n";
+                    o << '{';
+
+                    for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i)
+                    {
+                        if (i != m_value.object->cbegin())
+                        {
+                            o << ',';
+                        }
+                        o << '\"' << escape_string(i->first) << "\":";
+                        i->second.dump(o, false, indent_step, current_indent);
+                    }
+
+                    o << '}';
                 }
 
-                o << string_t(new_indent, ' ') + "}";
                 return;
             }
 
@@ -8467,39 +8472,46 @@ class basic_json
                     return;
                 }
 
-                o << "[";
-
-                // increase indentation
                 if (pretty_print)
                 {
-                    new_indent += indent_step;
-                    o << "\n";
-                }
+                    o << "[\n";
 
-                for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i)
-                {
-                    if (i != m_value.array->cbegin())
+                    // variable to hold indentation for recursive calls
+                    const auto new_indent = current_indent + indent_step;
+                    const std::string indent_string = string_t(new_indent, ' ');
+
+                    for (auto i = m_value.array->cbegin(); i != m_value.array->cend() - 1; ++i)
                     {
-                        o << (pretty_print ? ",\n" : ",");
+                        o << indent_string;
+                        i->dump(o, true, indent_step, new_indent);
+                        o << ",\n";
                     }
-                    o << string_t(new_indent, ' ');
-                    i->dump(o, pretty_print, indent_step, new_indent);
-                }
 
-                // decrease indentation
-                if (pretty_print)
+                    o << indent_string;
+                    assert(not m_value.array->empty());
+                    m_value.array->back().dump(o, true, indent_step, new_indent);
+
+                    o << '\n' << string_t(current_indent, ' ') << ']';
+                }
+                else
                 {
-                    new_indent -= indent_step;
-                    o << "\n";
+                    o << '[';
+                    for (auto i = m_value.array->cbegin(); i != m_value.array->cend() - 1; ++i)
+                    {
+                        i->dump(o, false, indent_step, current_indent);
+                        o << ',';
+                    }
+                    assert(not m_value.array->empty());
+                    m_value.array->back().dump(o, false, indent_step, current_indent);
+                    o << ']';
                 }
 
-                o << string_t(new_indent, ' ') << "]";
                 return;
             }
 
             case value_t::string:
             {
-                o << string_t("\"") << escape_string(*m_value.string) << "\"";
+                o << '\"' << escape_string(*m_value.string) << '\"';
                 return;
             }
 
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 5fa876d7..6412fe8b 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -8414,9 +8414,6 @@ class basic_json
               const unsigned int indent_step,
               const unsigned int current_indent = 0) const
     {
-        // variable to hold indentation for recursive calls
-        unsigned int new_indent = current_indent;
-
         switch (m_type)
         {
             case value_t::object:
@@ -8427,35 +8424,43 @@ class basic_json
                     return;
                 }
 
-                o << "{";
-
-                // increase indentation
                 if (pretty_print)
                 {
-                    new_indent += indent_step;
-                    o << "\n";
-                }
+                    o << "{\n";
 
-                for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i)
-                {
-                    if (i != m_value.object->cbegin())
+                    // variable to hold indentation for recursive calls
+                    const auto new_indent = current_indent + indent_step;
+                    const std::string indent_string = string_t(new_indent, ' ');
+
+                    for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i)
                     {
-                        o << (pretty_print ? ",\n" : ",");
+                        if (i != m_value.object->cbegin())
+                        {
+                            o << ",\n";
+                        }
+                        o << indent_string << '\"' << escape_string(i->first) << "\": ";
+                        i->second.dump(o, true, indent_step, new_indent);
                     }
-                    o << string_t(new_indent, ' ') << "\""
-                      << escape_string(i->first) << "\":"
-                      << (pretty_print ? " " : "");
-                    i->second.dump(o, pretty_print, indent_step, new_indent);
-                }
 
-                // decrease indentation
-                if (pretty_print)
+                    o << '\n' << string_t(current_indent, ' ') + '}';
+                }
+                else
                 {
-                    new_indent -= indent_step;
-                    o << "\n";
+                    o << '{';
+
+                    for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i)
+                    {
+                        if (i != m_value.object->cbegin())
+                        {
+                            o << ',';
+                        }
+                        o << '\"' << escape_string(i->first) << "\":";
+                        i->second.dump(o, false, indent_step, current_indent);
+                    }
+
+                    o << '}';
                 }
 
-                o << string_t(new_indent, ' ') + "}";
                 return;
             }
 
@@ -8467,39 +8472,46 @@ class basic_json
                     return;
                 }
 
-                o << "[";
-
-                // increase indentation
                 if (pretty_print)
                 {
-                    new_indent += indent_step;
-                    o << "\n";
-                }
+                    o << "[\n";
 
-                for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i)
-                {
-                    if (i != m_value.array->cbegin())
+                    // variable to hold indentation for recursive calls
+                    const auto new_indent = current_indent + indent_step;
+                    const std::string indent_string = string_t(new_indent, ' ');
+
+                    for (auto i = m_value.array->cbegin(); i != m_value.array->cend() - 1; ++i)
                     {
-                        o << (pretty_print ? ",\n" : ",");
+                        o << indent_string;
+                        i->dump(o, true, indent_step, new_indent);
+                        o << ",\n";
                     }
-                    o << string_t(new_indent, ' ');
-                    i->dump(o, pretty_print, indent_step, new_indent);
-                }
 
-                // decrease indentation
-                if (pretty_print)
+                    o << indent_string;
+                    assert(not m_value.array->empty());
+                    m_value.array->back().dump(o, true, indent_step, new_indent);
+
+                    o << '\n' << string_t(current_indent, ' ') << ']';
+                }
+                else
                 {
-                    new_indent -= indent_step;
-                    o << "\n";
+                    o << '[';
+                    for (auto i = m_value.array->cbegin(); i != m_value.array->cend() - 1; ++i)
+                    {
+                        i->dump(o, false, indent_step, current_indent);
+                        o << ',';
+                    }
+                    assert(not m_value.array->empty());
+                    m_value.array->back().dump(o, false, indent_step, current_indent);
+                    o << ']';
                 }
 
-                o << string_t(new_indent, ' ') << "]";
                 return;
             }
 
             case value_t::string:
             {
-                o << string_t("\"") << escape_string(*m_value.string) << "\"";
+                o << '\"' << escape_string(*m_value.string) << '\"';
                 return;
             }
 

From b1441f3485355e774f178cce7e2b645fd3f20943 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sun, 26 Feb 2017 20:58:00 +0100
Subject: [PATCH 07/23] :zap: micro-optimizations for dump()

Indentation string is recycled to avoid allocations. Comma-separation
in objects does not need an if any more. Cachegrind measures 1%
performance improvement.
---
 src/json.hpp      | 31 +++++++++++++++++--------------
 src/json.hpp.re2c | 31 +++++++++++++++++--------------
 2 files changed, 34 insertions(+), 28 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index 3c41b41a..3a638d76 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -8430,34 +8430,36 @@ class basic_json
 
                     // variable to hold indentation for recursive calls
                     const auto new_indent = current_indent + indent_step;
-                    const std::string indent_string = string_t(new_indent, ' ');
+                    string_t indent_string = string_t(new_indent, ' ');
 
-                    for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i)
+                    auto i = m_value.object->cbegin();
+                    for (size_t cnt = 0; cnt < m_value.object->size() - 1; ++cnt, ++i)
                     {
-                        if (i != m_value.object->cbegin())
-                        {
-                            o << ",\n";
-                        }
                         o << indent_string << '\"' << escape_string(i->first) << "\": ";
                         i->second.dump(o, true, indent_step, new_indent);
+                        o << ",\n";
                     }
 
-                    o << '\n' << string_t(current_indent, ' ') + '}';
+                    o << indent_string << '\"' << escape_string(i->first) << "\": ";
+                    i->second.dump(o, true, indent_step, new_indent);
+
+                    indent_string.resize(current_indent);
+                    o << '\n' << indent_string << '}';
                 }
                 else
                 {
                     o << '{';
 
-                    for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i)
+                    auto i = m_value.object->cbegin();
+                    for (size_t cnt = 0; cnt < m_value.object->size() - 1; ++cnt, ++i)
                     {
-                        if (i != m_value.object->cbegin())
-                        {
-                            o << ',';
-                        }
                         o << '\"' << escape_string(i->first) << "\":";
                         i->second.dump(o, false, indent_step, current_indent);
+                        o << ',';
                     }
 
+                    o << '\"' << escape_string(i->first) << "\":";
+                    i->second.dump(o, false, indent_step, current_indent);
                     o << '}';
                 }
 
@@ -8478,7 +8480,7 @@ class basic_json
 
                     // variable to hold indentation for recursive calls
                     const auto new_indent = current_indent + indent_step;
-                    const std::string indent_string = string_t(new_indent, ' ');
+                    string_t indent_string = string_t(new_indent, ' ');
 
                     for (auto i = m_value.array->cbegin(); i != m_value.array->cend() - 1; ++i)
                     {
@@ -8491,7 +8493,8 @@ class basic_json
                     assert(not m_value.array->empty());
                     m_value.array->back().dump(o, true, indent_step, new_indent);
 
-                    o << '\n' << string_t(current_indent, ' ') << ']';
+                    indent_string.resize(current_indent);
+                    o << '\n' << indent_string << ']';
                 }
                 else
                 {
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 6412fe8b..796f8c56 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -8430,34 +8430,36 @@ class basic_json
 
                     // variable to hold indentation for recursive calls
                     const auto new_indent = current_indent + indent_step;
-                    const std::string indent_string = string_t(new_indent, ' ');
+                    string_t indent_string = string_t(new_indent, ' ');
 
-                    for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i)
+                    auto i = m_value.object->cbegin();
+                    for (size_t cnt = 0; cnt < m_value.object->size() - 1; ++cnt, ++i)
                     {
-                        if (i != m_value.object->cbegin())
-                        {
-                            o << ",\n";
-                        }
                         o << indent_string << '\"' << escape_string(i->first) << "\": ";
                         i->second.dump(o, true, indent_step, new_indent);
+                        o << ",\n";
                     }
 
-                    o << '\n' << string_t(current_indent, ' ') + '}';
+                    o << indent_string << '\"' << escape_string(i->first) << "\": ";
+                    i->second.dump(o, true, indent_step, new_indent);
+
+                    indent_string.resize(current_indent);
+                    o << '\n' << indent_string << '}';
                 }
                 else
                 {
                     o << '{';
 
-                    for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i)
+                    auto i = m_value.object->cbegin();
+                    for (size_t cnt = 0; cnt < m_value.object->size() - 1; ++cnt, ++i)
                     {
-                        if (i != m_value.object->cbegin())
-                        {
-                            o << ',';
-                        }
                         o << '\"' << escape_string(i->first) << "\":";
                         i->second.dump(o, false, indent_step, current_indent);
+                        o << ',';
                     }
 
+                    o << '\"' << escape_string(i->first) << "\":";
+                    i->second.dump(o, false, indent_step, current_indent);
                     o << '}';
                 }
 
@@ -8478,7 +8480,7 @@ class basic_json
 
                     // variable to hold indentation for recursive calls
                     const auto new_indent = current_indent + indent_step;
-                    const std::string indent_string = string_t(new_indent, ' ');
+                    string_t indent_string = string_t(new_indent, ' ');
 
                     for (auto i = m_value.array->cbegin(); i != m_value.array->cend() - 1; ++i)
                     {
@@ -8491,7 +8493,8 @@ class basic_json
                     assert(not m_value.array->empty());
                     m_value.array->back().dump(o, true, indent_step, new_indent);
 
-                    o << '\n' << string_t(current_indent, ' ') << ']';
+                    indent_string.resize(current_indent);
+                    o << '\n' << indent_string << ']';
                 }
                 else
                 {

From 0f04e42dd5ee53af466e25a82667fe4c3b039775 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Mon, 27 Feb 2017 01:22:24 +0100
Subject: [PATCH 08/23] :zap: micro-optimizations for dump()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

All ‘<<‘ calls have been replaced by write()/put() calls. The
indentation strings needs not to be resized. Cachegrind measures 1%
performance improvement.
---
 src/json.hpp      | 92 +++++++++++++++++++++++++++++++++--------------
 src/json.hpp.re2c | 92 +++++++++++++++++++++++++++++++++--------------
 2 files changed, 132 insertions(+), 52 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index 3a638d76..1094aade 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -8420,47 +8420,69 @@ class basic_json
             {
                 if (m_value.object->empty())
                 {
-                    o << "{}";
+                    o.write("{}", 2);
                     return;
                 }
 
                 if (pretty_print)
                 {
-                    o << "{\n";
+                    o.write("{\n", 2);
 
                     // variable to hold indentation for recursive calls
                     const auto new_indent = current_indent + indent_step;
                     string_t indent_string = string_t(new_indent, ' ');
 
+                    // first n-1 elements
                     auto i = m_value.object->cbegin();
                     for (size_t cnt = 0; cnt < m_value.object->size() - 1; ++cnt, ++i)
                     {
-                        o << indent_string << '\"' << escape_string(i->first) << "\": ";
+                        o.write(indent_string.c_str(), new_indent);
+                        o.put('\"');
+                        const auto s = escape_string(i->first);
+                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                        o.write("\": ", 3);
                         i->second.dump(o, true, indent_step, new_indent);
-                        o << ",\n";
+                        o.write(",\n", 2);
                     }
 
-                    o << indent_string << '\"' << escape_string(i->first) << "\": ";
+                    // last element
+                    assert(i != m_value.object->cend());
+                    o.write(indent_string.c_str(), new_indent);
+                    o.put('\"');
+                    const auto s = escape_string(i->first);
+                    o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                    o.write("\": ", 3);
                     i->second.dump(o, true, indent_step, new_indent);
 
-                    indent_string.resize(current_indent);
-                    o << '\n' << indent_string << '}';
+                    o.put('\n');
+                    o.write(indent_string.c_str(), current_indent);
+                    o.put('}');
                 }
                 else
                 {
-                    o << '{';
+                    o.put('{');
 
+                    // first n-1 elements
                     auto i = m_value.object->cbegin();
                     for (size_t cnt = 0; cnt < m_value.object->size() - 1; ++cnt, ++i)
                     {
-                        o << '\"' << escape_string(i->first) << "\":";
+                        o.put('\"');
+                        const auto s = escape_string(i->first);
+                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                        o.write("\":", 2);
                         i->second.dump(o, false, indent_step, current_indent);
-                        o << ',';
+                        o.put(',');
                     }
 
-                    o << '\"' << escape_string(i->first) << "\":";
+                    // last element
+                    assert(i != m_value.object->cend());
+                    o.put('\"');
+                    const auto s = escape_string(i->first);
+                    o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                    o.write("\":", 2);
                     i->second.dump(o, false, indent_step, current_indent);
-                    o << '}';
+
+                    o.put('}');
                 }
 
                 return;
@@ -8470,43 +8492,51 @@ class basic_json
             {
                 if (m_value.array->empty())
                 {
-                    o << "[]";
+                    o.write("[]", 2);
                     return;
                 }
 
                 if (pretty_print)
                 {
-                    o << "[\n";
+                    o.write("[\n", 2);
 
                     // variable to hold indentation for recursive calls
                     const auto new_indent = current_indent + indent_step;
                     string_t indent_string = string_t(new_indent, ' ');
 
+                    // first n-1 elements
                     for (auto i = m_value.array->cbegin(); i != m_value.array->cend() - 1; ++i)
                     {
-                        o << indent_string;
+                        o.write(indent_string.c_str(), new_indent);
                         i->dump(o, true, indent_step, new_indent);
-                        o << ",\n";
+                        o.write(",\n", 2);
                     }
 
-                    o << indent_string;
+                    // last element
                     assert(not m_value.array->empty());
+                    o.write(indent_string.c_str(), new_indent);
                     m_value.array->back().dump(o, true, indent_step, new_indent);
 
-                    indent_string.resize(current_indent);
-                    o << '\n' << indent_string << ']';
+                    o.put('\n');
+                    o.write(indent_string.c_str(), current_indent);
+                    o.put(']');
                 }
                 else
                 {
-                    o << '[';
+                    o.put('[');
+
+                    // first n-1 elements
                     for (auto i = m_value.array->cbegin(); i != m_value.array->cend() - 1; ++i)
                     {
                         i->dump(o, false, indent_step, current_indent);
-                        o << ',';
+                        o.put(',');
                     }
+
+                    // last element
                     assert(not m_value.array->empty());
                     m_value.array->back().dump(o, false, indent_step, current_indent);
-                    o << ']';
+
+                    o.put(']');
                 }
 
                 return;
@@ -8514,13 +8544,23 @@ class basic_json
 
             case value_t::string:
             {
-                o << '\"' << escape_string(*m_value.string) << '\"';
+                o.put('\"');
+                const auto s = escape_string(*m_value.string);
+                o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                o.put('\"');
                 return;
             }
 
             case value_t::boolean:
             {
-                o << (m_value.boolean ? "true" : "false");
+                if (m_value.boolean)
+                {
+                    o.write("true", 4);
+                }
+                else
+                {
+                    o.write("false", 5);
+                }
                 return;
             }
 
@@ -8544,13 +8584,13 @@ class basic_json
 
             case value_t::discarded:
             {
-                o << "<discarded>";
+                o.write("<discarded>", 11);
                 return;
             }
 
             case value_t::null:
             {
-                o << "null";
+                o.write("null", 4);
                 return;
             }
         }
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 796f8c56..a686a1a7 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -8420,47 +8420,69 @@ class basic_json
             {
                 if (m_value.object->empty())
                 {
-                    o << "{}";
+                    o.write("{}", 2);
                     return;
                 }
 
                 if (pretty_print)
                 {
-                    o << "{\n";
+                    o.write("{\n", 2);
 
                     // variable to hold indentation for recursive calls
                     const auto new_indent = current_indent + indent_step;
                     string_t indent_string = string_t(new_indent, ' ');
 
+                    // first n-1 elements
                     auto i = m_value.object->cbegin();
                     for (size_t cnt = 0; cnt < m_value.object->size() - 1; ++cnt, ++i)
                     {
-                        o << indent_string << '\"' << escape_string(i->first) << "\": ";
+                        o.write(indent_string.c_str(), new_indent);
+                        o.put('\"');
+                        const auto s = escape_string(i->first);
+                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                        o.write("\": ", 3);
                         i->second.dump(o, true, indent_step, new_indent);
-                        o << ",\n";
+                        o.write(",\n", 2);
                     }
 
-                    o << indent_string << '\"' << escape_string(i->first) << "\": ";
+                    // last element
+                    assert(i != m_value.object->cend());
+                    o.write(indent_string.c_str(), new_indent);
+                    o.put('\"');
+                    const auto s = escape_string(i->first);
+                    o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                    o.write("\": ", 3);
                     i->second.dump(o, true, indent_step, new_indent);
 
-                    indent_string.resize(current_indent);
-                    o << '\n' << indent_string << '}';
+                    o.put('\n');
+                    o.write(indent_string.c_str(), current_indent);
+                    o.put('}');
                 }
                 else
                 {
-                    o << '{';
+                    o.put('{');
 
+                    // first n-1 elements
                     auto i = m_value.object->cbegin();
                     for (size_t cnt = 0; cnt < m_value.object->size() - 1; ++cnt, ++i)
                     {
-                        o << '\"' << escape_string(i->first) << "\":";
+                        o.put('\"');
+                        const auto s = escape_string(i->first);
+                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                        o.write("\":", 2);
                         i->second.dump(o, false, indent_step, current_indent);
-                        o << ',';
+                        o.put(',');
                     }
 
-                    o << '\"' << escape_string(i->first) << "\":";
+                    // last element
+                    assert(i != m_value.object->cend());
+                    o.put('\"');
+                    const auto s = escape_string(i->first);
+                    o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                    o.write("\":", 2);
                     i->second.dump(o, false, indent_step, current_indent);
-                    o << '}';
+
+                    o.put('}');
                 }
 
                 return;
@@ -8470,43 +8492,51 @@ class basic_json
             {
                 if (m_value.array->empty())
                 {
-                    o << "[]";
+                    o.write("[]", 2);
                     return;
                 }
 
                 if (pretty_print)
                 {
-                    o << "[\n";
+                    o.write("[\n", 2);
 
                     // variable to hold indentation for recursive calls
                     const auto new_indent = current_indent + indent_step;
                     string_t indent_string = string_t(new_indent, ' ');
 
+                    // first n-1 elements
                     for (auto i = m_value.array->cbegin(); i != m_value.array->cend() - 1; ++i)
                     {
-                        o << indent_string;
+                        o.write(indent_string.c_str(), new_indent);
                         i->dump(o, true, indent_step, new_indent);
-                        o << ",\n";
+                        o.write(",\n", 2);
                     }
 
-                    o << indent_string;
+                    // last element
                     assert(not m_value.array->empty());
+                    o.write(indent_string.c_str(), new_indent);
                     m_value.array->back().dump(o, true, indent_step, new_indent);
 
-                    indent_string.resize(current_indent);
-                    o << '\n' << indent_string << ']';
+                    o.put('\n');
+                    o.write(indent_string.c_str(), current_indent);
+                    o.put(']');
                 }
                 else
                 {
-                    o << '[';
+                    o.put('[');
+
+                    // first n-1 elements
                     for (auto i = m_value.array->cbegin(); i != m_value.array->cend() - 1; ++i)
                     {
                         i->dump(o, false, indent_step, current_indent);
-                        o << ',';
+                        o.put(',');
                     }
+
+                    // last element
                     assert(not m_value.array->empty());
                     m_value.array->back().dump(o, false, indent_step, current_indent);
-                    o << ']';
+
+                    o.put(']');
                 }
 
                 return;
@@ -8514,13 +8544,23 @@ class basic_json
 
             case value_t::string:
             {
-                o << '\"' << escape_string(*m_value.string) << '\"';
+                o.put('\"');
+                const auto s = escape_string(*m_value.string);
+                o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                o.put('\"');
                 return;
             }
 
             case value_t::boolean:
             {
-                o << (m_value.boolean ? "true" : "false");
+                if (m_value.boolean)
+                {
+                    o.write("true", 4);
+                }
+                else
+                {
+                    o.write("false", 5);
+                }
                 return;
             }
 
@@ -8544,13 +8584,13 @@ class basic_json
 
             case value_t::discarded:
             {
-                o << "<discarded>";
+                o.write("<discarded>", 11);
                 return;
             }
 
             case value_t::null:
             {
-                o << "null";
+                o.write("null", 4);
                 return;
             }
         }

From 909b439b03dd1aa048eeaef82c5abfce85c6a56a Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Mon, 27 Feb 2017 14:58:10 +0100
Subject: [PATCH 09/23] :zap: micro-optimizations for dump()

numtostr now directly writes to a stream. Return value of snprintf is
reused to avoid finding end of string. Cachegrind suggests a 1%
performance increase.
---
 src/json.hpp      | 72 +++++++++++++++++------------------------------
 src/json.hpp.re2c | 72 +++++++++++++++++------------------------------
 2 files changed, 52 insertions(+), 92 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index 1094aade..0f452706 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -8252,14 +8252,9 @@ class basic_json
     {
       public:
         template<typename NumberType>
-        numtostr(NumberType value)
+        numtostr(NumberType value, std::ostream& o)
         {
-            x_write(value, std::is_integral<NumberType>());
-        }
-
-        const char* c_str() const
-        {
-            return m_buf.data();
+            x_write(value, std::is_integral<NumberType>(), o);
         }
 
       private:
@@ -8267,12 +8262,12 @@ class basic_json
         std::array < char, 64 > m_buf{{}};
 
         template<typename NumberType>
-        void x_write(NumberType x, /*is_integral=*/std::true_type)
+        void x_write(NumberType x, /*is_integral=*/std::true_type, std::ostream& o)
         {
             // special case for "0"
             if (x == 0)
             {
-                m_buf[0] = '0';
+                o.put('0');
                 return;
             }
 
@@ -8298,30 +8293,31 @@ class basic_json
             }
 
             std::reverse(m_buf.begin(), m_buf.begin() + i);
+            o.write(m_buf.data(), static_cast<std::streamsize>(i));
         }
 
         template<typename NumberType>
-        void x_write(NumberType x, /*is_integral=*/std::false_type)
+        void x_write(NumberType x, /*is_integral=*/std::false_type, std::ostream& o)
         {
             // special case for 0.0 and -0.0
             if (x == 0)
             {
-                size_t i = 0;
                 if (std::signbit(x))
                 {
-                    m_buf[i++] = '-';
+                    o.write("-0.0", 4);
+                }
+                else
+                {
+                    o.write("0.0", 3);
                 }
-                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<NumberType>::digits10;
 
-            // the actual conversion
-            const auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
+            // the actual conversion; store how many bytes have been written
+            auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
 
             // negative value indicates an error
             assert(written_bytes > 0);
@@ -8342,6 +8338,7 @@ 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 '.'
@@ -8357,36 +8354,19 @@ class basic_json
                 }
             }
 
+            o.write(m_buf.data(), static_cast<std::streamsize>(written_bytes));
+
             // determine if need to append ".0"
-            size_t i = 0;
-            bool value_is_int_like = true;
-            for (i = 0; i < m_buf.size(); ++i)
+            const bool value_is_int_like = std::all_of(m_buf.begin(),
+                                           m_buf.begin() + written_bytes + 1,
+                                           [](char c)
             {
-                // 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';
-            }
-
+                // we use %g above, so there cannot be an 'E' character
+                return c != '.' and c != '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');
+                o.write(".0", 2);
             }
         }
     };
@@ -8566,19 +8546,19 @@ class basic_json
 
             case value_t::number_integer:
             {
-                o << numtostr(m_value.number_integer).c_str();
+                numtostr(m_value.number_integer, o);
                 return;
             }
 
             case value_t::number_unsigned:
             {
-                o << numtostr(m_value.number_unsigned).c_str();
+                numtostr(m_value.number_unsigned, o);
                 return;
             }
 
             case value_t::number_float:
             {
-                o << numtostr(m_value.number_float).c_str();
+                numtostr(m_value.number_float, o);
                 return;
             }
 
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index a686a1a7..b4b9e3d2 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -8252,14 +8252,9 @@ class basic_json
     {
       public:
         template<typename NumberType>
-        numtostr(NumberType value)
+        numtostr(NumberType value, std::ostream& o)
         {
-            x_write(value, std::is_integral<NumberType>());
-        }
-
-        const char* c_str() const
-        {
-            return m_buf.data();
+            x_write(value, std::is_integral<NumberType>(), o);
         }
 
       private:
@@ -8267,12 +8262,12 @@ class basic_json
         std::array < char, 64 > m_buf{{}};
 
         template<typename NumberType>
-        void x_write(NumberType x, /*is_integral=*/std::true_type)
+        void x_write(NumberType x, /*is_integral=*/std::true_type, std::ostream& o)
         {
             // special case for "0"
             if (x == 0)
             {
-                m_buf[0] = '0';
+                o.put('0');
                 return;
             }
 
@@ -8298,30 +8293,31 @@ class basic_json
             }
 
             std::reverse(m_buf.begin(), m_buf.begin() + i);
+            o.write(m_buf.data(), static_cast<std::streamsize>(i));
         }
 
         template<typename NumberType>
-        void x_write(NumberType x, /*is_integral=*/std::false_type)
+        void x_write(NumberType x, /*is_integral=*/std::false_type, std::ostream& o)
         {
             // special case for 0.0 and -0.0
             if (x == 0)
             {
-                size_t i = 0;
                 if (std::signbit(x))
                 {
-                    m_buf[i++] = '-';
+                    o.write("-0.0", 4);
+                }
+                else
+                {
+                    o.write("0.0", 3);
                 }
-                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<NumberType>::digits10;
 
-            // the actual conversion
-            const auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
+            // the actual conversion; store how many bytes have been written
+            auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
 
             // negative value indicates an error
             assert(written_bytes > 0);
@@ -8342,6 +8338,7 @@ 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 '.'
@@ -8357,36 +8354,19 @@ class basic_json
                 }
             }
 
+            o.write(m_buf.data(), static_cast<std::streamsize>(written_bytes));
+
             // determine if need to append ".0"
-            size_t i = 0;
-            bool value_is_int_like = true;
-            for (i = 0; i < m_buf.size(); ++i)
+            const bool value_is_int_like = std::all_of(m_buf.begin(),
+                                           m_buf.begin() + written_bytes + 1,
+                                           [](char c)
             {
-                // 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';
-            }
-
+                // we use %g above, so there cannot be an 'E' character
+                return c != '.' and c != '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');
+                o.write(".0", 2);
             }
         }
     };
@@ -8566,19 +8546,19 @@ class basic_json
 
             case value_t::number_integer:
             {
-                o << numtostr(m_value.number_integer).c_str();
+                numtostr(m_value.number_integer, o);
                 return;
             }
 
             case value_t::number_unsigned:
             {
-                o << numtostr(m_value.number_unsigned).c_str();
+                numtostr(m_value.number_unsigned, o);
                 return;
             }
 
             case value_t::number_float:
             {
-                o << numtostr(m_value.number_float).c_str();
+                numtostr(m_value.number_float, o);
                 return;
             }
 

From 9c4919ff34a78dfd0dd4307518716e60399ad37a Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Mon, 27 Feb 2017 16:19:07 +0100
Subject: [PATCH 10/23] :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<typename NumberType>
-        numtostr(NumberType value, std::ostream& o)
+        numtostr(NumberType value)
         {
-            x_write(value, std::is_integral<NumberType>(), o);
+            x_write(value, std::is_integral<NumberType>());
+        }
+
+        const char* c_str() const
+        {
+            return m_buf.data();
         }
 
       private:
@@ -8262,12 +8267,12 @@ class basic_json
         std::array < char, 64 > m_buf{{}};
 
         template<typename NumberType>
-        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<std::streamsize>(i));
         }
 
         template<typename NumberType>
-        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<NumberType>::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<std::streamsize>(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<typename NumberType>
-        numtostr(NumberType value, std::ostream& o)
+        numtostr(NumberType value)
         {
-            x_write(value, std::is_integral<NumberType>(), o);
+            x_write(value, std::is_integral<NumberType>());
+        }
+
+        const char* c_str() const
+        {
+            return m_buf.data();
         }
 
       private:
@@ -8262,12 +8267,12 @@ class basic_json
         std::array < char, 64 > m_buf{{}};
 
         template<typename NumberType>
-        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<std::streamsize>(i));
         }
 
         template<typename NumberType>
-        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<NumberType>::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<std::streamsize>(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;
             }
 

From 54ef5f7b47276fc56637afbf083ee9e8a9b1ef44 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Mon, 27 Feb 2017 21:22:39 +0100
Subject: [PATCH 11/23] :hammer: moved serialization functions to serializer
 class

The class is currently just a wrapper for an std::ostream and collects
all functions related to serialization. The next step should be
recycling of variables to avoid repetitive initialization for each
recursive dump call.
---
 src/json.hpp                  | 1046 ++++++++++++++++----------------
 src/json.hpp.re2c             | 1050 +++++++++++++++++----------------
 test/src/unit-convenience.cpp |   76 +--
 3 files changed, 1101 insertions(+), 1071 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index 1094aade..a73b5048 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -2644,14 +2644,15 @@ class basic_json
     string_t dump(const int indent = -1) const
     {
         std::stringstream ss;
+        serializer s(ss);
 
         if (indent >= 0)
         {
-            dump(ss, true, static_cast<unsigned int>(indent));
+            s.dump(*this, true, static_cast<unsigned int>(indent));
         }
         else
         {
-            dump(ss, false, 0);
+            s.dump(*this, false, 0);
         }
 
         return ss.str();
@@ -6194,6 +6195,531 @@ class basic_json
     /// @name serialization
     /// @{
 
+  private:
+    class serializer
+    {
+      public:
+        serializer(std::ostream& s)
+            : o(s)
+        {}
+
+        /*!
+        @brief internal implementation of the serialization function
+
+        This function is called by the public member function dump and organizes
+        the serialization internally. The indentation level is propagated as
+        additional parameter. In case of arrays and objects, the function is
+        called recursively. Note that
+
+        - strings and object keys are escaped using `escape_string()`
+        - integer numbers are converted implicitly via `operator<<`
+        - floating-point numbers are converted to a string using `"%g"` format
+
+        @param[in] val             value to serialize
+        @param[in] pretty_print    whether the output shall be pretty-printed
+        @param[in] indent_step     the indent level
+        @param[in] current_indent  the current indent level (only used internally)
+        */
+        void dump(const basic_json& val,
+                  const bool pretty_print,
+                  const unsigned int indent_step,
+                  const unsigned int current_indent = 0) const
+        {
+            switch (val.m_type)
+            {
+                case value_t::object:
+                {
+                    if (val.m_value.object->empty())
+                    {
+                        o.write("{}", 2);
+                        return;
+                    }
+
+                    if (pretty_print)
+                    {
+                        o.write("{\n", 2);
+
+                        // variable to hold indentation for recursive calls
+                        const auto new_indent = current_indent + indent_step;
+                        string_t indent_string = string_t(new_indent, ' ');
+
+                        // first n-1 elements
+                        auto i = val.m_value.object->cbegin();
+                        for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
+                        {
+                            o.write(indent_string.c_str(), new_indent);
+                            o.put('\"');
+                            const auto s = escape_string(i->first);
+                            o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                            o.write("\": ", 3);
+                            dump(i->second, true, indent_step, new_indent);
+                            o.write(",\n", 2);
+                        }
+
+                        // last element
+                        assert(i != val.m_value.object->cend());
+                        o.write(indent_string.c_str(), new_indent);
+                        o.put('\"');
+                        const auto s = escape_string(i->first);
+                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                        o.write("\": ", 3);
+                        dump(i->second, true, indent_step, new_indent);
+
+                        o.put('\n');
+                        o.write(indent_string.c_str(), current_indent);
+                        o.put('}');
+                    }
+                    else
+                    {
+                        o.put('{');
+
+                        // first n-1 elements
+                        auto i = val.m_value.object->cbegin();
+                        for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
+                        {
+                            o.put('\"');
+                            const auto s = escape_string(i->first);
+                            o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                            o.write("\":", 2);
+                            dump(i->second, false, indent_step, current_indent);
+                            o.put(',');
+                        }
+
+                        // last element
+                        assert(i != val.m_value.object->cend());
+                        o.put('\"');
+                        const auto s = escape_string(i->first);
+                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                        o.write("\":", 2);
+                        dump(i->second, false, indent_step, current_indent);
+
+                        o.put('}');
+                    }
+
+                    return;
+                }
+
+                case value_t::array:
+                {
+                    if (val.m_value.array->empty())
+                    {
+                        o.write("[]", 2);
+                        return;
+                    }
+
+                    if (pretty_print)
+                    {
+                        o.write("[\n", 2);
+
+                        // variable to hold indentation for recursive calls
+                        const auto new_indent = current_indent + indent_step;
+                        string_t indent_string = string_t(new_indent, ' ');
+
+                        // first n-1 elements
+                        for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i)
+                        {
+                            o.write(indent_string.c_str(), new_indent);
+                            dump(*i, true, indent_step, new_indent);
+                            o.write(",\n", 2);
+                        }
+
+                        // last element
+                        assert(not val.m_value.array->empty());
+                        o.write(indent_string.c_str(), new_indent);
+                        dump(val.m_value.array->back(), true, indent_step, new_indent);
+
+                        o.put('\n');
+                        o.write(indent_string.c_str(), current_indent);
+                        o.put(']');
+                    }
+                    else
+                    {
+                        o.put('[');
+
+                        // first n-1 elements
+                        for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i)
+                        {
+                            dump(*i, false, indent_step, current_indent);
+                            o.put(',');
+                        }
+
+                        // last element
+                        assert(not val.m_value.array->empty());
+                        dump(val.m_value.array->back(), false, indent_step, current_indent);
+
+                        o.put(']');
+                    }
+
+                    return;
+                }
+
+                case value_t::string:
+                {
+                    o.put('\"');
+                    const auto s = escape_string(*val.m_value.string);
+                    o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                    o.put('\"');
+                    return;
+                }
+
+                case value_t::boolean:
+                {
+                    if (val.m_value.boolean)
+                    {
+                        o.write("true", 4);
+                    }
+                    else
+                    {
+                        o.write("false", 5);
+                    }
+                    return;
+                }
+
+                case value_t::number_integer:
+                {
+                    o << numtostr(val.m_value.number_integer).c_str();
+                    return;
+                }
+
+                case value_t::number_unsigned:
+                {
+                    o << numtostr(val.m_value.number_unsigned).c_str();
+                    return;
+                }
+
+                case value_t::number_float:
+                {
+                    o << numtostr(val.m_value.number_float).c_str();
+                    return;
+                }
+
+                case value_t::discarded:
+                {
+                    o.write("<discarded>", 11);
+                    return;
+                }
+
+                case value_t::null:
+                {
+                    o.write("null", 4);
+                    return;
+                }
+            }
+        }
+
+      private:
+        /*!
+        @brief calculates the extra space to escape a JSON string
+
+        @param[in] s  the string to escape
+        @return the number of characters required to escape string @a s
+
+        @complexity Linear in the length of string @a s.
+        */
+        static std::size_t extra_space(const string_t& s) noexcept
+        {
+            return std::accumulate(s.begin(), s.end(), size_t{},
+                                   [](size_t res, typename string_t::value_type c)
+            {
+                switch (c)
+                {
+                    case '"':
+                    case '\\':
+                    case '\b':
+                    case '\f':
+                    case '\n':
+                    case '\r':
+                    case '\t':
+                    {
+                        // from c (1 byte) to \x (2 bytes)
+                        return res + 1;
+                    }
+
+                    default:
+                    {
+                        if (c >= 0x00 and c <= 0x1f)
+                        {
+                            // from c (1 byte) to \uxxxx (6 bytes)
+                            return res + 5;
+                        }
+
+                        return res;
+                    }
+                }
+            });
+        }
+
+        /*!
+        @brief escape a string
+
+        Escape a string by replacing certain special characters by a sequence of
+        an escape character (backslash) and another character and other control
+        characters by a sequence of "\u" followed by a four-digit hex
+        representation.
+
+        @param[in] s  the string to escape
+        @return  the escaped string
+
+        @complexity Linear in the length of string @a s.
+        */
+        static string_t escape_string(const string_t& s)
+        {
+            const auto space = extra_space(s);
+            if (space == 0)
+            {
+                return s;
+            }
+
+            // create a result string of necessary size
+            string_t result(s.size() + space, '\\');
+            std::size_t pos = 0;
+
+            for (const auto& c : s)
+            {
+                switch (c)
+                {
+                    // quotation mark (0x22)
+                    case '"':
+                    {
+                        result[pos + 1] = '"';
+                        pos += 2;
+                        break;
+                    }
+
+                    // reverse solidus (0x5c)
+                    case '\\':
+                    {
+                        // nothing to change
+                        pos += 2;
+                        break;
+                    }
+
+                    // backspace (0x08)
+                    case '\b':
+                    {
+                        result[pos + 1] = 'b';
+                        pos += 2;
+                        break;
+                    }
+
+                    // formfeed (0x0c)
+                    case '\f':
+                    {
+                        result[pos + 1] = 'f';
+                        pos += 2;
+                        break;
+                    }
+
+                    // newline (0x0a)
+                    case '\n':
+                    {
+                        result[pos + 1] = 'n';
+                        pos += 2;
+                        break;
+                    }
+
+                    // carriage return (0x0d)
+                    case '\r':
+                    {
+                        result[pos + 1] = 'r';
+                        pos += 2;
+                        break;
+                    }
+
+                    // horizontal tab (0x09)
+                    case '\t':
+                    {
+                        result[pos + 1] = 't';
+                        pos += 2;
+                        break;
+                    }
+
+                    default:
+                    {
+                        if (c >= 0x00 and c <= 0x1f)
+                        {
+                            // convert a number 0..15 to its hex representation
+                            // (0..f)
+                            static const char hexify[16] =
+                            {
+                                '0', '1', '2', '3', '4', '5', '6', '7',
+                                '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+                            };
+
+                            // print character c as \uxxxx
+                            for (const char m :
+                        { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f]
+                            })
+                            {
+                                result[++pos] = m;
+                            }
+
+                            ++pos;
+                        }
+                        else
+                        {
+                            // all other characters are added as-is
+                            result[pos++] = c;
+                        }
+                        break;
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        /*!
+        @brief locale-independent serialization for built-in arithmetic types
+        */
+        struct numtostr
+        {
+          public:
+            template<typename NumberType>
+            numtostr(NumberType value)
+            {
+                x_write(value, std::is_integral<NumberType>());
+            }
+
+            const char* c_str() const
+            {
+                return m_buf.data();
+            }
+
+          private:
+            /// a (hopefully) large enough character buffer
+            std::array < char, 64 > m_buf{{}};
+
+            template<typename NumberType>
+            void x_write(NumberType x, /*is_integral=*/std::true_type)
+            {
+                // 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);
+            }
+
+            template<typename NumberType>
+            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))
+                    {
+                        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<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')
+                {
+                    const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep);
+                    std::fill(end, m_buf.end(), '\0');
+                }
+
+                // convert decimal point to '.'
+                if (decimal_point != '\0' and decimal_point != '.')
+                {
+                    for (auto& c : m_buf)
+                    {
+                        if (c == decimal_point)
+                        {
+                            c = '.';
+                            break;
+                        }
+                    }
+                }
+
+                // determine if need to append ".0"
+                size_t i = 0;
+                bool value_is_int_like = true;
+                for (i = 0; i < m_buf.size(); ++i)
+                {
+                    // 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)
+                {
+                    // 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');
+                }
+            }
+        };
+
+      private:
+        std::ostream& o;
+    };
+
+  public:
     /*!
     @brief serialize to stream
 
@@ -6226,7 +6752,8 @@ class basic_json
         o.width(0);
 
         // do the actual serialization
-        j.dump(o, pretty_print, static_cast<unsigned int>(indentation));
+        serializer s(o);
+        s.dump(j, pretty_print, static_cast<unsigned int>(indentation));
 
         return o;
     }
@@ -8082,519 +8609,6 @@ class basic_json
         }
     }
 
-  private:
-    /*!
-    @brief calculates the extra space to escape a JSON string
-
-    @param[in] s  the string to escape
-    @return the number of characters required to escape string @a s
-
-    @complexity Linear in the length of string @a s.
-    */
-    static std::size_t extra_space(const string_t& s) noexcept
-    {
-        return std::accumulate(s.begin(), s.end(), size_t{},
-                               [](size_t res, typename string_t::value_type c)
-        {
-            switch (c)
-            {
-                case '"':
-                case '\\':
-                case '\b':
-                case '\f':
-                case '\n':
-                case '\r':
-                case '\t':
-                {
-                    // from c (1 byte) to \x (2 bytes)
-                    return res + 1;
-                }
-
-                default:
-                {
-                    if (c >= 0x00 and c <= 0x1f)
-                    {
-                        // from c (1 byte) to \uxxxx (6 bytes)
-                        return res + 5;
-                    }
-
-                    return res;
-                }
-            }
-        });
-    }
-
-    /*!
-    @brief escape a string
-
-    Escape a string by replacing certain special characters by a sequence of
-    an escape character (backslash) and another character and other control
-    characters by a sequence of "\u" followed by a four-digit hex
-    representation.
-
-    @param[in] s  the string to escape
-    @return  the escaped string
-
-    @complexity Linear in the length of string @a s.
-    */
-    static string_t escape_string(const string_t& s)
-    {
-        const auto space = extra_space(s);
-        if (space == 0)
-        {
-            return s;
-        }
-
-        // create a result string of necessary size
-        string_t result(s.size() + space, '\\');
-        std::size_t pos = 0;
-
-        for (const auto& c : s)
-        {
-            switch (c)
-            {
-                // quotation mark (0x22)
-                case '"':
-                {
-                    result[pos + 1] = '"';
-                    pos += 2;
-                    break;
-                }
-
-                // reverse solidus (0x5c)
-                case '\\':
-                {
-                    // nothing to change
-                    pos += 2;
-                    break;
-                }
-
-                // backspace (0x08)
-                case '\b':
-                {
-                    result[pos + 1] = 'b';
-                    pos += 2;
-                    break;
-                }
-
-                // formfeed (0x0c)
-                case '\f':
-                {
-                    result[pos + 1] = 'f';
-                    pos += 2;
-                    break;
-                }
-
-                // newline (0x0a)
-                case '\n':
-                {
-                    result[pos + 1] = 'n';
-                    pos += 2;
-                    break;
-                }
-
-                // carriage return (0x0d)
-                case '\r':
-                {
-                    result[pos + 1] = 'r';
-                    pos += 2;
-                    break;
-                }
-
-                // horizontal tab (0x09)
-                case '\t':
-                {
-                    result[pos + 1] = 't';
-                    pos += 2;
-                    break;
-                }
-
-                default:
-                {
-                    if (c >= 0x00 and c <= 0x1f)
-                    {
-                        // convert a number 0..15 to its hex representation
-                        // (0..f)
-                        static const char hexify[16] =
-                        {
-                            '0', '1', '2', '3', '4', '5', '6', '7',
-                            '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
-                        };
-
-                        // print character c as \uxxxx
-                        for (const char m :
-                    { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f]
-                        })
-                        {
-                            result[++pos] = m;
-                        }
-
-                        ++pos;
-                    }
-                    else
-                    {
-                        // all other characters are added as-is
-                        result[pos++] = c;
-                    }
-                    break;
-                }
-            }
-        }
-
-        return result;
-    }
-
-
-    /*!
-    @brief locale-independent serialization for built-in arithmetic types
-    */
-    struct numtostr
-    {
-      public:
-        template<typename NumberType>
-        numtostr(NumberType value)
-        {
-            x_write(value, std::is_integral<NumberType>());
-        }
-
-        const char* c_str() const
-        {
-            return m_buf.data();
-        }
-
-      private:
-        /// a (hopefully) large enough character buffer
-        std::array < char, 64 > m_buf{{}};
-
-        template<typename NumberType>
-        void x_write(NumberType x, /*is_integral=*/std::true_type)
-        {
-            // 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);
-        }
-
-        template<typename NumberType>
-        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))
-                {
-                    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<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')
-            {
-                const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep);
-                std::fill(end, m_buf.end(), '\0');
-            }
-
-            // convert decimal point to '.'
-            if (decimal_point != '\0' and decimal_point != '.')
-            {
-                for (auto& c : m_buf)
-                {
-                    if (c == decimal_point)
-                    {
-                        c = '.';
-                        break;
-                    }
-                }
-            }
-
-            // determine if need to append ".0"
-            size_t i = 0;
-            bool value_is_int_like = true;
-            for (i = 0; i < m_buf.size(); ++i)
-            {
-                // 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)
-            {
-                // 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');
-            }
-        }
-    };
-
-
-    /*!
-    @brief internal implementation of the serialization function
-
-    This function is called by the public member function dump and organizes
-    the serialization internally. The indentation level is propagated as
-    additional parameter. In case of arrays and objects, the function is
-    called recursively. Note that
-
-    - strings and object keys are escaped using `escape_string()`
-    - integer numbers are converted implicitly via `operator<<`
-    - floating-point numbers are converted to a string using `"%g"` format
-
-    @param[out] o              stream to write to
-    @param[in] pretty_print    whether the output shall be pretty-printed
-    @param[in] indent_step     the indent level
-    @param[in] current_indent  the current indent level (only used internally)
-    */
-    void dump(std::ostream& o,
-              const bool pretty_print,
-              const unsigned int indent_step,
-              const unsigned int current_indent = 0) const
-    {
-        switch (m_type)
-        {
-            case value_t::object:
-            {
-                if (m_value.object->empty())
-                {
-                    o.write("{}", 2);
-                    return;
-                }
-
-                if (pretty_print)
-                {
-                    o.write("{\n", 2);
-
-                    // variable to hold indentation for recursive calls
-                    const auto new_indent = current_indent + indent_step;
-                    string_t indent_string = string_t(new_indent, ' ');
-
-                    // first n-1 elements
-                    auto i = m_value.object->cbegin();
-                    for (size_t cnt = 0; cnt < m_value.object->size() - 1; ++cnt, ++i)
-                    {
-                        o.write(indent_string.c_str(), new_indent);
-                        o.put('\"');
-                        const auto s = escape_string(i->first);
-                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
-                        o.write("\": ", 3);
-                        i->second.dump(o, true, indent_step, new_indent);
-                        o.write(",\n", 2);
-                    }
-
-                    // last element
-                    assert(i != m_value.object->cend());
-                    o.write(indent_string.c_str(), new_indent);
-                    o.put('\"');
-                    const auto s = escape_string(i->first);
-                    o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
-                    o.write("\": ", 3);
-                    i->second.dump(o, true, indent_step, new_indent);
-
-                    o.put('\n');
-                    o.write(indent_string.c_str(), current_indent);
-                    o.put('}');
-                }
-                else
-                {
-                    o.put('{');
-
-                    // first n-1 elements
-                    auto i = m_value.object->cbegin();
-                    for (size_t cnt = 0; cnt < m_value.object->size() - 1; ++cnt, ++i)
-                    {
-                        o.put('\"');
-                        const auto s = escape_string(i->first);
-                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
-                        o.write("\":", 2);
-                        i->second.dump(o, false, indent_step, current_indent);
-                        o.put(',');
-                    }
-
-                    // last element
-                    assert(i != m_value.object->cend());
-                    o.put('\"');
-                    const auto s = escape_string(i->first);
-                    o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
-                    o.write("\":", 2);
-                    i->second.dump(o, false, indent_step, current_indent);
-
-                    o.put('}');
-                }
-
-                return;
-            }
-
-            case value_t::array:
-            {
-                if (m_value.array->empty())
-                {
-                    o.write("[]", 2);
-                    return;
-                }
-
-                if (pretty_print)
-                {
-                    o.write("[\n", 2);
-
-                    // variable to hold indentation for recursive calls
-                    const auto new_indent = current_indent + indent_step;
-                    string_t indent_string = string_t(new_indent, ' ');
-
-                    // first n-1 elements
-                    for (auto i = m_value.array->cbegin(); i != m_value.array->cend() - 1; ++i)
-                    {
-                        o.write(indent_string.c_str(), new_indent);
-                        i->dump(o, true, indent_step, new_indent);
-                        o.write(",\n", 2);
-                    }
-
-                    // last element
-                    assert(not m_value.array->empty());
-                    o.write(indent_string.c_str(), new_indent);
-                    m_value.array->back().dump(o, true, indent_step, new_indent);
-
-                    o.put('\n');
-                    o.write(indent_string.c_str(), current_indent);
-                    o.put(']');
-                }
-                else
-                {
-                    o.put('[');
-
-                    // first n-1 elements
-                    for (auto i = m_value.array->cbegin(); i != m_value.array->cend() - 1; ++i)
-                    {
-                        i->dump(o, false, indent_step, current_indent);
-                        o.put(',');
-                    }
-
-                    // last element
-                    assert(not m_value.array->empty());
-                    m_value.array->back().dump(o, false, indent_step, current_indent);
-
-                    o.put(']');
-                }
-
-                return;
-            }
-
-            case value_t::string:
-            {
-                o.put('\"');
-                const auto s = escape_string(*m_value.string);
-                o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
-                o.put('\"');
-                return;
-            }
-
-            case value_t::boolean:
-            {
-                if (m_value.boolean)
-                {
-                    o.write("true", 4);
-                }
-                else
-                {
-                    o.write("false", 5);
-                }
-                return;
-            }
-
-            case value_t::number_integer:
-            {
-                o << numtostr(m_value.number_integer).c_str();
-                return;
-            }
-
-            case value_t::number_unsigned:
-            {
-                o << numtostr(m_value.number_unsigned).c_str();
-                return;
-            }
-
-            case value_t::number_float:
-            {
-                o << numtostr(m_value.number_float).c_str();
-                return;
-            }
-
-            case value_t::discarded:
-            {
-                o.write("<discarded>", 11);
-                return;
-            }
-
-            case value_t::null:
-            {
-                o.write("null", 4);
-                return;
-            }
-        }
-    }
 
   private:
     //////////////////////
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index a686a1a7..01f6bbfe 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -2644,14 +2644,15 @@ class basic_json
     string_t dump(const int indent = -1) const
     {
         std::stringstream ss;
+        serializer s(ss);
 
         if (indent >= 0)
         {
-            dump(ss, true, static_cast<unsigned int>(indent));
+            s.dump(*this, true, static_cast<unsigned int>(indent));
         }
         else
         {
-            dump(ss, false, 0);
+            s.dump(*this, false, 0);
         }
 
         return ss.str();
@@ -6194,6 +6195,534 @@ class basic_json
     /// @name serialization
     /// @{
 
+  private:
+    /*!
+    @brief wrapper around the serialization functions
+    */
+    class serializer
+    {
+      public:
+        serializer(std::ostream& s)
+            : o(s)
+        {}
+
+        /*!
+        @brief internal implementation of the serialization function
+
+        This function is called by the public member function dump and organizes
+        the serialization internally. The indentation level is propagated as
+        additional parameter. In case of arrays and objects, the function is
+        called recursively. Note that
+
+        - strings and object keys are escaped using `escape_string()`
+        - integer numbers are converted implicitly via `operator<<`
+        - floating-point numbers are converted to a string using `"%g"` format
+
+        @param[in] val             value to serialize
+        @param[in] pretty_print    whether the output shall be pretty-printed
+        @param[in] indent_step     the indent level
+        @param[in] current_indent  the current indent level (only used internally)
+        */
+        void dump(const basic_json& val,
+                  const bool pretty_print,
+                  const unsigned int indent_step,
+                  const unsigned int current_indent = 0) const
+        {
+            switch (val.m_type)
+            {
+                case value_t::object:
+                {
+                    if (val.m_value.object->empty())
+                    {
+                        o.write("{}", 2);
+                        return;
+                    }
+
+                    if (pretty_print)
+                    {
+                        o.write("{\n", 2);
+
+                        // variable to hold indentation for recursive calls
+                        const auto new_indent = current_indent + indent_step;
+                        string_t indent_string = string_t(new_indent, ' ');
+
+                        // first n-1 elements
+                        auto i = val.m_value.object->cbegin();
+                        for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
+                        {
+                            o.write(indent_string.c_str(), new_indent);
+                            o.put('\"');
+                            const auto s = escape_string(i->first);
+                            o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                            o.write("\": ", 3);
+                            dump(i->second, true, indent_step, new_indent);
+                            o.write(",\n", 2);
+                        }
+
+                        // last element
+                        assert(i != val.m_value.object->cend());
+                        o.write(indent_string.c_str(), new_indent);
+                        o.put('\"');
+                        const auto s = escape_string(i->first);
+                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                        o.write("\": ", 3);
+                        dump(i->second, true, indent_step, new_indent);
+
+                        o.put('\n');
+                        o.write(indent_string.c_str(), current_indent);
+                        o.put('}');
+                    }
+                    else
+                    {
+                        o.put('{');
+
+                        // first n-1 elements
+                        auto i = val.m_value.object->cbegin();
+                        for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
+                        {
+                            o.put('\"');
+                            const auto s = escape_string(i->first);
+                            o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                            o.write("\":", 2);
+                            dump(i->second, false, indent_step, current_indent);
+                            o.put(',');
+                        }
+
+                        // last element
+                        assert(i != val.m_value.object->cend());
+                        o.put('\"');
+                        const auto s = escape_string(i->first);
+                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                        o.write("\":", 2);
+                        dump(i->second, false, indent_step, current_indent);
+
+                        o.put('}');
+                    }
+
+                    return;
+                }
+
+                case value_t::array:
+                {
+                    if (val.m_value.array->empty())
+                    {
+                        o.write("[]", 2);
+                        return;
+                    }
+
+                    if (pretty_print)
+                    {
+                        o.write("[\n", 2);
+
+                        // variable to hold indentation for recursive calls
+                        const auto new_indent = current_indent + indent_step;
+                        string_t indent_string = string_t(new_indent, ' ');
+
+                        // first n-1 elements
+                        for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i)
+                        {
+                            o.write(indent_string.c_str(), new_indent);
+                            dump(*i, true, indent_step, new_indent);
+                            o.write(",\n", 2);
+                        }
+
+                        // last element
+                        assert(not val.m_value.array->empty());
+                        o.write(indent_string.c_str(), new_indent);
+                        dump(val.m_value.array->back(), true, indent_step, new_indent);
+
+                        o.put('\n');
+                        o.write(indent_string.c_str(), current_indent);
+                        o.put(']');
+                    }
+                    else
+                    {
+                        o.put('[');
+
+                        // first n-1 elements
+                        for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i)
+                        {
+                            dump(*i, false, indent_step, current_indent);
+                            o.put(',');
+                        }
+
+                        // last element
+                        assert(not val.m_value.array->empty());
+                        dump(val.m_value.array->back(), false, indent_step, current_indent);
+
+                        o.put(']');
+                    }
+
+                    return;
+                }
+
+                case value_t::string:
+                {
+                    o.put('\"');
+                    const auto s = escape_string(*val.m_value.string);
+                    o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                    o.put('\"');
+                    return;
+                }
+
+                case value_t::boolean:
+                {
+                    if (val.m_value.boolean)
+                    {
+                        o.write("true", 4);
+                    }
+                    else
+                    {
+                        o.write("false", 5);
+                    }
+                    return;
+                }
+
+                case value_t::number_integer:
+                {
+                    o << numtostr(val.m_value.number_integer).c_str();
+                    return;
+                }
+
+                case value_t::number_unsigned:
+                {
+                    o << numtostr(val.m_value.number_unsigned).c_str();
+                    return;
+                }
+
+                case value_t::number_float:
+                {
+                    o << numtostr(val.m_value.number_float).c_str();
+                    return;
+                }
+
+                case value_t::discarded:
+                {
+                    o.write("<discarded>", 11);
+                    return;
+                }
+
+                case value_t::null:
+                {
+                    o.write("null", 4);
+                    return;
+                }
+            }
+        }
+
+      private:
+        /*!
+        @brief calculates the extra space to escape a JSON string
+
+        @param[in] s  the string to escape
+        @return the number of characters required to escape string @a s
+
+        @complexity Linear in the length of string @a s.
+        */
+        static std::size_t extra_space(const string_t& s) noexcept
+        {
+            return std::accumulate(s.begin(), s.end(), size_t{},
+                                   [](size_t res, typename string_t::value_type c)
+            {
+                switch (c)
+                {
+                    case '"':
+                    case '\\':
+                    case '\b':
+                    case '\f':
+                    case '\n':
+                    case '\r':
+                    case '\t':
+                    {
+                        // from c (1 byte) to \x (2 bytes)
+                        return res + 1;
+                    }
+
+                    default:
+                    {
+                        if (c >= 0x00 and c <= 0x1f)
+                        {
+                            // from c (1 byte) to \uxxxx (6 bytes)
+                            return res + 5;
+                        }
+
+                        return res;
+                    }
+                }
+            });
+        }
+
+        /*!
+        @brief escape a string
+
+        Escape a string by replacing certain special characters by a sequence of
+        an escape character (backslash) and another character and other control
+        characters by a sequence of "\u" followed by a four-digit hex
+        representation.
+
+        @param[in] s  the string to escape
+        @return  the escaped string
+
+        @complexity Linear in the length of string @a s.
+        */
+        static string_t escape_string(const string_t& s)
+        {
+            const auto space = extra_space(s);
+            if (space == 0)
+            {
+                return s;
+            }
+
+            // create a result string of necessary size
+            string_t result(s.size() + space, '\\');
+            std::size_t pos = 0;
+
+            for (const auto& c : s)
+            {
+                switch (c)
+                {
+                    // quotation mark (0x22)
+                    case '"':
+                    {
+                        result[pos + 1] = '"';
+                        pos += 2;
+                        break;
+                    }
+
+                    // reverse solidus (0x5c)
+                    case '\\':
+                    {
+                        // nothing to change
+                        pos += 2;
+                        break;
+                    }
+
+                    // backspace (0x08)
+                    case '\b':
+                    {
+                        result[pos + 1] = 'b';
+                        pos += 2;
+                        break;
+                    }
+
+                    // formfeed (0x0c)
+                    case '\f':
+                    {
+                        result[pos + 1] = 'f';
+                        pos += 2;
+                        break;
+                    }
+
+                    // newline (0x0a)
+                    case '\n':
+                    {
+                        result[pos + 1] = 'n';
+                        pos += 2;
+                        break;
+                    }
+
+                    // carriage return (0x0d)
+                    case '\r':
+                    {
+                        result[pos + 1] = 'r';
+                        pos += 2;
+                        break;
+                    }
+
+                    // horizontal tab (0x09)
+                    case '\t':
+                    {
+                        result[pos + 1] = 't';
+                        pos += 2;
+                        break;
+                    }
+
+                    default:
+                    {
+                        if (c >= 0x00 and c <= 0x1f)
+                        {
+                            // convert a number 0..15 to its hex representation
+                            // (0..f)
+                            static const char hexify[16] =
+                            {
+                                '0', '1', '2', '3', '4', '5', '6', '7',
+                                '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+                            };
+
+                            // print character c as \uxxxx
+                            for (const char m :
+                        { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f]
+                            })
+                            {
+                                result[++pos] = m;
+                            }
+
+                            ++pos;
+                        }
+                        else
+                        {
+                            // all other characters are added as-is
+                            result[pos++] = c;
+                        }
+                        break;
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        /*!
+        @brief locale-independent serialization for built-in arithmetic types
+        */
+        struct numtostr
+        {
+          public:
+            template<typename NumberType>
+            numtostr(NumberType value)
+            {
+                x_write(value, std::is_integral<NumberType>());
+            }
+
+            const char* c_str() const
+            {
+                return m_buf.data();
+            }
+
+          private:
+            /// a (hopefully) large enough character buffer
+            std::array < char, 64 > m_buf{{}};
+
+            template<typename NumberType>
+            void x_write(NumberType x, /*is_integral=*/std::true_type)
+            {
+                // 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);
+            }
+
+            template<typename NumberType>
+            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))
+                    {
+                        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<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')
+                {
+                    const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep);
+                    std::fill(end, m_buf.end(), '\0');
+                }
+
+                // convert decimal point to '.'
+                if (decimal_point != '\0' and decimal_point != '.')
+                {
+                    for (auto& c : m_buf)
+                    {
+                        if (c == decimal_point)
+                        {
+                            c = '.';
+                            break;
+                        }
+                    }
+                }
+
+                // determine if need to append ".0"
+                size_t i = 0;
+                bool value_is_int_like = true;
+                for (i = 0; i < m_buf.size(); ++i)
+                {
+                    // 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)
+                {
+                    // 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');
+                }
+            }
+        };
+
+      private:
+        std::ostream& o;
+    };
+
+  public:
     /*!
     @brief serialize to stream
 
@@ -6226,8 +6755,8 @@ class basic_json
         o.width(0);
 
         // do the actual serialization
-        j.dump(o, pretty_print, static_cast<unsigned int>(indentation));
-
+        serializer s(o);
+        s.dump(j, pretty_print, static_cast<unsigned int>(indentation));
         return o;
     }
 
@@ -8082,519 +8611,6 @@ class basic_json
         }
     }
 
-  private:
-    /*!
-    @brief calculates the extra space to escape a JSON string
-
-    @param[in] s  the string to escape
-    @return the number of characters required to escape string @a s
-
-    @complexity Linear in the length of string @a s.
-    */
-    static std::size_t extra_space(const string_t& s) noexcept
-    {
-        return std::accumulate(s.begin(), s.end(), size_t{},
-                               [](size_t res, typename string_t::value_type c)
-        {
-            switch (c)
-            {
-                case '"':
-                case '\\':
-                case '\b':
-                case '\f':
-                case '\n':
-                case '\r':
-                case '\t':
-                {
-                    // from c (1 byte) to \x (2 bytes)
-                    return res + 1;
-                }
-
-                default:
-                {
-                    if (c >= 0x00 and c <= 0x1f)
-                    {
-                        // from c (1 byte) to \uxxxx (6 bytes)
-                        return res + 5;
-                    }
-
-                    return res;
-                }
-            }
-        });
-    }
-
-    /*!
-    @brief escape a string
-
-    Escape a string by replacing certain special characters by a sequence of
-    an escape character (backslash) and another character and other control
-    characters by a sequence of "\u" followed by a four-digit hex
-    representation.
-
-    @param[in] s  the string to escape
-    @return  the escaped string
-
-    @complexity Linear in the length of string @a s.
-    */
-    static string_t escape_string(const string_t& s)
-    {
-        const auto space = extra_space(s);
-        if (space == 0)
-        {
-            return s;
-        }
-
-        // create a result string of necessary size
-        string_t result(s.size() + space, '\\');
-        std::size_t pos = 0;
-
-        for (const auto& c : s)
-        {
-            switch (c)
-            {
-                // quotation mark (0x22)
-                case '"':
-                {
-                    result[pos + 1] = '"';
-                    pos += 2;
-                    break;
-                }
-
-                // reverse solidus (0x5c)
-                case '\\':
-                {
-                    // nothing to change
-                    pos += 2;
-                    break;
-                }
-
-                // backspace (0x08)
-                case '\b':
-                {
-                    result[pos + 1] = 'b';
-                    pos += 2;
-                    break;
-                }
-
-                // formfeed (0x0c)
-                case '\f':
-                {
-                    result[pos + 1] = 'f';
-                    pos += 2;
-                    break;
-                }
-
-                // newline (0x0a)
-                case '\n':
-                {
-                    result[pos + 1] = 'n';
-                    pos += 2;
-                    break;
-                }
-
-                // carriage return (0x0d)
-                case '\r':
-                {
-                    result[pos + 1] = 'r';
-                    pos += 2;
-                    break;
-                }
-
-                // horizontal tab (0x09)
-                case '\t':
-                {
-                    result[pos + 1] = 't';
-                    pos += 2;
-                    break;
-                }
-
-                default:
-                {
-                    if (c >= 0x00 and c <= 0x1f)
-                    {
-                        // convert a number 0..15 to its hex representation
-                        // (0..f)
-                        static const char hexify[16] =
-                        {
-                            '0', '1', '2', '3', '4', '5', '6', '7',
-                            '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
-                        };
-
-                        // print character c as \uxxxx
-                        for (const char m :
-                    { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f]
-                        })
-                        {
-                            result[++pos] = m;
-                        }
-
-                        ++pos;
-                    }
-                    else
-                    {
-                        // all other characters are added as-is
-                        result[pos++] = c;
-                    }
-                    break;
-                }
-            }
-        }
-
-        return result;
-    }
-
-
-    /*!
-    @brief locale-independent serialization for built-in arithmetic types
-    */
-    struct numtostr
-    {
-      public:
-        template<typename NumberType>
-        numtostr(NumberType value)
-        {
-            x_write(value, std::is_integral<NumberType>());
-        }
-
-        const char* c_str() const
-        {
-            return m_buf.data();
-        }
-
-      private:
-        /// a (hopefully) large enough character buffer
-        std::array < char, 64 > m_buf{{}};
-
-        template<typename NumberType>
-        void x_write(NumberType x, /*is_integral=*/std::true_type)
-        {
-            // 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);
-        }
-
-        template<typename NumberType>
-        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))
-                {
-                    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<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')
-            {
-                const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep);
-                std::fill(end, m_buf.end(), '\0');
-            }
-
-            // convert decimal point to '.'
-            if (decimal_point != '\0' and decimal_point != '.')
-            {
-                for (auto& c : m_buf)
-                {
-                    if (c == decimal_point)
-                    {
-                        c = '.';
-                        break;
-                    }
-                }
-            }
-
-            // determine if need to append ".0"
-            size_t i = 0;
-            bool value_is_int_like = true;
-            for (i = 0; i < m_buf.size(); ++i)
-            {
-                // 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)
-            {
-                // 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');
-            }
-        }
-    };
-
-
-    /*!
-    @brief internal implementation of the serialization function
-
-    This function is called by the public member function dump and organizes
-    the serialization internally. The indentation level is propagated as
-    additional parameter. In case of arrays and objects, the function is
-    called recursively. Note that
-
-    - strings and object keys are escaped using `escape_string()`
-    - integer numbers are converted implicitly via `operator<<`
-    - floating-point numbers are converted to a string using `"%g"` format
-
-    @param[out] o              stream to write to
-    @param[in] pretty_print    whether the output shall be pretty-printed
-    @param[in] indent_step     the indent level
-    @param[in] current_indent  the current indent level (only used internally)
-    */
-    void dump(std::ostream& o,
-              const bool pretty_print,
-              const unsigned int indent_step,
-              const unsigned int current_indent = 0) const
-    {
-        switch (m_type)
-        {
-            case value_t::object:
-            {
-                if (m_value.object->empty())
-                {
-                    o.write("{}", 2);
-                    return;
-                }
-
-                if (pretty_print)
-                {
-                    o.write("{\n", 2);
-
-                    // variable to hold indentation for recursive calls
-                    const auto new_indent = current_indent + indent_step;
-                    string_t indent_string = string_t(new_indent, ' ');
-
-                    // first n-1 elements
-                    auto i = m_value.object->cbegin();
-                    for (size_t cnt = 0; cnt < m_value.object->size() - 1; ++cnt, ++i)
-                    {
-                        o.write(indent_string.c_str(), new_indent);
-                        o.put('\"');
-                        const auto s = escape_string(i->first);
-                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
-                        o.write("\": ", 3);
-                        i->second.dump(o, true, indent_step, new_indent);
-                        o.write(",\n", 2);
-                    }
-
-                    // last element
-                    assert(i != m_value.object->cend());
-                    o.write(indent_string.c_str(), new_indent);
-                    o.put('\"');
-                    const auto s = escape_string(i->first);
-                    o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
-                    o.write("\": ", 3);
-                    i->second.dump(o, true, indent_step, new_indent);
-
-                    o.put('\n');
-                    o.write(indent_string.c_str(), current_indent);
-                    o.put('}');
-                }
-                else
-                {
-                    o.put('{');
-
-                    // first n-1 elements
-                    auto i = m_value.object->cbegin();
-                    for (size_t cnt = 0; cnt < m_value.object->size() - 1; ++cnt, ++i)
-                    {
-                        o.put('\"');
-                        const auto s = escape_string(i->first);
-                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
-                        o.write("\":", 2);
-                        i->second.dump(o, false, indent_step, current_indent);
-                        o.put(',');
-                    }
-
-                    // last element
-                    assert(i != m_value.object->cend());
-                    o.put('\"');
-                    const auto s = escape_string(i->first);
-                    o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
-                    o.write("\":", 2);
-                    i->second.dump(o, false, indent_step, current_indent);
-
-                    o.put('}');
-                }
-
-                return;
-            }
-
-            case value_t::array:
-            {
-                if (m_value.array->empty())
-                {
-                    o.write("[]", 2);
-                    return;
-                }
-
-                if (pretty_print)
-                {
-                    o.write("[\n", 2);
-
-                    // variable to hold indentation for recursive calls
-                    const auto new_indent = current_indent + indent_step;
-                    string_t indent_string = string_t(new_indent, ' ');
-
-                    // first n-1 elements
-                    for (auto i = m_value.array->cbegin(); i != m_value.array->cend() - 1; ++i)
-                    {
-                        o.write(indent_string.c_str(), new_indent);
-                        i->dump(o, true, indent_step, new_indent);
-                        o.write(",\n", 2);
-                    }
-
-                    // last element
-                    assert(not m_value.array->empty());
-                    o.write(indent_string.c_str(), new_indent);
-                    m_value.array->back().dump(o, true, indent_step, new_indent);
-
-                    o.put('\n');
-                    o.write(indent_string.c_str(), current_indent);
-                    o.put(']');
-                }
-                else
-                {
-                    o.put('[');
-
-                    // first n-1 elements
-                    for (auto i = m_value.array->cbegin(); i != m_value.array->cend() - 1; ++i)
-                    {
-                        i->dump(o, false, indent_step, current_indent);
-                        o.put(',');
-                    }
-
-                    // last element
-                    assert(not m_value.array->empty());
-                    m_value.array->back().dump(o, false, indent_step, current_indent);
-
-                    o.put(']');
-                }
-
-                return;
-            }
-
-            case value_t::string:
-            {
-                o.put('\"');
-                const auto s = escape_string(*m_value.string);
-                o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
-                o.put('\"');
-                return;
-            }
-
-            case value_t::boolean:
-            {
-                if (m_value.boolean)
-                {
-                    o.write("true", 4);
-                }
-                else
-                {
-                    o.write("false", 5);
-                }
-                return;
-            }
-
-            case value_t::number_integer:
-            {
-                o << numtostr(m_value.number_integer).c_str();
-                return;
-            }
-
-            case value_t::number_unsigned:
-            {
-                o << numtostr(m_value.number_unsigned).c_str();
-                return;
-            }
-
-            case value_t::number_float:
-            {
-                o << numtostr(m_value.number_float).c_str();
-                return;
-            }
-
-            case value_t::discarded:
-            {
-                o.write("<discarded>", 11);
-                return;
-            }
-
-            case value_t::null:
-            {
-                o.write("null", 4);
-                return;
-            }
-        }
-    }
 
   private:
     //////////////////////
diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp
index 891dbc14..45637033 100644
--- a/test/src/unit-convenience.cpp
+++ b/test/src/unit-convenience.cpp
@@ -49,44 +49,44 @@ TEST_CASE("convenience functions")
 
     SECTION("string escape")
     {
-        CHECK(json::escape_string("\"") == "\\\"");
-        CHECK(json::escape_string("\\") == "\\\\");
-        CHECK(json::escape_string("\b") == "\\b");
-        CHECK(json::escape_string("\f") == "\\f");
-        CHECK(json::escape_string("\n") == "\\n");
-        CHECK(json::escape_string("\r") == "\\r");
-        CHECK(json::escape_string("\t") == "\\t");
+        CHECK(json::serializer::escape_string("\"") == "\\\"");
+        CHECK(json::serializer::escape_string("\\") == "\\\\");
+        CHECK(json::serializer::escape_string("\b") == "\\b");
+        CHECK(json::serializer::escape_string("\f") == "\\f");
+        CHECK(json::serializer::escape_string("\n") == "\\n");
+        CHECK(json::serializer::escape_string("\r") == "\\r");
+        CHECK(json::serializer::escape_string("\t") == "\\t");
 
-        CHECK(json::escape_string("\x01") == "\\u0001");
-        CHECK(json::escape_string("\x02") == "\\u0002");
-        CHECK(json::escape_string("\x03") == "\\u0003");
-        CHECK(json::escape_string("\x04") == "\\u0004");
-        CHECK(json::escape_string("\x05") == "\\u0005");
-        CHECK(json::escape_string("\x06") == "\\u0006");
-        CHECK(json::escape_string("\x07") == "\\u0007");
-        CHECK(json::escape_string("\x08") == "\\b");
-        CHECK(json::escape_string("\x09") == "\\t");
-        CHECK(json::escape_string("\x0a") == "\\n");
-        CHECK(json::escape_string("\x0b") == "\\u000b");
-        CHECK(json::escape_string("\x0c") == "\\f");
-        CHECK(json::escape_string("\x0d") == "\\r");
-        CHECK(json::escape_string("\x0e") == "\\u000e");
-        CHECK(json::escape_string("\x0f") == "\\u000f");
-        CHECK(json::escape_string("\x10") == "\\u0010");
-        CHECK(json::escape_string("\x11") == "\\u0011");
-        CHECK(json::escape_string("\x12") == "\\u0012");
-        CHECK(json::escape_string("\x13") == "\\u0013");
-        CHECK(json::escape_string("\x14") == "\\u0014");
-        CHECK(json::escape_string("\x15") == "\\u0015");
-        CHECK(json::escape_string("\x16") == "\\u0016");
-        CHECK(json::escape_string("\x17") == "\\u0017");
-        CHECK(json::escape_string("\x18") == "\\u0018");
-        CHECK(json::escape_string("\x19") == "\\u0019");
-        CHECK(json::escape_string("\x1a") == "\\u001a");
-        CHECK(json::escape_string("\x1b") == "\\u001b");
-        CHECK(json::escape_string("\x1c") == "\\u001c");
-        CHECK(json::escape_string("\x1d") == "\\u001d");
-        CHECK(json::escape_string("\x1e") == "\\u001e");
-        CHECK(json::escape_string("\x1f") == "\\u001f");
+        CHECK(json::serializer::escape_string("\x01") == "\\u0001");
+        CHECK(json::serializer::escape_string("\x02") == "\\u0002");
+        CHECK(json::serializer::escape_string("\x03") == "\\u0003");
+        CHECK(json::serializer::escape_string("\x04") == "\\u0004");
+        CHECK(json::serializer::escape_string("\x05") == "\\u0005");
+        CHECK(json::serializer::escape_string("\x06") == "\\u0006");
+        CHECK(json::serializer::escape_string("\x07") == "\\u0007");
+        CHECK(json::serializer::escape_string("\x08") == "\\b");
+        CHECK(json::serializer::escape_string("\x09") == "\\t");
+        CHECK(json::serializer::escape_string("\x0a") == "\\n");
+        CHECK(json::serializer::escape_string("\x0b") == "\\u000b");
+        CHECK(json::serializer::escape_string("\x0c") == "\\f");
+        CHECK(json::serializer::escape_string("\x0d") == "\\r");
+        CHECK(json::serializer::escape_string("\x0e") == "\\u000e");
+        CHECK(json::serializer::escape_string("\x0f") == "\\u000f");
+        CHECK(json::serializer::escape_string("\x10") == "\\u0010");
+        CHECK(json::serializer::escape_string("\x11") == "\\u0011");
+        CHECK(json::serializer::escape_string("\x12") == "\\u0012");
+        CHECK(json::serializer::escape_string("\x13") == "\\u0013");
+        CHECK(json::serializer::escape_string("\x14") == "\\u0014");
+        CHECK(json::serializer::escape_string("\x15") == "\\u0015");
+        CHECK(json::serializer::escape_string("\x16") == "\\u0016");
+        CHECK(json::serializer::escape_string("\x17") == "\\u0017");
+        CHECK(json::serializer::escape_string("\x18") == "\\u0018");
+        CHECK(json::serializer::escape_string("\x19") == "\\u0019");
+        CHECK(json::serializer::escape_string("\x1a") == "\\u001a");
+        CHECK(json::serializer::escape_string("\x1b") == "\\u001b");
+        CHECK(json::serializer::escape_string("\x1c") == "\\u001c");
+        CHECK(json::serializer::escape_string("\x1d") == "\\u001d");
+        CHECK(json::serializer::escape_string("\x1e") == "\\u001e");
+        CHECK(json::serializer::escape_string("\x1f") == "\\u001f");
     }
 }

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 12/23] :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:

From fc48b8ac2b595a8df5ad121b588d8820b3b6941d Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Tue, 28 Feb 2017 11:45:38 +0100
Subject: [PATCH 13/23] :bug: fixed a logical error

Treated the size of the range as the number of thousand separators.
This logical error yielded a negative value for written_bytes and
eventually an infinite loop, as written_bytes was converted to an
unsigned value.
---
 src/json.hpp      | 3 ++-
 src/json.hpp.re2c | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index 3e5a45cf..5cf06249 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -6648,7 +6648,8 @@ 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);
+                assert((end - m_buf.begin()) <= written_bytes);
+                written_bytes = (end - m_buf.begin());
             }
 
             // convert decimal point to '.'
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index e648b41b..c7784266 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -6648,7 +6648,8 @@ 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);
+                assert((end - m_buf.begin()) <= written_bytes);
+                written_bytes = (end - m_buf.begin());
             }
 
             // convert decimal point to '.'

From 224f99070b8083e2c574014c1c7c13607795ec5a Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Tue, 28 Feb 2017 16:28:22 +0100
Subject: [PATCH 14/23] :zap: micro-optimization of dump()

A lot of small changes to avoid memory allocations:

- The locale is only queried once rather than with every number
serialization.
- The indentation string is recycled between different calls.
- The string escape function avoids a copy if no escaping is necessary.
- The string escape and the space function use a complete switch case
instead of cascaded ifs.

Cachegrind measures some 15% performance improvement.
---
 src/json.hpp                  | 176 ++++++++++++++++++++++------------
 src/json.hpp.re2c             | 176 ++++++++++++++++++++++------------
 test/src/unit-convenience.cpp |  85 ++++++++--------
 3 files changed, 277 insertions(+), 160 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index 5cf06249..8d545a93 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -34,6 +34,7 @@ SOFTWARE.
 #include <cassert> // assert
 #include <cctype> // isdigit
 #include <ciso646> // and, not, or
+#include <clocale> // lconv, localeconv
 #include <cmath> // isfinite, labs, ldexp, signbit
 #include <cstddef> // nullptr_t, ptrdiff_t, size_t
 #include <cstdint> // int64_t, uint64_t
@@ -6203,7 +6204,9 @@ class basic_json
     {
       public:
         serializer(std::ostream& s)
-            : o(s)
+            : o(s), loc(std::localeconv()),
+              thousands_sep(!loc->thousands_sep ? '\0' : loc->thousands_sep[0]),
+              decimal_point(!loc->decimal_point ? '\0' : loc->decimal_point[0])
         {}
 
         /*!
@@ -6244,7 +6247,10 @@ class basic_json
 
                         // variable to hold indentation for recursive calls
                         const auto new_indent = current_indent + indent_step;
-                        string_t indent_string = string_t(new_indent, ' ');
+                        if (indent_string.size() < new_indent)
+                        {
+                            indent_string.resize(new_indent, ' ');
+                        }
 
                         // first n-1 elements
                         auto i = val.m_value.object->cbegin();
@@ -6252,8 +6258,7 @@ class basic_json
                         {
                             o.write(indent_string.c_str(), new_indent);
                             o.put('\"');
-                            const auto s = escape_string(i->first);
-                            o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                            dump_escaped(i->first);
                             o.write("\": ", 3);
                             dump(i->second, true, indent_step, new_indent);
                             o.write(",\n", 2);
@@ -6263,8 +6268,7 @@ class basic_json
                         assert(i != val.m_value.object->cend());
                         o.write(indent_string.c_str(), new_indent);
                         o.put('\"');
-                        const auto s = escape_string(i->first);
-                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                        dump_escaped(i->first);
                         o.write("\": ", 3);
                         dump(i->second, true, indent_step, new_indent);
 
@@ -6281,8 +6285,7 @@ class basic_json
                         for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
                         {
                             o.put('\"');
-                            const auto s = escape_string(i->first);
-                            o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                            dump_escaped(i->first);
                             o.write("\":", 2);
                             dump(i->second, false, indent_step, current_indent);
                             o.put(',');
@@ -6291,8 +6294,7 @@ class basic_json
                         // last element
                         assert(i != val.m_value.object->cend());
                         o.put('\"');
-                        const auto s = escape_string(i->first);
-                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                        dump_escaped(i->first);
                         o.write("\":", 2);
                         dump(i->second, false, indent_step, current_indent);
 
@@ -6316,7 +6318,10 @@ class basic_json
 
                         // variable to hold indentation for recursive calls
                         const auto new_indent = current_indent + indent_step;
-                        string_t indent_string = string_t(new_indent, ' ');
+                        if (indent_string.size() < new_indent)
+                        {
+                            indent_string.resize(new_indent, ' ');
+                        }
 
                         // first n-1 elements
                         for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i)
@@ -6359,8 +6364,7 @@ class basic_json
                 case value_t::string:
                 {
                     o.put('\"');
-                    const auto s = escape_string(*val.m_value.string);
-                    o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                    dump_escaped(*val.m_value.string);
                     o.put('\"');
                     return;
                 }
@@ -6380,19 +6384,19 @@ class basic_json
 
                 case value_t::number_integer:
                 {
-                    x_write(val.m_value.number_integer);
+                    dump_integer(val.m_value.number_integer);
                     return;
                 }
 
                 case value_t::number_unsigned:
                 {
-                    x_write(val.m_value.number_unsigned);
+                    dump_integer(val.m_value.number_unsigned);
                     return;
                 }
 
                 case value_t::number_float:
                 {
-                    x_write(val.m_value.number_float);
+                    dump_float(val.m_value.number_float);
                     return;
                 }
 
@@ -6438,14 +6442,40 @@ class basic_json
                         return res + 1;
                     }
 
+                    case 0x00:
+                    case 0x01:
+                    case 0x02:
+                    case 0x03:
+                    case 0x04:
+                    case 0x05:
+                    case 0x06:
+                    case 0x07:
+                    case 0x0b:
+                    case 0x0e:
+                    case 0x0f:
+                    case 0x10:
+                    case 0x11:
+                    case 0x12:
+                    case 0x13:
+                    case 0x14:
+                    case 0x15:
+                    case 0x16:
+                    case 0x17:
+                    case 0x18:
+                    case 0x19:
+                    case 0x1a:
+                    case 0x1b:
+                    case 0x1c:
+                    case 0x1d:
+                    case 0x1e:
+                    case 0x1f:
+                    {
+                        // from c (1 byte) to \uxxxx (6 bytes)
+                        return res + 5;
+                    }
+
                     default:
                     {
-                        if (c >= 0x00 and c <= 0x1f)
-                        {
-                            // from c (1 byte) to \uxxxx (6 bytes)
-                            return res + 5;
-                        }
-
                         return res;
                     }
                 }
@@ -6465,12 +6495,13 @@ class basic_json
 
         @complexity Linear in the length of string @a s.
         */
-        static string_t escape_string(const string_t& s)
+        void dump_escaped(const string_t& s) const
         {
             const auto space = extra_space(s);
             if (space == 0)
             {
-                return s;
+                o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                return;
             }
 
             // create a result string of necessary size
@@ -6537,43 +6568,69 @@ class basic_json
                         break;
                     }
 
+                    case 0x00:
+                    case 0x01:
+                    case 0x02:
+                    case 0x03:
+                    case 0x04:
+                    case 0x05:
+                    case 0x06:
+                    case 0x07:
+                    case 0x0b:
+                    case 0x0e:
+                    case 0x0f:
+                    case 0x10:
+                    case 0x11:
+                    case 0x12:
+                    case 0x13:
+                    case 0x14:
+                    case 0x15:
+                    case 0x16:
+                    case 0x17:
+                    case 0x18:
+                    case 0x19:
+                    case 0x1a:
+                    case 0x1b:
+                    case 0x1c:
+                    case 0x1d:
+                    case 0x1e:
+                    case 0x1f:
+                    {
+                        // convert a number 0..15 to its hex representation
+                        // (0..f)
+                        static const char hexify[16] =
+                        {
+                            '0', '1', '2', '3', '4', '5', '6', '7',
+                            '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+                        };
+
+                        // print character c as \uxxxx
+                        for (const char m :
+                    { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f]
+                        })
+                        {
+                            result[++pos] = m;
+                        }
+
+                        ++pos;
+                        break;
+                    }
+
                     default:
                     {
-                        if (c >= 0x00 and c <= 0x1f)
-                        {
-                            // convert a number 0..15 to its hex representation
-                            // (0..f)
-                            static const char hexify[16] =
-                            {
-                                '0', '1', '2', '3', '4', '5', '6', '7',
-                                '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
-                            };
-
-                            // print character c as \uxxxx
-                            for (const char m :
-                        { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f]
-                            })
-                            {
-                                result[++pos] = m;
-                            }
-
-                            ++pos;
-                        }
-                        else
-                        {
-                            // all other characters are added as-is
-                            result[pos++] = c;
-                        }
+                        // all other characters are added as-is
+                        result[pos++] = c;
                         break;
                     }
                 }
             }
 
-            return result;
+            assert(pos == s.size() + space);
+            o.write(result.c_str(), static_cast<std::streamsize>(result.size()));
         }
 
         template<typename NumberType>
-        void x_write(NumberType x)
+        void dump_integer(NumberType x)
         {
             // special case for "0"
             if (x == 0)
@@ -6607,7 +6664,7 @@ class basic_json
             o.write(m_buf.data(), static_cast<std::streamsize>(i));
         }
 
-        void x_write(number_float_t x)
+        void dump_float(number_float_t x)
         {
             // special case for 0.0 and -0.0
             if (x == 0)
@@ -6634,15 +6691,6 @@ class basic_json
             // 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')
             {
@@ -6687,6 +6735,12 @@ class basic_json
 
         /// a (hopefully) large enough character buffer
         std::array < char, 64 > m_buf{{}};
+
+        const std::lconv* loc = nullptr;
+        const char thousands_sep = '\0';
+        const char decimal_point = '\0';
+
+        string_t indent_string = string_t(512, ' ');
     };
 
   public:
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index c7784266..b14fb68d 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -34,6 +34,7 @@ SOFTWARE.
 #include <cassert> // assert
 #include <cctype> // isdigit
 #include <ciso646> // and, not, or
+#include <clocale> // lconv, localeconv
 #include <cmath> // isfinite, labs, ldexp, signbit
 #include <cstddef> // nullptr_t, ptrdiff_t, size_t
 #include <cstdint> // int64_t, uint64_t
@@ -6203,7 +6204,9 @@ class basic_json
     {
       public:
         serializer(std::ostream& s)
-            : o(s)
+            : o(s), loc(std::localeconv()),
+              thousands_sep(!loc->thousands_sep ? '\0' : loc->thousands_sep[0]),
+              decimal_point(!loc->decimal_point ? '\0' : loc->decimal_point[0])
         {}
 
         /*!
@@ -6244,7 +6247,10 @@ class basic_json
 
                         // variable to hold indentation for recursive calls
                         const auto new_indent = current_indent + indent_step;
-                        string_t indent_string = string_t(new_indent, ' ');
+                        if (indent_string.size() < new_indent)
+                        {
+                            indent_string.resize(new_indent, ' ');
+                        }
 
                         // first n-1 elements
                         auto i = val.m_value.object->cbegin();
@@ -6252,8 +6258,7 @@ class basic_json
                         {
                             o.write(indent_string.c_str(), new_indent);
                             o.put('\"');
-                            const auto s = escape_string(i->first);
-                            o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                            dump_escaped(i->first);
                             o.write("\": ", 3);
                             dump(i->second, true, indent_step, new_indent);
                             o.write(",\n", 2);
@@ -6263,8 +6268,7 @@ class basic_json
                         assert(i != val.m_value.object->cend());
                         o.write(indent_string.c_str(), new_indent);
                         o.put('\"');
-                        const auto s = escape_string(i->first);
-                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                        dump_escaped(i->first);
                         o.write("\": ", 3);
                         dump(i->second, true, indent_step, new_indent);
 
@@ -6281,8 +6285,7 @@ class basic_json
                         for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
                         {
                             o.put('\"');
-                            const auto s = escape_string(i->first);
-                            o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                            dump_escaped(i->first);
                             o.write("\":", 2);
                             dump(i->second, false, indent_step, current_indent);
                             o.put(',');
@@ -6291,8 +6294,7 @@ class basic_json
                         // last element
                         assert(i != val.m_value.object->cend());
                         o.put('\"');
-                        const auto s = escape_string(i->first);
-                        o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                        dump_escaped(i->first);
                         o.write("\":", 2);
                         dump(i->second, false, indent_step, current_indent);
 
@@ -6316,7 +6318,10 @@ class basic_json
 
                         // variable to hold indentation for recursive calls
                         const auto new_indent = current_indent + indent_step;
-                        string_t indent_string = string_t(new_indent, ' ');
+                        if (indent_string.size() < new_indent)
+                        {
+                            indent_string.resize(new_indent, ' ');
+                        }
 
                         // first n-1 elements
                         for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i)
@@ -6359,8 +6364,7 @@ class basic_json
                 case value_t::string:
                 {
                     o.put('\"');
-                    const auto s = escape_string(*val.m_value.string);
-                    o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                    dump_escaped(*val.m_value.string);
                     o.put('\"');
                     return;
                 }
@@ -6380,19 +6384,19 @@ class basic_json
 
                 case value_t::number_integer:
                 {
-                    x_write(val.m_value.number_integer);
+                    dump_integer(val.m_value.number_integer);
                     return;
                 }
 
                 case value_t::number_unsigned:
                 {
-                    x_write(val.m_value.number_unsigned);
+                    dump_integer(val.m_value.number_unsigned);
                     return;
                 }
 
                 case value_t::number_float:
                 {
-                    x_write(val.m_value.number_float);
+                    dump_float(val.m_value.number_float);
                     return;
                 }
 
@@ -6438,14 +6442,40 @@ class basic_json
                         return res + 1;
                     }
 
+                    case 0x00:
+                    case 0x01:
+                    case 0x02:
+                    case 0x03:
+                    case 0x04:
+                    case 0x05:
+                    case 0x06:
+                    case 0x07:
+                    case 0x0b:
+                    case 0x0e:
+                    case 0x0f:
+                    case 0x10:
+                    case 0x11:
+                    case 0x12:
+                    case 0x13:
+                    case 0x14:
+                    case 0x15:
+                    case 0x16:
+                    case 0x17:
+                    case 0x18:
+                    case 0x19:
+                    case 0x1a:
+                    case 0x1b:
+                    case 0x1c:
+                    case 0x1d:
+                    case 0x1e:
+                    case 0x1f:
+                    {
+                        // from c (1 byte) to \uxxxx (6 bytes)
+                        return res + 5;
+                    }
+
                     default:
                     {
-                        if (c >= 0x00 and c <= 0x1f)
-                        {
-                            // from c (1 byte) to \uxxxx (6 bytes)
-                            return res + 5;
-                        }
-
                         return res;
                     }
                 }
@@ -6465,12 +6495,13 @@ class basic_json
 
         @complexity Linear in the length of string @a s.
         */
-        static string_t escape_string(const string_t& s)
+        void dump_escaped(const string_t& s) const
         {
             const auto space = extra_space(s);
             if (space == 0)
             {
-                return s;
+                o.write(s.c_str(), static_cast<std::streamsize>(s.size()));
+                return;
             }
 
             // create a result string of necessary size
@@ -6537,43 +6568,69 @@ class basic_json
                         break;
                     }
 
+                    case 0x00:
+                    case 0x01:
+                    case 0x02:
+                    case 0x03:
+                    case 0x04:
+                    case 0x05:
+                    case 0x06:
+                    case 0x07:
+                    case 0x0b:
+                    case 0x0e:
+                    case 0x0f:
+                    case 0x10:
+                    case 0x11:
+                    case 0x12:
+                    case 0x13:
+                    case 0x14:
+                    case 0x15:
+                    case 0x16:
+                    case 0x17:
+                    case 0x18:
+                    case 0x19:
+                    case 0x1a:
+                    case 0x1b:
+                    case 0x1c:
+                    case 0x1d:
+                    case 0x1e:
+                    case 0x1f:
+                    {
+                        // convert a number 0..15 to its hex representation
+                        // (0..f)
+                        static const char hexify[16] =
+                        {
+                            '0', '1', '2', '3', '4', '5', '6', '7',
+                            '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+                        };
+
+                        // print character c as \uxxxx
+                        for (const char m :
+                    { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f]
+                        })
+                        {
+                            result[++pos] = m;
+                        }
+
+                        ++pos;
+                        break;
+                    }
+
                     default:
                     {
-                        if (c >= 0x00 and c <= 0x1f)
-                        {
-                            // convert a number 0..15 to its hex representation
-                            // (0..f)
-                            static const char hexify[16] =
-                            {
-                                '0', '1', '2', '3', '4', '5', '6', '7',
-                                '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
-                            };
-
-                            // print character c as \uxxxx
-                            for (const char m :
-                        { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f]
-                            })
-                            {
-                                result[++pos] = m;
-                            }
-
-                            ++pos;
-                        }
-                        else
-                        {
-                            // all other characters are added as-is
-                            result[pos++] = c;
-                        }
+                        // all other characters are added as-is
+                        result[pos++] = c;
                         break;
                     }
                 }
             }
 
-            return result;
+            assert(pos == s.size() + space);
+            o.write(result.c_str(), static_cast<std::streamsize>(result.size()));
         }
 
         template<typename NumberType>
-        void x_write(NumberType x)
+        void dump_integer(NumberType x)
         {
             // special case for "0"
             if (x == 0)
@@ -6607,7 +6664,7 @@ class basic_json
             o.write(m_buf.data(), static_cast<std::streamsize>(i));
         }
 
-        void x_write(number_float_t x)
+        void dump_float(number_float_t x)
         {
             // special case for 0.0 and -0.0
             if (x == 0)
@@ -6634,15 +6691,6 @@ class basic_json
             // 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')
             {
@@ -6687,6 +6735,12 @@ class basic_json
 
         /// a (hopefully) large enough character buffer
         std::array < char, 64 > m_buf{{}};
+
+        const std::lconv* loc = nullptr;
+        const char thousands_sep = '\0';
+        const char decimal_point = '\0';
+
+        string_t indent_string = string_t(512, ' ');
     };
 
   public:
diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp
index 45637033..33556311 100644
--- a/test/src/unit-convenience.cpp
+++ b/test/src/unit-convenience.cpp
@@ -49,44 +49,53 @@ TEST_CASE("convenience functions")
 
     SECTION("string escape")
     {
-        CHECK(json::serializer::escape_string("\"") == "\\\"");
-        CHECK(json::serializer::escape_string("\\") == "\\\\");
-        CHECK(json::serializer::escape_string("\b") == "\\b");
-        CHECK(json::serializer::escape_string("\f") == "\\f");
-        CHECK(json::serializer::escape_string("\n") == "\\n");
-        CHECK(json::serializer::escape_string("\r") == "\\r");
-        CHECK(json::serializer::escape_string("\t") == "\\t");
+        const auto check_escaped = [](const char* original,
+                                      const char* escaped)
+        {
+            std::stringstream ss;
+            json::serializer s(ss);
+            s.dump_escaped(original);
+            CHECK(ss.str() == escaped);
+        };
 
-        CHECK(json::serializer::escape_string("\x01") == "\\u0001");
-        CHECK(json::serializer::escape_string("\x02") == "\\u0002");
-        CHECK(json::serializer::escape_string("\x03") == "\\u0003");
-        CHECK(json::serializer::escape_string("\x04") == "\\u0004");
-        CHECK(json::serializer::escape_string("\x05") == "\\u0005");
-        CHECK(json::serializer::escape_string("\x06") == "\\u0006");
-        CHECK(json::serializer::escape_string("\x07") == "\\u0007");
-        CHECK(json::serializer::escape_string("\x08") == "\\b");
-        CHECK(json::serializer::escape_string("\x09") == "\\t");
-        CHECK(json::serializer::escape_string("\x0a") == "\\n");
-        CHECK(json::serializer::escape_string("\x0b") == "\\u000b");
-        CHECK(json::serializer::escape_string("\x0c") == "\\f");
-        CHECK(json::serializer::escape_string("\x0d") == "\\r");
-        CHECK(json::serializer::escape_string("\x0e") == "\\u000e");
-        CHECK(json::serializer::escape_string("\x0f") == "\\u000f");
-        CHECK(json::serializer::escape_string("\x10") == "\\u0010");
-        CHECK(json::serializer::escape_string("\x11") == "\\u0011");
-        CHECK(json::serializer::escape_string("\x12") == "\\u0012");
-        CHECK(json::serializer::escape_string("\x13") == "\\u0013");
-        CHECK(json::serializer::escape_string("\x14") == "\\u0014");
-        CHECK(json::serializer::escape_string("\x15") == "\\u0015");
-        CHECK(json::serializer::escape_string("\x16") == "\\u0016");
-        CHECK(json::serializer::escape_string("\x17") == "\\u0017");
-        CHECK(json::serializer::escape_string("\x18") == "\\u0018");
-        CHECK(json::serializer::escape_string("\x19") == "\\u0019");
-        CHECK(json::serializer::escape_string("\x1a") == "\\u001a");
-        CHECK(json::serializer::escape_string("\x1b") == "\\u001b");
-        CHECK(json::serializer::escape_string("\x1c") == "\\u001c");
-        CHECK(json::serializer::escape_string("\x1d") == "\\u001d");
-        CHECK(json::serializer::escape_string("\x1e") == "\\u001e");
-        CHECK(json::serializer::escape_string("\x1f") == "\\u001f");
+        check_escaped("\"", "\\\"");
+        check_escaped("\\", "\\\\");
+        check_escaped("\b", "\\b");
+        check_escaped("\f", "\\f");
+        check_escaped("\n", "\\n");
+        check_escaped("\r", "\\r");
+        check_escaped("\t", "\\t");
+
+        check_escaped("\x01", "\\u0001");
+        check_escaped("\x02", "\\u0002");
+        check_escaped("\x03", "\\u0003");
+        check_escaped("\x04", "\\u0004");
+        check_escaped("\x05", "\\u0005");
+        check_escaped("\x06", "\\u0006");
+        check_escaped("\x07", "\\u0007");
+        check_escaped("\x08", "\\b");
+        check_escaped("\x09", "\\t");
+        check_escaped("\x0a", "\\n");
+        check_escaped("\x0b", "\\u000b");
+        check_escaped("\x0c", "\\f");
+        check_escaped("\x0d", "\\r");
+        check_escaped("\x0e", "\\u000e");
+        check_escaped("\x0f", "\\u000f");
+        check_escaped("\x10", "\\u0010");
+        check_escaped("\x11", "\\u0011");
+        check_escaped("\x12", "\\u0012");
+        check_escaped("\x13", "\\u0013");
+        check_escaped("\x14", "\\u0014");
+        check_escaped("\x15", "\\u0015");
+        check_escaped("\x16", "\\u0016");
+        check_escaped("\x17", "\\u0017");
+        check_escaped("\x18", "\\u0018");
+        check_escaped("\x19", "\\u0019");
+        check_escaped("\x1a", "\\u001a");
+        check_escaped("\x1b", "\\u001b");
+        check_escaped("\x1c", "\\u001c");
+        check_escaped("\x1d", "\\u001d");
+        check_escaped("\x1e", "\\u001e");
+        check_escaped("\x1f", "\\u001f");
     }
 }

From 059f21aadae5da6f1bebbdd12e3cc1421b39b46d Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Tue, 28 Feb 2017 17:24:03 +0100
Subject: [PATCH 15/23] :lipstick: fixed a warning

snprintf returns an int, but we later assign it a difference_type which
is usually a long.
---
 src/json.hpp      | 2 +-
 src/json.hpp.re2c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index 8d545a93..739f3540 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -6684,7 +6684,7 @@ class basic_json
             static constexpr auto d = std::numeric_limits<number_float_t>::digits10;
 
             // the actual conversion
-            auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
+            long written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
 
             // negative value indicates an error
             assert(written_bytes > 0);
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index b14fb68d..a55792c5 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -6684,7 +6684,7 @@ class basic_json
             static constexpr auto d = std::numeric_limits<number_float_t>::digits10;
 
             // the actual conversion
-            auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
+            long written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
 
             // negative value indicates an error
             assert(written_bytes > 0);

From d69242c6ba40bb809b18d43eb4712b15ccb7a912 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Tue, 28 Feb 2017 19:20:50 +0100
Subject: [PATCH 16/23] :lipstick: cleanup
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- Added comments for the serializer class.
- Added test case for resizing of the indentation string.
- Using std::none_of to check if “.0” needs to be added to
floating-point number.
---
 src/json.hpp                 | 98 +++++++++++++++++++++++-------------
 src/json.hpp.re2c            | 98 +++++++++++++++++++++++-------------
 test/src/unit-inspection.cpp | 12 +++++
 3 files changed, 138 insertions(+), 70 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index 739f3540..507ec9f2 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -6203,6 +6203,9 @@ class basic_json
     class serializer
     {
       public:
+        /*!
+        @param[in] s  output stream to serialize to
+        */
         serializer(std::ostream& s)
             : o(s), loc(std::localeconv()),
               thousands_sep(!loc->thousands_sep ? '\0' : loc->thousands_sep[0]),
@@ -6212,10 +6215,10 @@ class basic_json
         /*!
         @brief internal implementation of the serialization function
 
-        This function is called by the public member function dump and organizes
-        the serialization internally. The indentation level is propagated as
-        additional parameter. In case of arrays and objects, the function is
-        called recursively. Note that
+        This function is called by the public member function dump and
+        organizes the serialization internally. The indentation level is
+        propagated as additional parameter. In case of arrays and objects, the
+        function is called recursively.
 
         - strings and object keys are escaped using `escape_string()`
         - integer numbers are converted implicitly via `operator<<`
@@ -6483,15 +6486,14 @@ class basic_json
         }
 
         /*!
-        @brief escape a string
+        @brief dump escaped string
 
-        Escape a string by replacing certain special characters by a sequence of
-        an escape character (backslash) and another character and other control
-        characters by a sequence of "\u" followed by a four-digit hex
-        representation.
+        Escape a string by replacing certain special characters by a sequence
+        of an escape character (backslash) and another character and other
+        control characters by a sequence of "\u" followed by a four-digit hex
+        representation. The escaped string is written to output stream @a o.
 
         @param[in] s  the string to escape
-        @return  the escaped string
 
         @complexity Linear in the length of string @a s.
         */
@@ -6629,7 +6631,18 @@ class basic_json
             o.write(result.c_str(), static_cast<std::streamsize>(result.size()));
         }
 
-        template<typename NumberType>
+        /*!
+        @brief dump an integer
+
+        Dump a given integer to output stream @a o. Works internally with
+        @a number_buffer.
+
+        @param[in] x  integer number (signed or unsigned) to dump
+        @tparam NumberType either @a number_integer_t or @a number_unsigned_t
+        */
+        template<typename NumberType, detail::enable_if_t <
+                     std::is_same<NumberType, number_unsigned_t>::value or
+                     std::is_same<NumberType, number_integer_t>::value, int> = 0>
         void dump_integer(NumberType x)
         {
             // special case for "0"
@@ -6643,10 +6656,10 @@ class basic_json
             size_t i = 0;
 
             // spare 1 byte for '\0'
-            while (x != 0 and i < m_buf.size() - 1)
+            while (x != 0 and i < number_buffer.size() - 1)
             {
                 const auto digit = std::labs(static_cast<long>(x % 10));
-                m_buf[i++] = static_cast<char>('0' + digit);
+                number_buffer[i++] = static_cast<char>('0' + digit);
                 x /= 10;
             }
 
@@ -6656,14 +6669,22 @@ class basic_json
             if (is_negative)
             {
                 // make sure there is capacity for the '-'
-                assert(i < m_buf.size() - 2);
-                m_buf[i++] = '-';
+                assert(i < number_buffer.size() - 2);
+                number_buffer[i++] = '-';
             }
 
-            std::reverse(m_buf.begin(), m_buf.begin() + i);
-            o.write(m_buf.data(), static_cast<std::streamsize>(i));
+            std::reverse(number_buffer.begin(), number_buffer.begin() + i);
+            o.write(number_buffer.data(), static_cast<std::streamsize>(i));
         }
 
+        /*!
+        @brief dump a floating-point number
+
+        Dump a given floating-point number to output stream @a o. Works
+        internally with @a number_buffer.
+
+        @param[in] x  floating-point number to dump
+        */
         void dump_float(number_float_t x)
         {
             // special case for 0.0 and -0.0
@@ -6684,26 +6705,29 @@ class basic_json
             static constexpr auto d = std::numeric_limits<number_float_t>::digits10;
 
             // the actual conversion
-            long written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
+            long len = snprintf(number_buffer.data(), number_buffer.size(),
+                                "%.*g", d, x);
 
             // negative value indicates an error
-            assert(written_bytes > 0);
+            assert(len > 0);
             // check if buffer was large enough
-            assert(static_cast<size_t>(written_bytes) < m_buf.size());
+            assert(static_cast<size_t>(len) < number_buffer.size());
 
             // 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');
-                assert((end - m_buf.begin()) <= written_bytes);
-                written_bytes = (end - m_buf.begin());
+                const auto end = std::remove(number_buffer.begin(),
+                                             number_buffer.begin() + len,
+                                             thousands_sep);
+                std::fill(end, number_buffer.end(), '\0');
+                assert((end - number_buffer.begin()) <= len);
+                len = (end - number_buffer.begin());
             }
 
             // convert decimal point to '.'
             if (decimal_point != '\0' and decimal_point != '.')
             {
-                for (auto& c : m_buf)
+                for (auto& c : number_buffer)
                 {
                     if (c == decimal_point)
                     {
@@ -6713,16 +6737,15 @@ class basic_json
                 }
             }
 
-            // 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(number_buffer.data(), static_cast<std::streamsize>(len));
 
-            o.write(m_buf.data(), static_cast<std::streamsize>(written_bytes));
+            // determine if need to append ".0"
+            const bool value_is_int_like = std::none_of(number_buffer.begin(),
+                                           number_buffer.begin() + len + 1,
+                                           [](char c)
+            {
+                return c == '.' or c == 'e';
+            });
 
             if (value_is_int_like)
             {
@@ -6731,15 +6754,20 @@ class basic_json
         }
 
       private:
+        /// the output of the serializer
         std::ostream& o;
 
         /// a (hopefully) large enough character buffer
-        std::array < char, 64 > m_buf{{}};
+        std::array<char, 64> number_buffer{{}};
 
+        /// the locale
         const std::lconv* loc = nullptr;
+        /// the locale's thousand separator character
         const char thousands_sep = '\0';
+        /// the locale's decimal point character
         const char decimal_point = '\0';
 
+        /// the indentation string
         string_t indent_string = string_t(512, ' ');
     };
 
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index a55792c5..1dbbb3b5 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -6203,6 +6203,9 @@ class basic_json
     class serializer
     {
       public:
+        /*!
+        @param[in] s  output stream to serialize to
+        */
         serializer(std::ostream& s)
             : o(s), loc(std::localeconv()),
               thousands_sep(!loc->thousands_sep ? '\0' : loc->thousands_sep[0]),
@@ -6212,10 +6215,10 @@ class basic_json
         /*!
         @brief internal implementation of the serialization function
 
-        This function is called by the public member function dump and organizes
-        the serialization internally. The indentation level is propagated as
-        additional parameter. In case of arrays and objects, the function is
-        called recursively. Note that
+        This function is called by the public member function dump and
+        organizes the serialization internally. The indentation level is
+        propagated as additional parameter. In case of arrays and objects, the
+        function is called recursively.
 
         - strings and object keys are escaped using `escape_string()`
         - integer numbers are converted implicitly via `operator<<`
@@ -6483,15 +6486,14 @@ class basic_json
         }
 
         /*!
-        @brief escape a string
+        @brief dump escaped string
 
-        Escape a string by replacing certain special characters by a sequence of
-        an escape character (backslash) and another character and other control
-        characters by a sequence of "\u" followed by a four-digit hex
-        representation.
+        Escape a string by replacing certain special characters by a sequence
+        of an escape character (backslash) and another character and other
+        control characters by a sequence of "\u" followed by a four-digit hex
+        representation. The escaped string is written to output stream @a o.
 
         @param[in] s  the string to escape
-        @return  the escaped string
 
         @complexity Linear in the length of string @a s.
         */
@@ -6629,7 +6631,18 @@ class basic_json
             o.write(result.c_str(), static_cast<std::streamsize>(result.size()));
         }
 
-        template<typename NumberType>
+        /*!
+        @brief dump an integer
+
+        Dump a given integer to output stream @a o. Works internally with
+        @a number_buffer.
+
+        @param[in] x  integer number (signed or unsigned) to dump
+        @tparam NumberType either @a number_integer_t or @a number_unsigned_t
+        */
+        template<typename NumberType, detail::enable_if_t <
+                     std::is_same<NumberType, number_unsigned_t>::value or
+                     std::is_same<NumberType, number_integer_t>::value, int> = 0>
         void dump_integer(NumberType x)
         {
             // special case for "0"
@@ -6643,10 +6656,10 @@ class basic_json
             size_t i = 0;
 
             // spare 1 byte for '\0'
-            while (x != 0 and i < m_buf.size() - 1)
+            while (x != 0 and i < number_buffer.size() - 1)
             {
                 const auto digit = std::labs(static_cast<long>(x % 10));
-                m_buf[i++] = static_cast<char>('0' + digit);
+                number_buffer[i++] = static_cast<char>('0' + digit);
                 x /= 10;
             }
 
@@ -6656,14 +6669,22 @@ class basic_json
             if (is_negative)
             {
                 // make sure there is capacity for the '-'
-                assert(i < m_buf.size() - 2);
-                m_buf[i++] = '-';
+                assert(i < number_buffer.size() - 2);
+                number_buffer[i++] = '-';
             }
 
-            std::reverse(m_buf.begin(), m_buf.begin() + i);
-            o.write(m_buf.data(), static_cast<std::streamsize>(i));
+            std::reverse(number_buffer.begin(), number_buffer.begin() + i);
+            o.write(number_buffer.data(), static_cast<std::streamsize>(i));
         }
 
+        /*!
+        @brief dump a floating-point number
+
+        Dump a given floating-point number to output stream @a o. Works
+        internally with @a number_buffer.
+
+        @param[in] x  floating-point number to dump
+        */
         void dump_float(number_float_t x)
         {
             // special case for 0.0 and -0.0
@@ -6684,26 +6705,29 @@ class basic_json
             static constexpr auto d = std::numeric_limits<number_float_t>::digits10;
 
             // the actual conversion
-            long written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
+            long len = snprintf(number_buffer.data(), number_buffer.size(),
+                                "%.*g", d, x);
 
             // negative value indicates an error
-            assert(written_bytes > 0);
+            assert(len > 0);
             // check if buffer was large enough
-            assert(static_cast<size_t>(written_bytes) < m_buf.size());
+            assert(static_cast<size_t>(len) < number_buffer.size());
 
             // 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');
-                assert((end - m_buf.begin()) <= written_bytes);
-                written_bytes = (end - m_buf.begin());
+                const auto end = std::remove(number_buffer.begin(),
+                                             number_buffer.begin() + len,
+                                             thousands_sep);
+                std::fill(end, number_buffer.end(), '\0');
+                assert((end - number_buffer.begin()) <= len);
+                len = (end - number_buffer.begin());
             }
 
             // convert decimal point to '.'
             if (decimal_point != '\0' and decimal_point != '.')
             {
-                for (auto& c : m_buf)
+                for (auto& c : number_buffer)
                 {
                     if (c == decimal_point)
                     {
@@ -6713,16 +6737,15 @@ class basic_json
                 }
             }
 
-            // 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(number_buffer.data(), static_cast<std::streamsize>(len));
 
-            o.write(m_buf.data(), static_cast<std::streamsize>(written_bytes));
+            // determine if need to append ".0"
+            const bool value_is_int_like = std::none_of(number_buffer.begin(),
+                                           number_buffer.begin() + len + 1,
+                                           [](char c)
+            {
+                return c == '.' or c == 'e';
+            });
 
             if (value_is_int_like)
             {
@@ -6731,15 +6754,20 @@ class basic_json
         }
 
       private:
+        /// the output of the serializer
         std::ostream& o;
 
         /// a (hopefully) large enough character buffer
-        std::array < char, 64 > m_buf{{}};
+        std::array<char, 64> number_buffer{{}};
 
+        /// the locale
         const std::lconv* loc = nullptr;
+        /// the locale's thousand separator character
         const char thousands_sep = '\0';
+        /// the locale's decimal point character
         const char decimal_point = '\0';
 
+        /// the indentation string
         string_t indent_string = string_t(512, ' ');
     };
 
diff --git a/test/src/unit-inspection.cpp b/test/src/unit-inspection.cpp
index aead1258..4900e425 100644
--- a/test/src/unit-inspection.cpp
+++ b/test/src/unit-inspection.cpp
@@ -213,6 +213,18 @@ TEST_CASE("object inspection")
                   "{\n    \"array\": [\n        1,\n        2,\n        3,\n        4\n    ],\n    \"boolean\": false,\n    \"null\": null,\n    \"number\": 42,\n    \"object\": {},\n    \"string\": \"Hello world\"\n}");
         }
 
+        SECTION("indent=x")
+        {
+            CHECK(j.dump().size() == 94);
+            CHECK(j.dump(1).size() == 127);
+            CHECK(j.dump(2).size() == 142);
+            CHECK(j.dump(512).size() == 7792);
+
+            // important test, because it yields a resize of the indent_string
+            // inside the dump() function
+            CHECK(j.dump(1024).size() == 15472);
+        }
+
         SECTION("dump and floating-point numbers")
         {
             auto s = json(42.23).dump();

From f84ac523aa857fe4acc1398da923209fbaad4f92 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Wed, 1 Mar 2017 10:15:07 +0100
Subject: [PATCH 17/23] :memo: added a note to ordered maps

The library does not preserve the insertion order of object keys. There
are frequent requests to change the library in this aspect. The README
and the contribution guidelines now contain links to containers that
can be used to replace std::map to preserve the insertion order.
---
 .github/CONTRIBUTING.md | 2 +-
 README.md               | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 5110aea6..4a621c07 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -64,7 +64,7 @@ Please understand that I cannot accept pull requests changing only file `src/jso
 - Specifically, I am aware of compilation problems with **Microsoft Visual Studio** (there even is an [issue label](https://github.com/nlohmann/json/issues?utf8=✓&q=label%3A%22visual+studio%22+) for these kind of bugs). I understand that even in 2016, complete C++11 support isn't there yet. But please also understand that I do not want to drop features or uglify the code just to make Microsoft's sub-standard compiler happy. The past has shown that there are ways to express the functionality such that the code compiles with the most recent MSVC - unfortunately, this is not the main objective of the project.
 - Please refrain from proposing changes that would **break [JSON](http://json.org) conformance**. If you propose a conformant extension of JSON to be supported by the library, please motivate this extension.
   - We shall not extend the library to **support comments**. There is quite some [controversy](https://www.reddit.com/r/programming/comments/4v6chu/why_json_doesnt_support_comments_douglas_crockford/) around this topic, and there were quite some [issues](https://github.com/nlohmann/json/issues/376) on this. We believe that JSON is fine without comments.
-  - We do not preserve the **insertion order of object elements**. The [JSON standard](https://tools.ietf.org/html/rfc7159.html) defines objects as "an unordered collection of zero or more name/value pairs". To this end, this library does not preserve insertion order of name/value pairs. (In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default.) Note this behavior conforms to the standard, and we shall not it to any other order.
+  - We do not preserve the **insertion order of object elements**. The [JSON standard](https://tools.ietf.org/html/rfc7159.html) defines objects as "an unordered collection of zero or more name/value pairs". To this end, this library does not preserve insertion order of name/value pairs. (In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default.) Note this behavior conforms to the standard, and we shall not it to any other order. If you do want to preserve the insertion order, you can specialize the object type with containers like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map).
 
 - Please do not open pull requests that address **multiple issues**.
 
diff --git a/README.md b/README.md
index 8dea44aa..697a5743 100644
--- a/README.md
+++ b/README.md
@@ -876,6 +876,7 @@ The library is currently used in Apple macOS Sierra and iOS 10. I am not sure wh
   - The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
 - The code can be compiled without C++ **runtime type identification** features; that is, you can use the `-fno-rtti` compiler flag.
 - **Exceptions** are used widly within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION`. In this case, exceptions are replaced by an `abort()` call.
+- By default, the library does not preserve the **insertion order of object elements**. This is standards-compliant, as the [JSON standard](https://tools.ietf.org/html/rfc7159.html) defines objects as "an unordered collection of zero or more name/value pairs". If you do want to preserve the insertion order, you can specialize the object type with containers like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map).
 
 
 ## Execute unit tests

From 6b3912d936039b56c24f49e0f81e8bac6627e11f Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Wed, 1 Mar 2017 17:26:32 +0100
Subject: [PATCH 18/23] :memo: added note to 3.0.0 wiki page #474

I created a wiki page
https://github.com/nlohmann/json/wiki/Road-toward-3.0.0 to describe the
transition toward version 3.0.0. On this page, all API-breaking changes
shall be documented.
---
 README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/README.md b/README.md
index 697a5743..1ada712c 100644
--- a/README.md
+++ b/README.md
@@ -65,6 +65,8 @@ to the files you want to use JSON objects. That's it. Do not forget to set the n
 
 :beer: If you are using OS X and [Homebrew](http://brew.sh), just type `brew tap nlohmann/json` and `brew install nlohmann_json` and you're set. If you want the bleeding edge rather than the latest release, use `brew install nlohmann_json --HEAD`.
 
+:warning: [Version 3.0.0](https://github.com/nlohmann/json/wiki/Road-toward-3.0.0) is currently under development. Branch `develop` is used for the ongoing work and is probably **unstable**. Please use the `master` branch for the last stable version 2.1.1.
+
 
 ## Examples
 

From 7b8fd864e220eeb2b7eac06f20a921ea0d93983f Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Wed, 1 Mar 2017 17:49:03 +0100
Subject: [PATCH 19/23] :fire: removed deprecated constructor #480

The constructor basic_json(std::istream&, const parser_callback_t) has
been deprecated since version 2.0.0. This commit removes it together
with its code example, deprecation macro, and test cases. The code now
also compiles with -W-deprecated-declarations.
---
 Makefile                                |  2 -
 doc/examples/basic_json__istream.cpp    | 57 -------------------------
 doc/examples/basic_json__istream.link   |  1 -
 doc/examples/basic_json__istream.output | 34 ---------------
 src/json.hpp                            | 44 -------------------
 src/json.hpp.re2c                       | 44 -------------------
 test/src/unit-constructor1.cpp          | 36 ----------------
 7 files changed, 218 deletions(-)
 delete mode 100644 doc/examples/basic_json__istream.cpp
 delete mode 100644 doc/examples/basic_json__istream.link
 delete mode 100644 doc/examples/basic_json__istream.output

diff --git a/Makefile b/Makefile
index ac75bcaf..1b7cf8c1 100644
--- a/Makefile
+++ b/Makefile
@@ -49,7 +49,6 @@ doctest:
 # -Wno-documentation-unknown-command: code uses user-defined commands like @complexity
 # -Wno-exit-time-destructors: warning in Catch code
 # -Wno-keyword-macro: unit-tests use "#define private public"
-# -Wno-deprecated-declarations: some functions are deprecated until 3.0.0
 # -Wno-range-loop-analysis: iterator_wrapper tests tests "for(const auto i...)"
 pedantic:
 	$(MAKE) json_unit CXXFLAGS="\
@@ -59,7 +58,6 @@ pedantic:
 		-Wno-documentation-unknown-command \
 		-Wno-exit-time-destructors \
 		-Wno-keyword-macro \
-		-Wno-deprecated-declarations \
 		-Wno-range-loop-analysis"
 
 
diff --git a/doc/examples/basic_json__istream.cpp b/doc/examples/basic_json__istream.cpp
deleted file mode 100644
index 32885b22..00000000
--- a/doc/examples/basic_json__istream.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-#include <json.hpp>
-
-using json = nlohmann::json;
-
-int main()
-{
-    // a JSON text
-    auto text = R"(
-    {
-        "Image": {
-            "Width":  800,
-            "Height": 600,
-            "Title":  "View from 15th Floor",
-            "Thumbnail": {
-                "Url":    "http://www.example.com/image/481989943",
-                "Height": 125,
-                "Width":  100
-            },
-            "Animated" : false,
-            "IDs": [116, 943, 234, 38793]
-        }
-    }
-    )";
-
-    // fill a stream with JSON text
-    std::stringstream ss;
-    ss << text;
-
-    // create JSON from stream
-    json j_complete(ss); // deprecated!
-    // shall be replaced by: json j_complete = json::parse(ss);
-    std::cout << std::setw(4) << j_complete << "\n\n";
-
-
-    // define parser callback
-    json::parser_callback_t cb = [](int depth, json::parse_event_t event, json & parsed)
-    {
-        // skip object elements with key "Thumbnail"
-        if (event == json::parse_event_t::key and parsed == json("Thumbnail"))
-        {
-            return false;
-        }
-        else
-        {
-            return true;
-        }
-    };
-
-    // fill a stream with JSON text
-    ss.clear();
-    ss << text;
-
-    // create JSON from stream (with callback)
-    json j_filtered(ss, cb);
-    // shall be replaced by: json j_filtered = json::parse(ss, cb);
-    std::cout << std::setw(4) << j_filtered << '\n';
-}
\ No newline at end of file
diff --git a/doc/examples/basic_json__istream.link b/doc/examples/basic_json__istream.link
deleted file mode 100644
index eb165e2f..00000000
--- a/doc/examples/basic_json__istream.link
+++ /dev/null
@@ -1 +0,0 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/R6dzpKXlxrttShf7"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__istream.output b/doc/examples/basic_json__istream.output
deleted file mode 100644
index 279a7ff7..00000000
--- a/doc/examples/basic_json__istream.output
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-    "Image": {
-        "Animated": false,
-        "Height": 600,
-        "IDs": [
-            116,
-            943,
-            234,
-            38793
-        ],
-        "Thumbnail": {
-            "Height": 125,
-            "Url": "http://www.example.com/image/481989943",
-            "Width": 100
-        },
-        "Title": "View from 15th Floor",
-        "Width": 800
-    }
-}
-
-{
-    "Image": {
-        "Animated": false,
-        "Height": 600,
-        "IDs": [
-            116,
-            943,
-            234,
-            38793
-        ],
-        "Title": "View from 15th Floor",
-        "Width": 800
-    }
-}
diff --git a/src/json.hpp b/src/json.hpp
index 507ec9f2..ff0ce5ad 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -81,15 +81,6 @@ SOFTWARE.
     #pragma GCC diagnostic ignored "-Wdocumentation"
 #endif
 
-// allow for portable deprecation warnings
-#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
-    #define JSON_DEPRECATED __attribute__((deprecated))
-#elif defined(_MSC_VER)
-    #define JSON_DEPRECATED __declspec(deprecated)
-#else
-    #define JSON_DEPRECATED
-#endif
-
 // allow to disable exceptions
 #if not defined(JSON_NOEXCEPTION) || defined(__EXCEPTIONS)
     #define JSON_THROW(exception) throw exception
@@ -2362,40 +2353,6 @@ class basic_json
         assert_invariant();
     }
 
-    /*!
-    @brief construct a JSON value given an input stream
-
-    @param[in,out] i  stream to read a serialized JSON value from
-    @param[in] cb a parser callback function of type @ref parser_callback_t
-    which is used to control the deserialization by filtering unwanted values
-    (optional)
-
-    @complexity Linear in the length of the input. The parser is a predictive
-    LL(1) parser. The complexity can be higher if the parser callback function
-    @a cb has a super-linear complexity.
-
-    @note A UTF-8 byte order mark is silently ignored.
-
-    @deprecated This constructor is deprecated and will be removed in version
-      3.0.0 to unify the interface of the library. Deserialization will be
-      done by stream operators or by calling one of the `parse` functions,
-      e.g. @ref parse(std::istream&, const parser_callback_t). That is, calls
-      like `json j(i);` for an input stream @a i need to be replaced by
-      `json j = json::parse(i);`. See the example below.
-
-    @liveexample{The example below demonstrates constructing a JSON value from
-    a `std::stringstream` with and without callback
-    function.,basic_json__istream}
-
-    @since version 2.0.0, deprecated in version 2.0.3, to be removed in
-           version 3.0.0
-    */
-    JSON_DEPRECATED
-    explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr)
-    {
-        *this = parser(i, cb).parse();
-        assert_invariant();
-    }
 
     ///////////////////////////////////////
     // other constructors and destructor //
@@ -13113,7 +13070,6 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std
 
 // clean up
 #undef JSON_CATCH
-#undef JSON_DEPRECATED
 #undef JSON_THROW
 #undef JSON_TRY
 
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 1dbbb3b5..fc7cf965 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -81,15 +81,6 @@ SOFTWARE.
     #pragma GCC diagnostic ignored "-Wdocumentation"
 #endif
 
-// allow for portable deprecation warnings
-#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
-    #define JSON_DEPRECATED __attribute__((deprecated))
-#elif defined(_MSC_VER)
-    #define JSON_DEPRECATED __declspec(deprecated)
-#else
-    #define JSON_DEPRECATED
-#endif
-
 // allow to disable exceptions
 #if not defined(JSON_NOEXCEPTION) || defined(__EXCEPTIONS)
     #define JSON_THROW(exception) throw exception
@@ -2362,40 +2353,6 @@ class basic_json
         assert_invariant();
     }
 
-    /*!
-    @brief construct a JSON value given an input stream
-
-    @param[in,out] i  stream to read a serialized JSON value from
-    @param[in] cb a parser callback function of type @ref parser_callback_t
-    which is used to control the deserialization by filtering unwanted values
-    (optional)
-
-    @complexity Linear in the length of the input. The parser is a predictive
-    LL(1) parser. The complexity can be higher if the parser callback function
-    @a cb has a super-linear complexity.
-
-    @note A UTF-8 byte order mark is silently ignored.
-
-    @deprecated This constructor is deprecated and will be removed in version
-      3.0.0 to unify the interface of the library. Deserialization will be
-      done by stream operators or by calling one of the `parse` functions,
-      e.g. @ref parse(std::istream&, const parser_callback_t). That is, calls
-      like `json j(i);` for an input stream @a i need to be replaced by
-      `json j = json::parse(i);`. See the example below.
-
-    @liveexample{The example below demonstrates constructing a JSON value from
-    a `std::stringstream` with and without callback
-    function.,basic_json__istream}
-
-    @since version 2.0.0, deprecated in version 2.0.3, to be removed in
-           version 3.0.0
-    */
-    JSON_DEPRECATED
-    explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr)
-    {
-        *this = parser(i, cb).parse();
-        assert_invariant();
-    }
 
     ///////////////////////////////////////
     // other constructors and destructor //
@@ -12147,7 +12104,6 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std
 
 // clean up
 #undef JSON_CATCH
-#undef JSON_DEPRECATED
 #undef JSON_THROW
 #undef JSON_TRY
 
diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp
index b3354b58..9363f0ba 100644
--- a/test/src/unit-constructor1.cpp
+++ b/test/src/unit-constructor1.cpp
@@ -1281,40 +1281,4 @@ TEST_CASE("constructors")
             }
         }
     }
-
-    SECTION("create a JSON value from an input stream")
-    {
-        SECTION("std::stringstream")
-        {
-            std::stringstream ss;
-            ss << "[\"foo\",1,2,3,false,{\"one\":1}]";
-            json j(ss);
-            CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
-        }
-
-        SECTION("with callback function")
-        {
-            std::stringstream ss;
-            ss << "[\"foo\",1,2,3,false,{\"one\":1}]";
-            json j(ss, [](int, json::parse_event_t, const json & val)
-            {
-                // filter all number(2) elements
-                if (val == json(2))
-                {
-                    return false;
-                }
-                else
-                {
-                    return true;
-                }
-            });
-            CHECK(j == json({"foo", 1, 3, false, {{"one", 1}}}));
-        }
-
-        SECTION("std::ifstream")
-        {
-            std::ifstream f("test/data/json_tests/pass1.json");
-            json j(f);
-        }
-    }
 }

From 01d3a006b1dd8b80ee9898de7b5ab2a17484f46e Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Thu, 2 Mar 2017 18:13:19 +0100
Subject: [PATCH 20/23] :arrow_up: updated Catch to v1.8.1

---
 test/thirdparty/catch/catch.hpp | 801 ++++++++++++++++++++------------
 1 file changed, 507 insertions(+), 294 deletions(-)

diff --git a/test/thirdparty/catch/catch.hpp b/test/thirdparty/catch/catch.hpp
index 2a09fd19..6f9334ba 100644
--- a/test/thirdparty/catch/catch.hpp
+++ b/test/thirdparty/catch/catch.hpp
@@ -1,6 +1,6 @@
 /*
- *  Catch v1.7.2
- *  Generated: 2017-02-13 15:57:33.350226
+ *  Catch v1.8.1
+ *  Generated: 2017-03-01 16:04:19.016511
  *  ----------------------------------------------------------
  *  This file has been merged from multiple headers. Please don't edit it directly
  *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@@ -40,6 +40,12 @@
 #elif defined __GNUC__
 #    pragma GCC diagnostic ignored "-Wvariadic-macros"
 #    pragma GCC diagnostic ignored "-Wunused-variable"
+
+     // For newer version we can use __Pragma to disable the warnings locally
+#    if __GNUC__ == 4 && __GNUC_MINOR__ >= 4 && __GNUC_MINOR__ <= 7
+#        pragma GCC diagnostic ignored "-Wparentheses"
+#    endif
+
 #    pragma GCC diagnostic push
 #    pragma GCC diagnostic ignored "-Wpadded"
 #endif
@@ -82,6 +88,7 @@
 // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
 // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
 // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
+// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
 // ****************
 // Note to maintainers: if new toggles are added please document them
 // in configuration.md, too
@@ -117,11 +124,29 @@
 #  endif
 
 #   if defined(CATCH_CPP11_OR_GREATER)
-#       define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+#       define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+            _Pragma( "clang diagnostic push" ) \
+            _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+#       define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+            _Pragma( "clang diagnostic pop" )
 #   endif
 
 #endif // __clang__
 
+////////////////////////////////////////////////////////////////////////////////
+// Cygwin
+#ifdef __CYGWIN__
+
+#   if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+#       define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+#   endif
+
+// Required for some versions of Cygwin to declare gettimeofday
+// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
+#   define _BSD_SOURCE
+
+#endif // __CYGWIN__
+
 ////////////////////////////////////////////////////////////////////////////////
 // Borland
 #ifdef __BORLANDC__
@@ -144,12 +169,20 @@
 // GCC
 #ifdef __GNUC__
 
+#   if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
+#       define CATCH_GCC_HAS_NEW_PRAGMA
+#   endif
+
 #   if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
 #       define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
 #   endif
 
-#   if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER)
-#       define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" )
+#   if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_GCC_HAS_NEW_PRAGMA)
+#       define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+            _Pragma( "GCC diagnostic push" ) \
+            _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" )
+#       define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+            _Pragma( "GCC diagnostic pop" )
 #   endif
 
 // - otherwise more recent versions define __cplusplus >= 201103L
@@ -290,9 +323,14 @@
 #if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH)
 #   define CATCH_CONFIG_WINDOWS_SEH
 #endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+#   define CATCH_CONFIG_POSIX_SIGNALS
+#endif
 
 #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
 #   define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+#   define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
 #endif
 
 // noexcept support:
@@ -871,6 +909,9 @@ namespace Catch {
         template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& );
         template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& );
         template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& );
+
+	private:
+		DecomposedExpression& operator = (DecomposedExpression const&);
     };
 
     struct AssertionInfo
@@ -967,313 +1008,153 @@ namespace Catch {
 namespace Matchers {
     namespace Impl {
 
-    namespace Generic {
-        template<typename ExpressionT> class AllOf;
-        template<typename ExpressionT> class AnyOf;
-        template<typename ExpressionT> class Not;
-    }
+        template<typename ArgT> struct MatchAllOf;
+        template<typename ArgT> struct MatchAnyOf;
+        template<typename ArgT> struct MatchNotOf;
 
-    template<typename ExpressionT>
-    struct Matcher : SharedImpl<IShared>
-    {
-        typedef ExpressionT ExpressionType;
-
-        virtual ~Matcher() {}
-        virtual Ptr<Matcher> clone() const = 0;
-        virtual bool match( ExpressionT const& expr ) const = 0;
-        virtual std::string toString() const = 0;
-
-        Generic::AllOf<ExpressionT> operator && ( Matcher<ExpressionT> const& other ) const;
-        Generic::AnyOf<ExpressionT> operator || ( Matcher<ExpressionT> const& other ) const;
-        Generic::Not<ExpressionT> operator ! () const;
-    };
-
-    template<typename DerivedT, typename ExpressionT>
-    struct MatcherImpl : Matcher<ExpressionT> {
-
-        virtual Ptr<Matcher<ExpressionT> > clone() const {
-            return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
-        }
-    };
-
-    namespace Generic {
-        template<typename ExpressionT>
-        class Not : public MatcherImpl<Not<ExpressionT>, ExpressionT> {
+        class MatcherUntypedBase {
         public:
-            explicit Not( Matcher<ExpressionT> const& matcher ) : m_matcher(matcher.clone()) {}
-            Not( Not const& other ) : m_matcher( other.m_matcher ) {}
-
-            virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE {
-                return !m_matcher->match( expr );
+            std::string toString() const {
+                if( m_cachedToString.empty() )
+                    m_cachedToString = describe();
+                return m_cachedToString;
             }
 
-            virtual std::string toString() const CATCH_OVERRIDE {
-                return "not " + m_matcher->toString();
-            }
+        protected:
+            virtual std::string describe() const = 0;
+            mutable std::string m_cachedToString;
         private:
-            Ptr< Matcher<ExpressionT> > m_matcher;
+            MatcherUntypedBase& operator = ( MatcherUntypedBase const& );
         };
 
-        template<typename ExpressionT>
-        class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
-        public:
+        template<typename ObjectT, typename ComparatorT = ObjectT>
+        struct MatcherBase : MatcherUntypedBase {
 
-            AllOf() {}
-            AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
+            virtual bool match( ObjectT const& arg ) const = 0;
 
-            AllOf& add( Matcher<ExpressionT> const& matcher ) {
-                m_matchers.push_back( matcher.clone() );
-                return *this;
-            }
-            virtual bool match( ExpressionT const& expr ) const
-            {
-                for( std::size_t i = 0; i < m_matchers.size(); ++i )
-                    if( !m_matchers[i]->match( expr ) )
+            MatchAllOf<ComparatorT> operator && ( MatcherBase const& other ) const;
+            MatchAnyOf<ComparatorT> operator || ( MatcherBase const& other ) const;
+            MatchNotOf<ComparatorT> operator ! () const;
+        };
+
+        template<typename ArgT>
+        struct MatchAllOf : MatcherBase<ArgT> {
+            virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if (!m_matchers[i]->match(arg))
                         return false;
+                }
                 return true;
             }
-            virtual std::string toString() const {
-                std::ostringstream oss;
-                oss << "( ";
+            virtual std::string describe() const CATCH_OVERRIDE {
+                std::string description;
+                description.reserve( 4 + m_matchers.size()*32 );
+                description += "( ";
                 for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
                     if( i != 0 )
-                        oss << " and ";
-                    oss << m_matchers[i]->toString();
+                        description += " and ";
+                    description += m_matchers[i]->toString();
                 }
-                oss << " )";
-                return oss.str();
+                description += " )";
+                return description;
             }
 
-            AllOf operator && ( Matcher<ExpressionT> const& other ) const {
-                AllOf allOfExpr( *this );
-                allOfExpr.add( other );
-                return allOfExpr;
-            }
-
-        private:
-            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
-        };
-
-        template<typename ExpressionT>
-        class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
-        public:
-
-            AnyOf() {}
-            AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
-
-            AnyOf& add( Matcher<ExpressionT> const& matcher ) {
-                m_matchers.push_back( matcher.clone() );
+            MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) {
+                m_matchers.push_back( &other );
                 return *this;
             }
-            virtual bool match( ExpressionT const& expr ) const
-            {
-                for( std::size_t i = 0; i < m_matchers.size(); ++i )
-                    if( m_matchers[i]->match( expr ) )
+
+            std::vector<MatcherBase<ArgT> const*> m_matchers;
+        };
+        template<typename ArgT>
+        struct MatchAnyOf : MatcherBase<ArgT> {
+
+            virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if (m_matchers[i]->match(arg))
                         return true;
+                }
                 return false;
             }
-            virtual std::string toString() const {
-                std::ostringstream oss;
-                oss << "( ";
+            virtual std::string describe() const CATCH_OVERRIDE {
+                std::string description;
+                description.reserve( 4 + m_matchers.size()*32 );
+                description += "( ";
                 for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
                     if( i != 0 )
-                        oss << " or ";
-                    oss << m_matchers[i]->toString();
+                        description += " or ";
+                    description += m_matchers[i]->toString();
                 }
-                oss << " )";
-                return oss.str();
+                description += " )";
+                return description;
             }
 
-            AnyOf operator || ( Matcher<ExpressionT> const& other ) const {
-                AnyOf anyOfExpr( *this );
-                anyOfExpr.add( other );
-                return anyOfExpr;
+            MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) {
+                m_matchers.push_back( &other );
+                return *this;
             }
 
-        private:
-            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+            std::vector<MatcherBase<ArgT> const*> m_matchers;
         };
 
-    } // namespace Generic
+        template<typename ArgT>
+        struct MatchNotOf : MatcherBase<ArgT> {
 
-    template<typename ExpressionT>
-    Generic::AllOf<ExpressionT> Matcher<ExpressionT>::operator && ( Matcher<ExpressionT> const& other ) const {
-        Generic::AllOf<ExpressionT> allOfExpr;
-        allOfExpr.add( *this );
-        allOfExpr.add( other );
-        return allOfExpr;
-    }
-
-    template<typename ExpressionT>
-    Generic::AnyOf<ExpressionT> Matcher<ExpressionT>::operator || ( Matcher<ExpressionT> const& other ) const {
-        Generic::AnyOf<ExpressionT> anyOfExpr;
-        anyOfExpr.add( *this );
-        anyOfExpr.add( other );
-        return anyOfExpr;
-    }
-
-    template<typename ExpressionT>
-    Generic::Not<ExpressionT> Matcher<ExpressionT>::operator ! () const {
-        return Generic::Not<ExpressionT>( *this );
-    }
-
-    namespace StdString {
-
-        inline std::string makeString( std::string const& str ) { return str; }
-        inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
-
-        struct CasedString
-        {
-            CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
-            :   m_caseSensitivity( caseSensitivity ),
-                m_str( adjustString( str ) )
-            {}
-            std::string adjustString( std::string const& str ) const {
-                return m_caseSensitivity == CaseSensitive::No
-                    ? toLower( str )
-                    : str;
+            MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {}
 
+            virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+                return !m_underlyingMatcher.match( arg );
             }
-            std::string toStringSuffix() const
-            {
-                return m_caseSensitivity == CaseSensitive::No
-                    ? " (case insensitive)"
-                    : std::string();
+
+            virtual std::string describe() const CATCH_OVERRIDE {
+                return "not " + m_underlyingMatcher.toString();
             }
-            CaseSensitive::Choice m_caseSensitivity;
-            std::string m_str;
+            MatcherBase<ArgT> const& m_underlyingMatcher;
         };
 
-        struct Equals : MatcherImpl<Equals, std::string> {
-            Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
-            :   m_data( str, caseSensitivity )
-            {}
-            Equals( Equals const& other ) : m_data( other.m_data ){}
+        template<typename ObjectT, typename ComparatorT>
+        MatchAllOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator && ( MatcherBase const& other ) const {
+            return MatchAllOf<ComparatorT>() && *this && other;
+        }
+        template<typename ObjectT, typename ComparatorT>
+        MatchAnyOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator || ( MatcherBase const& other ) const {
+            return MatchAnyOf<ComparatorT>() || *this || other;
+        }
+        template<typename ObjectT, typename ComparatorT>
+        MatchNotOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator ! () const {
+            return MatchNotOf<ComparatorT>( *this );
+        }
 
-            virtual ~Equals();
-
-            virtual bool match( std::string const& expr ) const {
-                return m_data.m_str == m_data.adjustString( expr );;
-            }
-            virtual std::string toString() const {
-                return "equals: \"" + m_data.m_str + '"' + m_data.toStringSuffix();
-            }
-
-            CasedString m_data;
-        };
-
-        struct Contains : MatcherImpl<Contains, std::string> {
-            Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
-            : m_data( substr, caseSensitivity ){}
-            Contains( Contains const& other ) : m_data( other.m_data ){}
-
-            virtual ~Contains();
-
-            virtual bool match( std::string const& expr ) const {
-                return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos;
-            }
-            virtual std::string toString() const {
-                return "contains: \"" + m_data.m_str  + '"' + m_data.toStringSuffix();
-            }
-
-            CasedString m_data;
-        };
-
-        struct StartsWith : MatcherImpl<StartsWith, std::string> {
-            StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
-            : m_data( substr, caseSensitivity ){}
-
-            StartsWith( StartsWith const& other ) : m_data( other.m_data ){}
-
-            virtual ~StartsWith();
-
-            virtual bool match( std::string const& expr ) const {
-                return startsWith( m_data.adjustString( expr ), m_data.m_str );
-            }
-            virtual std::string toString() const {
-                return "starts with: \"" + m_data.m_str + '"' + m_data.toStringSuffix();
-            }
-
-            CasedString m_data;
-        };
-
-        struct EndsWith : MatcherImpl<EndsWith, std::string> {
-            EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
-            : m_data( substr, caseSensitivity ){}
-            EndsWith( EndsWith const& other ) : m_data( other.m_data ){}
-
-            virtual ~EndsWith();
-
-            virtual bool match( std::string const& expr ) const {
-                return endsWith( m_data.adjustString( expr ), m_data.m_str );
-            }
-            virtual std::string toString() const {
-                return "ends with: \"" + m_data.m_str + '"' + m_data.toStringSuffix();
-            }
-
-            CasedString m_data;
-        };
-    } // namespace StdString
     } // namespace Impl
 
     // The following functions create the actual matcher objects.
     // This allows the types to be inferred
-    template<typename ExpressionT>
-    inline Impl::Generic::Not<ExpressionT> Not( Impl::Matcher<ExpressionT> const& m ) {
-        return Impl::Generic::Not<ExpressionT>( m );
+    // - deprecated: prefer ||, && and !
+    template<typename T>
+    inline Impl::MatchNotOf<T> Not( Impl::MatcherBase<T> const& underlyingMatcher ) {
+        return Impl::MatchNotOf<T>( underlyingMatcher );
     }
-
-    template<typename ExpressionT>
-    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2 ) {
-        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
+    template<typename T>
+    inline Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
+        return Impl::MatchAllOf<T>() && m1 && m2;
     }
-    template<typename ExpressionT>
-    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2,
-                                                    Impl::Matcher<ExpressionT> const& m3 ) {
-        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+    template<typename T>
+    inline Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
+        return Impl::MatchAllOf<T>() && m1 && m2 && m3;
     }
-    template<typename ExpressionT>
-    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2 ) {
-        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
+    template<typename T>
+    inline Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
+        return Impl::MatchAnyOf<T>() || m1 || m2;
     }
-    template<typename ExpressionT>
-    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2,
-                                                    Impl::Matcher<ExpressionT> const& m3 ) {
-        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
-    }
-
-    inline Impl::StdString::Equals      Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
-        return Impl::StdString::Equals( str, caseSensitivity );
-    }
-    inline Impl::StdString::Equals      Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
-        return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity );
-    }
-    inline Impl::StdString::Contains    Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
-        return Impl::StdString::Contains( substr, caseSensitivity );
-    }
-    inline Impl::StdString::Contains    Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
-        return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity );
-    }
-    inline Impl::StdString::StartsWith  StartsWith( std::string const& substr ) {
-        return Impl::StdString::StartsWith( substr );
-    }
-    inline Impl::StdString::StartsWith  StartsWith( const char* substr ) {
-        return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
-    }
-    inline Impl::StdString::EndsWith    EndsWith( std::string const& substr ) {
-        return Impl::StdString::EndsWith( substr );
-    }
-    inline Impl::StdString::EndsWith    EndsWith( const char* substr ) {
-        return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
+    template<typename T>
+    inline Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
+        return Impl::MatchAnyOf<T>() || m1 || m2 || m3;
     }
 
 } // namespace Matchers
 
 using namespace Matchers;
+using Matchers::Impl::MatcherBase;
 
 } // namespace Catch
 
@@ -1328,7 +1209,7 @@ namespace Catch {
         void captureResult( ResultWas::OfType resultType );
         void captureExpression();
         void captureExpectedException( std::string const& expectedMessage );
-        void captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher );
+        void captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher );
         void handleResult( AssertionResult const& result );
         void react();
         bool shouldDebugBreak() const;
@@ -1358,6 +1239,7 @@ namespace Catch {
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
 #endif
 
 #include <cstddef>
@@ -1874,6 +1756,8 @@ class ExpressionLhs : public DecomposedExpression {
 public:
     ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {}
 
+    ExpressionLhs& operator = ( const ExpressionLhs& );
+
     template<typename RhsT>
     BinaryExpression<T, Internal::IsEqualTo, RhsT const&>
     operator == ( RhsT const& rhs ) {
@@ -1952,6 +1836,8 @@ public:
     BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs )
         : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {}
 
+    BinaryExpression& operator = ( BinaryExpression& );
+
     void endExpression() const {
         m_rb
             .setResultType( Internal::compare<Op>( m_lhs, m_rhs ) )
@@ -2246,6 +2132,14 @@ namespace Catch {
 
 }
 
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+///////////////////////////////////////////////////////////////////////////////
+// We can speedup compilation significantly by breaking into debugger lower in
+// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER
+// macro in each assertion
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+    resultBuilder.react();
+#else
 ///////////////////////////////////////////////////////////////////////////////
 // In the event of a failure works out if the debugger needs to be invoked
 // and/or an exception thrown and takes appropriate action.
@@ -2254,6 +2148,7 @@ namespace Catch {
 #define INTERNAL_CATCH_REACT( resultBuilder ) \
     if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
     resultBuilder.react();
+#endif
 
 ///////////////////////////////////////////////////////////////////////////////
 #define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \
@@ -2262,6 +2157,7 @@ namespace Catch {
         try { \
             CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
             ( __catchResult <= expr ).endExpression(); \
+            CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
         } \
         catch( ... ) { \
             __catchResult.useActiveException( resultDisposition ); \
@@ -2825,12 +2721,14 @@ namespace Detail {
     public:
         explicit Approx ( double value )
         :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+            m_margin( 0.0 ),
             m_scale( 1.0 ),
             m_value( value )
         {}
 
         Approx( Approx const& other )
         :   m_epsilon( other.m_epsilon ),
+            m_margin( other.m_margin ),
             m_scale( other.m_scale ),
             m_value( other.m_value )
         {}
@@ -2842,6 +2740,7 @@ namespace Detail {
         Approx operator()( double value ) {
             Approx approx( value );
             approx.epsilon( m_epsilon );
+            approx.margin( m_margin );
             approx.scale( m_scale );
             return approx;
         }
@@ -2851,7 +2750,11 @@ namespace Detail {
         friend bool operator == ( const T& lhs, Approx const& rhs ) {
             // Thanks to Richard Harris for his help refining this formula
             auto lhs_v = double(lhs);
-            return std::fabs( lhs_v - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs_v), std::fabs(rhs.m_value) ) );
+            bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value)));
+            if (relativeOK) {
+                return true;
+            }
+            return std::fabs(lhs_v - rhs.m_value) < rhs.m_margin;
         }
 
         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
@@ -2895,7 +2798,11 @@ namespace Detail {
 #else
         friend bool operator == ( double lhs, Approx const& rhs ) {
             // Thanks to Richard Harris for his help refining this formula
-            return std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) );
+            bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) );
+            if (relativeOK) {
+                return true;
+            }
+            return std::fabs(lhs - rhs.m_value) < rhs.m_margin;
         }
 
         friend bool operator == ( Approx const& lhs, double rhs ) {
@@ -2936,6 +2843,11 @@ namespace Detail {
             return *this;
         }
 
+        Approx& margin( double newMargin ) {
+            m_margin = newMargin;
+            return *this;
+        }
+
         Approx& scale( double newScale ) {
             m_scale = newScale;
             return *this;
@@ -2949,6 +2861,7 @@ namespace Detail {
 
     private:
         double m_epsilon;
+        double m_margin;
         double m_scale;
         double m_value;
     };
@@ -2961,6 +2874,153 @@ inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
 
 } // end namespace Catch
 
+// #included from: internal/catch_matchers_string.h
+#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+
+    namespace StdString {
+
+        struct CasedString
+        {
+            CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity );
+            std::string adjustString( std::string const& str ) const;
+            std::string caseSensitivitySuffix() const;
+
+            CaseSensitive::Choice m_caseSensitivity;
+            std::string m_str;
+        };
+
+        struct StringMatcherBase : MatcherBase<std::string> {
+            StringMatcherBase( std::string operation, CasedString const& comparator );
+            virtual std::string describe() const CATCH_OVERRIDE;
+
+            CasedString m_comparator;
+            std::string m_operation;
+        };
+
+        struct EqualsMatcher : StringMatcherBase {
+            EqualsMatcher( CasedString const& comparator );
+            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+        };
+        struct ContainsMatcher : StringMatcherBase {
+            ContainsMatcher( CasedString const& comparator );
+            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+        };
+        struct StartsWithMatcher : StringMatcherBase {
+            StartsWithMatcher( CasedString const& comparator );
+            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+        };
+        struct EndsWithMatcher : StringMatcherBase {
+            EndsWithMatcher( CasedString const& comparator );
+            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+        };
+
+    } // namespace StdString
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+
+    StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+
+} // namespace Matchers
+} // namespace Catch
+
+// #included from: internal/catch_matchers_vector.h
+#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+
+    namespace Vector {
+
+        template<typename T>
+        struct ContainsElementMatcher : MatcherBase<std::vector<T>, T> {
+
+            ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
+
+            bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+                return std::find(v.begin(), v.end(), m_comparator) != v.end();
+            }
+
+            virtual std::string describe() const CATCH_OVERRIDE {
+                return "Contains: " + Catch::toString( m_comparator );
+            }
+
+            T const& m_comparator;
+        };
+
+        template<typename T>
+        struct ContainsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
+
+            ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+
+            bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+                // !TBD: see note in EqualsMatcher
+                if (m_comparator.size() > v.size())
+                    return false;
+                for (size_t i = 0; i < m_comparator.size(); ++i)
+                    if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end())
+                        return false;
+                return true;
+            }
+            virtual std::string describe() const CATCH_OVERRIDE {
+                return "Contains: " + Catch::toString( m_comparator );
+            }
+
+            std::vector<T> const& m_comparator;
+        };
+
+        template<typename T>
+        struct EqualsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
+
+            EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+
+            bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+                // !TBD: This currently works if all elements can be compared using !=
+                // - a more general approach would be via a compare template that defaults
+                // to using !=. but could be specialised for, e.g. std::vector<T> etc
+                // - then just call that directly
+                if (m_comparator.size() != v.size())
+                    return false;
+                for (size_t i = 0; i < v.size(); ++i)
+                    if (m_comparator[i] != v[i])
+                        return false;
+                return true;
+            }
+            virtual std::string describe() const CATCH_OVERRIDE {
+                return "Equals: " + Catch::toString( m_comparator );
+            }
+            std::vector<T> const& m_comparator;
+        };
+
+    } // namespace Vector
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+
+    template<typename T>
+    Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
+        return Vector::ContainsMatcher<T>( comparator );
+    }
+
+    template<typename T>
+    Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
+        return Vector::ContainsElementMatcher<T>( comparator );
+    }
+
+    template<typename T>
+    Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
+        return Vector::EqualsMatcher<T>( comparator );
+    }
+
+} // namespace Matchers
+} // namespace Catch
+
 // #included from: internal/catch_interfaces_tag_alias_registry.h
 #define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
 
@@ -3342,6 +3402,29 @@ return @ desc; \
 #endif
 
 #ifdef CATCH_IMPL
+
+// !TBD: Move the leak detector code into a separate header
+#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
+#include <crtdbg.h>
+class LeakDetector {
+public:
+	LeakDetector() {
+		int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+		flag |= _CRTDBG_LEAK_CHECK_DF;
+		flag |= _CRTDBG_ALLOC_MEM_DF;
+		_CrtSetDbgFlag(flag);
+		_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+		_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+		// Change this to leaking allocation's number to break there
+		_CrtSetBreakAlloc(-1);
+	}
+};
+#else
+class LeakDetector {};
+#endif
+
+LeakDetector leakDetector;
+
 // #included from: internal/catch_impl.hpp
 #define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED
 
@@ -3601,7 +3684,7 @@ namespace Catch {
         void addPattern() {
             std::string token = subString();
             for( size_t i = 0; i < m_escapeChars.size(); ++i )
-                token = token.substr( 0, m_escapeChars[i]-i ) + token.substr( m_escapeChars[i]+1-i );
+                token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 );
             m_escapeChars.clear();
             if( startsWith( token, "exclude:" ) ) {
                 m_exclusion = true;
@@ -6259,6 +6342,16 @@ namespace Catch {
 
 #else // Not Windows - assumed to be POSIX compatible //////////////////////////
 
+#  if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+
+namespace Catch {
+    struct FatalConditionHandler {
+        void reset() {}
+    };
+}
+
+#  else // CATCH_CONFIG_POSIX_SIGNALS is defined
+
 #include <signal.h>
 
 namespace Catch {
@@ -6337,6 +6430,8 @@ namespace Catch {
 
 } // namespace Catch
 
+#  endif // CATCH_CONFIG_POSIX_SIGNALS
+
 #endif // not Windows
 
 #include <set>
@@ -8043,7 +8138,7 @@ namespace Catch {
         return os;
     }
 
-    Version libraryVersion( 1, 7, 2, "", 0 );
+    Version libraryVersion( 1, 8, 1, "", 0 );
 
 }
 
@@ -8214,8 +8309,11 @@ namespace Catch
 #endif
 
 #ifdef CATCH_PLATFORM_WINDOWS
+
 #else
+
 #include <sys/time.h>
+
 #endif
 
 namespace Catch {
@@ -8768,12 +8866,12 @@ namespace Catch {
 
     void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) {
         if( expectedMessage.empty() )
-            captureExpectedException( Matchers::Impl::Generic::AllOf<std::string>() );
+            captureExpectedException( Matchers::Impl::MatchAllOf<std::string>() );
         else
             captureExpectedException( Matchers::Equals( expectedMessage ) );
     }
 
-    void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher ) {
+    void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher ) {
 
         assert( !isFalseTest( m_assertionInfo.resultDisposition ) );
         AssertionResultData data = m_data;
@@ -8807,6 +8905,15 @@ namespace Catch {
     }
 
     void ResultBuilder::react() {
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+        if (m_shouldDebugBreak) {
+            ///////////////////////////////////////////////////////////////////
+            // To inspect the state during test, you need to go one level up the callstack
+            // To go back to the test and change execution, jump over the throw statement
+            ///////////////////////////////////////////////////////////////////
+            CATCH_BREAK_INTO_DEBUGGER();
+        }
+#endif
         if( m_shouldThrow )
             throw Catch::TestFailureException();
     }
@@ -8935,6 +9042,86 @@ namespace Catch {
 
 } // end namespace Catch
 
+// #included from: catch_matchers_string.hpp
+
+namespace Catch {
+namespace Matchers {
+
+    namespace StdString {
+
+        CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
+        :   m_caseSensitivity( caseSensitivity ),
+            m_str( adjustString( str ) )
+        {}
+        std::string CasedString::adjustString( std::string const& str ) const {
+            return m_caseSensitivity == CaseSensitive::No
+                   ? toLower( str )
+                   : str;
+        }
+        std::string CasedString::caseSensitivitySuffix() const {
+            return m_caseSensitivity == CaseSensitive::No
+                   ? " (case insensitive)"
+                   : std::string();
+        }
+
+        StringMatcherBase::StringMatcherBase( std::string operation, CasedString const& comparator )
+        : m_comparator( comparator ),
+          m_operation( operation ) {
+        }
+
+        std::string StringMatcherBase::describe() const {
+            std::string description;
+            description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
+                                        m_comparator.caseSensitivitySuffix().size());
+            description += m_operation;
+            description += ": \"";
+            description += m_comparator.m_str;
+            description += "\"";
+            description += m_comparator.caseSensitivitySuffix();
+            return description;
+        }
+
+        EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
+
+        bool EqualsMatcher::match( std::string const& source ) const {
+            return m_comparator.adjustString( source ) == m_comparator.m_str;
+        }
+
+        ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
+
+        bool ContainsMatcher::match( std::string const& source ) const {
+            return contains( m_comparator.adjustString( source ), m_comparator.m_str );
+        }
+
+        StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
+
+        bool StartsWithMatcher::match( std::string const& source ) const {
+            return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+        }
+
+        EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
+
+        bool EndsWithMatcher::match( std::string const& source ) const {
+            return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+        }
+
+    } // namespace StdString
+
+    StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+    StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+    StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+    StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+
+} // namespace Matchers
+} // namespace Catch
 // #included from: ../reporters/catch_reporter_multi.hpp
 #define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED
 
@@ -9078,6 +9265,7 @@ Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingRepo
 #define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
 
 #include <cstring>
+#include <assert.h>
 
 namespace Catch {
 
@@ -9677,6 +9865,12 @@ namespace Catch {
             return std::string();
         }
 
+        void writeSourceInfo( SourceLineInfo const& sourceInfo ) {
+            m_xml
+                .writeAttribute( "filename", sourceInfo.file )
+                .writeAttribute( "line", sourceInfo.line );
+        }
+
     public: // StreamingReporterBase
 
         virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE {
@@ -9706,6 +9900,8 @@ namespace Catch {
                 .writeAttribute( "description", testInfo.description )
                 .writeAttribute( "tags", testInfo.tagsAsString );
 
+            writeSourceInfo( testInfo.lineInfo );
+
             if ( m_config->showDurations() == ShowDurations::Always )
                 m_testCaseTimer.start();
             m_xml.ensureTagClosed();
@@ -9717,6 +9913,7 @@ namespace Catch {
                 m_xml.startElement( "Section" )
                     .writeAttribute( "name", trim( sectionInfo.name ) )
                     .writeAttribute( "description", sectionInfo.description );
+                writeSourceInfo( sectionInfo.lineInfo );
                 m_xml.ensureTagClosed();
             }
         }
@@ -9749,9 +9946,9 @@ namespace Catch {
             if( assertionResult.hasExpression() ) {
                 m_xml.startElement( "Expression" )
                     .writeAttribute( "success", assertionResult.succeeded() )
-                    .writeAttribute( "type", assertionResult.getTestMacroName() )
-                    .writeAttribute( "filename", assertionResult.getSourceInfo().file )
-                    .writeAttribute( "line", assertionResult.getSourceInfo().line );
+                    .writeAttribute( "type", assertionResult.getTestMacroName() );
+
+                writeSourceInfo( assertionResult.getSourceInfo() );
 
                 m_xml.scopedElement( "Original" )
                     .writeText( assertionResult.getExpression() );
@@ -9762,16 +9959,16 @@ namespace Catch {
             // And... Print a result applicable to each result type.
             switch( assertionResult.getResultType() ) {
                 case ResultWas::ThrewException:
-                    m_xml.scopedElement( "Exception" )
-                        .writeAttribute( "filename", assertionResult.getSourceInfo().file )
-                        .writeAttribute( "line", assertionResult.getSourceInfo().line )
-                        .writeText( assertionResult.getMessage() );
+                    m_xml.startElement( "Exception" );
+                    writeSourceInfo( assertionResult.getSourceInfo() );
+                    m_xml.writeText( assertionResult.getMessage() );
+                    m_xml.endElement();
                     break;
                 case ResultWas::FatalErrorCondition:
-                    m_xml.scopedElement( "FatalErrorCondition" )
-                        .writeAttribute( "filename", assertionResult.getSourceInfo().file )
-                        .writeAttribute( "line", assertionResult.getSourceInfo().line )
-                        .writeText( assertionResult.getMessage() );
+                    m_xml.startElement( "FatalErrorCondition" );
+                    writeSourceInfo( assertionResult.getSourceInfo() );
+                    m_xml.writeText( assertionResult.getMessage() );
+                    m_xml.endElement();
                     break;
                 case ResultWas::Info:
                     m_xml.scopedElement( "Info" )
@@ -9781,8 +9978,10 @@ namespace Catch {
                     // Warning will already have been written
                     break;
                 case ResultWas::ExplicitFailure:
-                    m_xml.scopedElement( "Failure" )
-                        .writeText( assertionResult.getMessage() );
+                    m_xml.startElement( "Failure" );
+                    writeSourceInfo( assertionResult.getSourceInfo() );
+                    m_xml.writeText( assertionResult.getMessage() );
+                    m_xml.endElement();
                     break;
                 default:
                     break;
@@ -10095,8 +10294,30 @@ namespace Catch {
 // #included from: ../reporters/catch_reporter_console.hpp
 #define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
 
+#include <cfloat>
+#include <cstdio>
+
 namespace Catch {
 
+    namespace {
+        // Because formatting using c++ streams is stateful, drop down to C is required
+        // Alternatively we could use stringstream, but its performance is... not good.
+        std::string getFormattedDuration( double duration ) {
+            // Max exponent + 1 is required to represent the whole part
+            // + 1 for decimal point
+            // + 3 for the 3 decimal places
+            // + 1 for null terminator
+            const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
+            char buffer[maxDoubleSize];
+#ifdef _MSC_VER
+            sprintf_s(buffer, "%.3f", duration);
+#else
+            sprintf(buffer, "%.3f", duration);
+#endif
+            return std::string(buffer);
+        }
+    }
+
     struct ConsoleReporter : StreamingReporterBase {
         ConsoleReporter( ReporterConfig const& _config )
         :   StreamingReporterBase( _config ),
@@ -10149,14 +10370,11 @@ namespace Catch {
                     stream << "\nNo assertions in test case";
                 stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
             }
-            if( m_headerPrinted ) {
-                if( m_config->showDurations() == ShowDurations::Always )
-                    stream << "Completed in " << _sectionStats.durationInSeconds << 's' << std::endl;
-                m_headerPrinted = false;
+            if( m_config->showDurations() == ShowDurations::Always ) {
+                stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
             }
-            else {
-                if( m_config->showDurations() == ShowDurations::Always )
-                    stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << 's' << std::endl;
+            if( m_headerPrinted ) {
+                m_headerPrinted = false;
             }
             StreamingReporterBase::sectionEnded( _sectionStats );
         }
@@ -10860,11 +11078,6 @@ namespace Catch {
     TestSpec::TagPattern::~TagPattern() {}
     TestSpec::ExcludedPattern::~ExcludedPattern() {}
 
-    Matchers::Impl::StdString::Equals::~Equals() {}
-    Matchers::Impl::StdString::Contains::~Contains() {}
-    Matchers::Impl::StdString::StartsWith::~StartsWith() {}
-    Matchers::Impl::StdString::EndsWith::~EndsWith() {}
-
     void Config::dummy() {}
 
     namespace TestCaseTracking {
@@ -10889,7 +11102,7 @@ namespace Catch {
 
 // Standard C/C++ main entry point
 int main (int argc, char * argv[]) {
-    int result = Catch::Session().run( argc, argv );
+	int result = Catch::Session().run( argc, argv );
     return ( result < 0xff ? result : 0xff );
 }
 

From 06c788e4fd9beec4ac7788d65918bfdb5c5e5f65 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Fri, 3 Mar 2017 10:01:16 +0100
Subject: [PATCH 21/23] :memo: added missing word

---
 .github/CONTRIBUTING.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 4a621c07..b11f19fd 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -64,7 +64,7 @@ Please understand that I cannot accept pull requests changing only file `src/jso
 - Specifically, I am aware of compilation problems with **Microsoft Visual Studio** (there even is an [issue label](https://github.com/nlohmann/json/issues?utf8=✓&q=label%3A%22visual+studio%22+) for these kind of bugs). I understand that even in 2016, complete C++11 support isn't there yet. But please also understand that I do not want to drop features or uglify the code just to make Microsoft's sub-standard compiler happy. The past has shown that there are ways to express the functionality such that the code compiles with the most recent MSVC - unfortunately, this is not the main objective of the project.
 - Please refrain from proposing changes that would **break [JSON](http://json.org) conformance**. If you propose a conformant extension of JSON to be supported by the library, please motivate this extension.
   - We shall not extend the library to **support comments**. There is quite some [controversy](https://www.reddit.com/r/programming/comments/4v6chu/why_json_doesnt_support_comments_douglas_crockford/) around this topic, and there were quite some [issues](https://github.com/nlohmann/json/issues/376) on this. We believe that JSON is fine without comments.
-  - We do not preserve the **insertion order of object elements**. The [JSON standard](https://tools.ietf.org/html/rfc7159.html) defines objects as "an unordered collection of zero or more name/value pairs". To this end, this library does not preserve insertion order of name/value pairs. (In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default.) Note this behavior conforms to the standard, and we shall not it to any other order. If you do want to preserve the insertion order, you can specialize the object type with containers like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map).
+  - We do not preserve the **insertion order of object elements**. The [JSON standard](https://tools.ietf.org/html/rfc7159.html) defines objects as "an unordered collection of zero or more name/value pairs". To this end, this library does not preserve insertion order of name/value pairs. (In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default.) Note this behavior conforms to the standard, and we shall not change it to any other order. If you do want to preserve the insertion order, you can specialize the object type with containers like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map).
 
 - Please do not open pull requests that address **multiple issues**.
 

From a1354184ceadb72669cecbb83ece091180fb74ab Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Fri, 3 Mar 2017 12:56:54 +0100
Subject: [PATCH 22/23] :memo: fixed typo #481

---
 README.md | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 1ada712c..94120db6 100644
--- a/README.md
+++ b/README.md
@@ -825,9 +825,10 @@ I deeply appreciate the help of the following people.
 - [EnricoBilla](https://github.com/EnricoBilla) noted a typo in an example.
 - [Martin Hořeňovský](https://github.com/horenmar) found a way for a 2x speedup for the compilation time of the test suite.
 - [ukhegg](https://github.com/ukhegg) found proposed an improvement for the examples section.
-- [rswanson-ihi](https://github.com/rswanson-ihi) noted a type in the README.
+- [rswanson-ihi](https://github.com/rswanson-ihi) noted a typo in the README.
 - [Mihai Stan](https://github.com/stanmihai4) fixed a bug in the comparison with `nullptr`s.
 - [Tushar Maheshwari](https://github.com/tusharpm) added [cotire](https://github.com/sakra/cotire) support to speed up the compilation.
+- [TedLyngmo](https://github.com/TedLyngmo) noted a typo in the README.
 
 Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.
 
@@ -887,7 +888,7 @@ To compile and run the tests, you need to execute
 
 ```sh
 $ make json_unit -Ctest
-$ ./test/json_unit "*""
+$ ./test/json_unit "*"
 
 ===============================================================================
 All tests passed (11202597 assertions in 47 test cases)

From 758c4addc1c6a010a3e9f3730419f9dfae9cd769 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Thu, 9 Mar 2017 18:20:26 +0100
Subject: [PATCH 23/23] :ambulance: fix for #492

The original test case relied on an invalidated iterator. This error
did not occur before, but only with GCC with -D_GLIBCXX_DEBUG. This
commit fixes the test case. The library is unaffected by this change.
---
 test/src/unit-modifiers.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp
index 0edc9a12..2b5fdd71 100644
--- a/test/src/unit-modifiers.cpp
+++ b/test/src/unit-modifiers.cpp
@@ -583,10 +583,11 @@ TEST_CASE("modifiers")
 
             SECTION("insert nothing (count = 0)")
             {
-                auto pos = j_array.end();
                 auto it = j_array.insert(j_array.end(), 0, 5);
                 CHECK(j_array.size() == 4);
-                CHECK(it == pos);
+                // the returned iterator points to the first inserted element;
+                // there were 4 elements, so it should point to the 5th
+                CHECK(it == j_array.begin() + 4);
                 CHECK(j_array == json({1, 2, 3, 4}));
             }
         }