improved performance for dump()

This commit is contained in:
Niels 2015-06-03 23:34:10 +02:00
parent cb873a42ed
commit 9dbb4402fb
6 changed files with 185 additions and 156 deletions

View file

@ -33,6 +33,6 @@ pretty:
src/json.hpp src/json.hpp.re2c test/unit.cpp benchmarks/benchmarks.cpp src/json.hpp src/json.hpp.re2c test/unit.cpp benchmarks/benchmarks.cpp
# benchmarks # benchmarks
json_benchmarks: benchmarks/benchmarks.cpp benchmarks/benchpress.hpp benchmarks/cxxopts.hpp json_benchmarks: benchmarks/benchmarks.cpp benchmarks/benchpress.hpp benchmarks/cxxopts.hpp src/json.hpp
$(CXX) -std=c++11 $(CXXFLAGS) -O3 -flto -I src -I benchmarks $< $(LDFLAGS) -o $@ $(CXX) -std=c++11 $(CXXFLAGS) -O3 -flto -I src -I benchmarks $< $(LDFLAGS) -o $@
./json_benchmarks ./json_benchmarks

View file

@ -1,5 +1,5 @@
version: '{build}' version: '{build}'
os: Visual Studio 2015 RC os: MinGW
init: [] init: []
install: [] install: []
build_script: build_script:

View file

@ -49,12 +49,15 @@ BENCHMARK("dump jeopardy.json", [](benchpress::context* ctx)
std::ifstream input_file("benchmarks/files/jeopardy/jeopardy.json"); std::ifstream input_file("benchmarks/files/jeopardy/jeopardy.json");
nlohmann::json j; nlohmann::json j;
j << input_file; j << input_file;
std::ofstream output_file("jeopardy.dump.json");
ctx->reset_timer(); ctx->reset_timer();
for (size_t i = 0; i < ctx->num_iterations(); ++i) for (size_t i = 0; i < ctx->num_iterations(); ++i)
{ {
j.dump(); output_file << j;
} }
std::remove("jeopardy.dump.json");
}) })
BENCHMARK("dump jeopardy.json with indent", [](benchpress::context* ctx) BENCHMARK("dump jeopardy.json with indent", [](benchpress::context* ctx)
@ -62,10 +65,13 @@ BENCHMARK("dump jeopardy.json with indent", [](benchpress::context* ctx)
std::ifstream input_file("benchmarks/files/jeopardy/jeopardy.json"); std::ifstream input_file("benchmarks/files/jeopardy/jeopardy.json");
nlohmann::json j; nlohmann::json j;
j << input_file; j << input_file;
std::ofstream output_file("jeopardy.dump.json");
ctx->reset_timer(); ctx->reset_timer();
for (size_t i = 0; i < ctx->num_iterations(); ++i) for (size_t i = 0; i < ctx->num_iterations(); ++i)
{ {
j.dump(4); output_file << std::setw(4) << j;
} }
std::remove("jeopardy.dump.json");
}) })

View file

@ -779,14 +779,18 @@ class basic_json
*/ */
inline string_t dump(const int indent = -1) const noexcept inline string_t dump(const int indent = -1) const noexcept
{ {
std::stringstream ss;
if (indent >= 0) if (indent >= 0)
{ {
return dump(true, static_cast<unsigned int>(indent)); dump(ss, true, static_cast<unsigned int>(indent));
} }
else else
{ {
return dump(false, 0); dump(ss, false, 0);
} }
return ss.str();
} }
/// return the type of the object (explicit) /// return the type of the object (explicit)
@ -1964,19 +1968,21 @@ class basic_json
friend std::ostream& operator<<(std::ostream& o, const basic_json& j) friend std::ostream& operator<<(std::ostream& o, const basic_json& j)
{ {
// read width member and use it as indentation parameter if nonzero // read width member and use it as indentation parameter if nonzero
const int indentation = (o.width() == 0) ? -1 : o.width(); const bool prettyPrint = (o.width() > 0);
const auto indentation = (prettyPrint ? o.width() : 0);
o << j.dump(indentation); // reset width to 0 for subsequent calls to this stream
o.width(0);
// do the actual serialization
j.dump(o, prettyPrint, indentation);
return o; return o;
} }
/// serialize to stream /// serialize to stream
friend std::ostream& operator>>(const basic_json& j, std::ostream& o) friend std::ostream& operator>>(const basic_json& j, std::ostream& o)
{ {
// read width member and use it as indentation parameter if nonzero o << j;
const int indentation = (o.width() == 0) ? -1 : o.width();
o << j.dump(indentation);
return o; return o;
} }
@ -2067,15 +2073,11 @@ class basic_json
characters by a sequence of "\u" followed by a four-digit hex characters by a sequence of "\u" followed by a four-digit hex
representation. representation.
@param o the stream to write the escaped string to
@param s the string to escape @param s the string to escape
@return escaped string
*/ */
static string_t escape_string(const string_t& s) noexcept static void escape_string(std::ostream& o, const string_t& s) noexcept
{ {
// create a result string of at least the size than s
string_t result;
result.reserve(s.size());
for (const auto c : s) for (const auto c : s)
{ {
switch (c) switch (c)
@ -2083,49 +2085,49 @@ class basic_json
// quotation mark (0x22) // quotation mark (0x22)
case '"': case '"':
{ {
result += "\\\""; o << "\\\"";
break; break;
} }
// reverse solidus (0x5c) // reverse solidus (0x5c)
case '\\': case '\\':
{ {
result += "\\\\"; o << "\\\\";
break; break;
} }
// backspace (0x08) // backspace (0x08)
case '\b': case '\b':
{ {
result += "\\b"; o << "\\b";
break; break;
} }
// formfeed (0x0c) // formfeed (0x0c)
case '\f': case '\f':
{ {
result += "\\f"; o << "\\f";
break; break;
} }
// newline (0x0a) // newline (0x0a)
case '\n': case '\n':
{ {
result += "\\n"; o << "\\n";
break; break;
} }
// carriage return (0x0d) // carriage return (0x0d)
case '\r': case '\r':
{ {
result += "\\r"; o << "\\r";
break; break;
} }
// horizontal tab (0x09) // horizontal tab (0x09)
case '\t': case '\t':
{ {
result += "\\t"; o << "\\t";
break; break;
} }
@ -2135,24 +2137,19 @@ class basic_json
{ {
// control characters (everything between 0x00 and 0x1f) // control characters (everything between 0x00 and 0x1f)
// -> create four-digit hex representation // -> create four-digit hex representation
std::basic_stringstream<typename string_t::value_type> ss; o << "\\u" << std::hex << std::setw(4) << std::setfill('0') << int(c);
ss << "\\u" << std::hex << std::setw(4) << std::setfill('0') << int(c);
result += ss.str();
} }
else else
{ {
// all other characters are added as-is // all other characters are added as-is
result.append(1, c); o << c;
} }
break; break;
} }
} }
} }
return result;
} }
/*! /*!
@brief internal implementation of the serialization function @brief internal implementation of the serialization function
@ -2166,108 +2163,115 @@ class basic_json
std::to_string() std::to_string()
- floating-point numbers are converted to a string using "%g" format - floating-point numbers are converted to a string using "%g" format
@param o stream to write to
@param prettyPrint whether the output shall be pretty-printed @param prettyPrint whether the output shall be pretty-printed
@param indentStep the indent level @param indentStep the indent level
@param currentIndent the current indent level (only used internally) @param currentIndent the current indent level (only used internally)
*/ */
inline string_t dump(const bool prettyPrint, const unsigned int indentStep, inline void dump(std::ostream& o, const bool prettyPrint, const unsigned int indentStep,
const unsigned int currentIndent = 0) const noexcept const unsigned int currentIndent = 0) const noexcept
{ {
// variable to hold indentation for recursive calls // variable to hold indentation for recursive calls
auto new_indent = currentIndent; auto new_indent = currentIndent;
// helper function to return whitespace as indentation
const auto indent = [prettyPrint, &new_indent]()
{
return prettyPrint ? string_t(new_indent, ' ') : string_t();
};
switch (m_type) switch (m_type)
{ {
case (value_t::object): case (value_t::object):
{ {
if (m_value.object->empty()) if (m_value.object->empty())
{ {
return "{}"; o << "{}";
return;
} }
string_t result = "{"; o << "{";
// increase indentation // increase indentation
if (prettyPrint) if (prettyPrint)
{ {
new_indent += indentStep; new_indent += indentStep;
result += "\n"; o << "\n";
} }
for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i) for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i)
{ {
if (i != m_value.object->cbegin()) if (i != m_value.object->cbegin())
{ {
result += prettyPrint ? ",\n" : ","; o << (prettyPrint ? ",\n" : ",");
} }
result += indent() + "\"" + escape_string(i->first) + "\":" + (prettyPrint ? " " : "") o << string_t(new_indent, ' ') << "\"";
+ i->second.dump(prettyPrint, indentStep, new_indent); escape_string(o, i->first);
o << "\":" << (prettyPrint ? " " : "");
i->second.dump(o, prettyPrint, indentStep, new_indent);
} }
// decrease indentation // decrease indentation
if (prettyPrint) if (prettyPrint)
{ {
new_indent -= indentStep; new_indent -= indentStep;
result += "\n"; o << "\n";
} }
return result + indent() + "}"; o << string_t(new_indent, ' ') + "}";
return;
} }
case (value_t::array): case (value_t::array):
{ {
if (m_value.array->empty()) if (m_value.array->empty())
{ {
return "[]"; o << "[]";
return;
} }
string_t result = "["; o << "[";
// increase indentation // increase indentation
if (prettyPrint) if (prettyPrint)
{ {
new_indent += indentStep; new_indent += indentStep;
result += "\n"; o << "\n";
} }
for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i) for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i)
{ {
if (i != m_value.array->cbegin()) if (i != m_value.array->cbegin())
{ {
result += prettyPrint ? ",\n" : ","; o << (prettyPrint ? ",\n" : ",");
} }
result += indent() + i->dump(prettyPrint, indentStep, new_indent); o << string_t(new_indent, ' ');
i->dump(o, prettyPrint, indentStep, new_indent);
} }
// decrease indentation // decrease indentation
if (prettyPrint) if (prettyPrint)
{ {
new_indent -= indentStep; new_indent -= indentStep;
result += "\n"; o << "\n";
} }
return result + indent() + "]"; o << string_t(new_indent, ' ') << "]";
return;
} }
case (value_t::string): case (value_t::string):
{ {
return string_t("\"") + escape_string(*m_value.string) + "\""; o << string_t("\"");
escape_string(o, *m_value.string);
o << "\"";
return;
} }
case (value_t::boolean): case (value_t::boolean):
{ {
return m_value.boolean ? "true" : "false"; o << (m_value.boolean ? "true" : "false");
return;
} }
case (value_t::number_integer): case (value_t::number_integer):
{ {
return std::to_string(m_value.number_integer); o << m_value.number_integer;
return;
} }
case (value_t::number_float): case (value_t::number_float):
@ -2277,16 +2281,20 @@ class basic_json
const auto sz = static_cast<unsigned int>(std::snprintf(nullptr, 0, "%.15g", m_value.number_float)); const auto sz = static_cast<unsigned int>(std::snprintf(nullptr, 0, "%.15g", m_value.number_float));
std::vector<typename string_t::value_type> buf(sz + 1); std::vector<typename string_t::value_type> buf(sz + 1);
std::snprintf(&buf[0], buf.size(), "%.15g", m_value.number_float); std::snprintf(&buf[0], buf.size(), "%.15g", m_value.number_float);
return string_t(buf.data()); o << buf.data();
return;
} }
case (value_t::discarded): case (value_t::discarded):
{ {
return "<discarded>"; o << "<discarded>";
return;
} }
default: default:
{ {
return "null"; o << "null";
return;
} }
} }
} }

View file

@ -779,14 +779,18 @@ class basic_json
*/ */
inline string_t dump(const int indent = -1) const noexcept inline string_t dump(const int indent = -1) const noexcept
{ {
std::stringstream ss;
if (indent >= 0) if (indent >= 0)
{ {
return dump(true, static_cast<unsigned int>(indent)); dump(ss, true, static_cast<unsigned int>(indent));
} }
else else
{ {
return dump(false, 0); dump(ss, false, 0);
} }
return ss.str();
} }
/// return the type of the object (explicit) /// return the type of the object (explicit)
@ -1964,19 +1968,21 @@ class basic_json
friend std::ostream& operator<<(std::ostream& o, const basic_json& j) friend std::ostream& operator<<(std::ostream& o, const basic_json& j)
{ {
// read width member and use it as indentation parameter if nonzero // read width member and use it as indentation parameter if nonzero
const int indentation = (o.width() == 0) ? -1 : o.width(); const bool prettyPrint = (o.width() > 0);
const auto indentation = (prettyPrint ? o.width() : 0);
o << j.dump(indentation); // reset width to 0 for subsequent calls to this stream
o.width(0);
// do the actual serialization
j.dump(o, prettyPrint, indentation);
return o; return o;
} }
/// serialize to stream /// serialize to stream
friend std::ostream& operator>>(const basic_json& j, std::ostream& o) friend std::ostream& operator>>(const basic_json& j, std::ostream& o)
{ {
// read width member and use it as indentation parameter if nonzero o << j;
const int indentation = (o.width() == 0) ? -1 : o.width();
o << j.dump(indentation);
return o; return o;
} }
@ -2067,15 +2073,11 @@ class basic_json
characters by a sequence of "\u" followed by a four-digit hex characters by a sequence of "\u" followed by a four-digit hex
representation. representation.
@param o the stream to write the escaped string to
@param s the string to escape @param s the string to escape
@return escaped string
*/ */
static string_t escape_string(const string_t& s) noexcept static void escape_string(std::ostream& o, const string_t& s) noexcept
{ {
// create a result string of at least the size than s
string_t result;
result.reserve(s.size());
for (const auto c : s) for (const auto c : s)
{ {
switch (c) switch (c)
@ -2083,49 +2085,49 @@ class basic_json
// quotation mark (0x22) // quotation mark (0x22)
case '"': case '"':
{ {
result += "\\\""; o << "\\\"";
break; break;
} }
// reverse solidus (0x5c) // reverse solidus (0x5c)
case '\\': case '\\':
{ {
result += "\\\\"; o << "\\\\";
break; break;
} }
// backspace (0x08) // backspace (0x08)
case '\b': case '\b':
{ {
result += "\\b"; o << "\\b";
break; break;
} }
// formfeed (0x0c) // formfeed (0x0c)
case '\f': case '\f':
{ {
result += "\\f"; o << "\\f";
break; break;
} }
// newline (0x0a) // newline (0x0a)
case '\n': case '\n':
{ {
result += "\\n"; o << "\\n";
break; break;
} }
// carriage return (0x0d) // carriage return (0x0d)
case '\r': case '\r':
{ {
result += "\\r"; o << "\\r";
break; break;
} }
// horizontal tab (0x09) // horizontal tab (0x09)
case '\t': case '\t':
{ {
result += "\\t"; o << "\\t";
break; break;
} }
@ -2135,24 +2137,19 @@ class basic_json
{ {
// control characters (everything between 0x00 and 0x1f) // control characters (everything between 0x00 and 0x1f)
// -> create four-digit hex representation // -> create four-digit hex representation
std::basic_stringstream<typename string_t::value_type> ss; o << "\\u" << std::hex << std::setw(4) << std::setfill('0') << int(c);
ss << "\\u" << std::hex << std::setw(4) << std::setfill('0') << int(c);
result += ss.str();
} }
else else
{ {
// all other characters are added as-is // all other characters are added as-is
result.append(1, c); o << c;
} }
break; break;
} }
} }
} }
return result;
} }
/*! /*!
@brief internal implementation of the serialization function @brief internal implementation of the serialization function
@ -2166,108 +2163,115 @@ class basic_json
std::to_string() std::to_string()
- floating-point numbers are converted to a string using "%g" format - floating-point numbers are converted to a string using "%g" format
@param o stream to write to
@param prettyPrint whether the output shall be pretty-printed @param prettyPrint whether the output shall be pretty-printed
@param indentStep the indent level @param indentStep the indent level
@param currentIndent the current indent level (only used internally) @param currentIndent the current indent level (only used internally)
*/ */
inline string_t dump(const bool prettyPrint, const unsigned int indentStep, inline void dump(std::ostream& o, const bool prettyPrint, const unsigned int indentStep,
const unsigned int currentIndent = 0) const noexcept const unsigned int currentIndent = 0) const noexcept
{ {
// variable to hold indentation for recursive calls // variable to hold indentation for recursive calls
auto new_indent = currentIndent; auto new_indent = currentIndent;
// helper function to return whitespace as indentation
const auto indent = [prettyPrint, &new_indent]()
{
return prettyPrint ? string_t(new_indent, ' ') : string_t();
};
switch (m_type) switch (m_type)
{ {
case (value_t::object): case (value_t::object):
{ {
if (m_value.object->empty()) if (m_value.object->empty())
{ {
return "{}"; o << "{}";
return;
} }
string_t result = "{"; o << "{";
// increase indentation // increase indentation
if (prettyPrint) if (prettyPrint)
{ {
new_indent += indentStep; new_indent += indentStep;
result += "\n"; o << "\n";
} }
for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i) for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i)
{ {
if (i != m_value.object->cbegin()) if (i != m_value.object->cbegin())
{ {
result += prettyPrint ? ",\n" : ","; o << (prettyPrint ? ",\n" : ",");
} }
result += indent() + "\"" + escape_string(i->first) + "\":" + (prettyPrint ? " " : "") o << string_t(new_indent, ' ') << "\"";
+ i->second.dump(prettyPrint, indentStep, new_indent); escape_string(o, i->first);
o << "\":" << (prettyPrint ? " " : "");
i->second.dump(o, prettyPrint, indentStep, new_indent);
} }
// decrease indentation // decrease indentation
if (prettyPrint) if (prettyPrint)
{ {
new_indent -= indentStep; new_indent -= indentStep;
result += "\n"; o << "\n";
} }
return result + indent() + "}"; o << string_t(new_indent, ' ') + "}";
return;
} }
case (value_t::array): case (value_t::array):
{ {
if (m_value.array->empty()) if (m_value.array->empty())
{ {
return "[]"; o << "[]";
return;
} }
string_t result = "["; o << "[";
// increase indentation // increase indentation
if (prettyPrint) if (prettyPrint)
{ {
new_indent += indentStep; new_indent += indentStep;
result += "\n"; o << "\n";
} }
for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i) for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i)
{ {
if (i != m_value.array->cbegin()) if (i != m_value.array->cbegin())
{ {
result += prettyPrint ? ",\n" : ","; o << (prettyPrint ? ",\n" : ",");
} }
result += indent() + i->dump(prettyPrint, indentStep, new_indent); o << string_t(new_indent, ' ');
i->dump(o, prettyPrint, indentStep, new_indent);
} }
// decrease indentation // decrease indentation
if (prettyPrint) if (prettyPrint)
{ {
new_indent -= indentStep; new_indent -= indentStep;
result += "\n"; o << "\n";
} }
return result + indent() + "]"; o << string_t(new_indent, ' ') << "]";
return;
} }
case (value_t::string): case (value_t::string):
{ {
return string_t("\"") + escape_string(*m_value.string) + "\""; o << string_t("\"");
escape_string(o, *m_value.string);
o << "\"";
return;
} }
case (value_t::boolean): case (value_t::boolean):
{ {
return m_value.boolean ? "true" : "false"; o << (m_value.boolean ? "true" : "false");
return;
} }
case (value_t::number_integer): case (value_t::number_integer):
{ {
return std::to_string(m_value.number_integer); o << m_value.number_integer;
return;
} }
case (value_t::number_float): case (value_t::number_float):
@ -2277,16 +2281,20 @@ class basic_json
const auto sz = static_cast<unsigned int>(std::snprintf(nullptr, 0, "%.15g", m_value.number_float)); const auto sz = static_cast<unsigned int>(std::snprintf(nullptr, 0, "%.15g", m_value.number_float));
std::vector<typename string_t::value_type> buf(sz + 1); std::vector<typename string_t::value_type> buf(sz + 1);
std::snprintf(&buf[0], buf.size(), "%.15g", m_value.number_float); std::snprintf(&buf[0], buf.size(), "%.15g", m_value.number_float);
return string_t(buf.data()); o << buf.data();
return;
} }
case (value_t::discarded): case (value_t::discarded):
{ {
return "<discarded>"; o << "<discarded>";
return;
} }
default: default:
{ {
return "null"; o << "null";
return;
} }
} }
} }

View file

@ -7162,45 +7162,52 @@ TEST_CASE("convenience functions")
SECTION("string escape") SECTION("string escape")
{ {
CHECK(json::escape_string("\"") == "\\\""); auto escape_string = [](const std::string & s)
CHECK(json::escape_string("\\") == "\\\\"); {
CHECK(json::escape_string("\b") == "\\b"); std::stringstream ss;
CHECK(json::escape_string("\f") == "\\f"); json::escape_string(ss, s);
CHECK(json::escape_string("\n") == "\\n"); return ss.str();
CHECK(json::escape_string("\r") == "\\r"); };
CHECK(json::escape_string("\t") == "\\t");
CHECK(json::escape_string("\x01") == "\\u0001"); CHECK(escape_string("\"") == "\\\"");
CHECK(json::escape_string("\x02") == "\\u0002"); CHECK(escape_string("\\") == "\\\\");
CHECK(json::escape_string("\x03") == "\\u0003"); CHECK(escape_string("\b") == "\\b");
CHECK(json::escape_string("\x04") == "\\u0004"); CHECK(escape_string("\f") == "\\f");
CHECK(json::escape_string("\x05") == "\\u0005"); CHECK(escape_string("\n") == "\\n");
CHECK(json::escape_string("\x06") == "\\u0006"); CHECK(escape_string("\r") == "\\r");
CHECK(json::escape_string("\x07") == "\\u0007"); CHECK(escape_string("\t") == "\\t");
CHECK(json::escape_string("\x08") == "\\b");
CHECK(json::escape_string("\x09") == "\\t"); CHECK(escape_string("\x01") == "\\u0001");
CHECK(json::escape_string("\x0a") == "\\n"); CHECK(escape_string("\x02") == "\\u0002");
CHECK(json::escape_string("\x0b") == "\\u000b"); CHECK(escape_string("\x03") == "\\u0003");
CHECK(json::escape_string("\x0c") == "\\f"); CHECK(escape_string("\x04") == "\\u0004");
CHECK(json::escape_string("\x0d") == "\\r"); CHECK(escape_string("\x05") == "\\u0005");
CHECK(json::escape_string("\x0e") == "\\u000e"); CHECK(escape_string("\x06") == "\\u0006");
CHECK(json::escape_string("\x0f") == "\\u000f"); CHECK(escape_string("\x07") == "\\u0007");
CHECK(json::escape_string("\x10") == "\\u0010"); CHECK(escape_string("\x08") == "\\b");
CHECK(json::escape_string("\x11") == "\\u0011"); CHECK(escape_string("\x09") == "\\t");
CHECK(json::escape_string("\x12") == "\\u0012"); CHECK(escape_string("\x0a") == "\\n");
CHECK(json::escape_string("\x13") == "\\u0013"); CHECK(escape_string("\x0b") == "\\u000b");
CHECK(json::escape_string("\x14") == "\\u0014"); CHECK(escape_string("\x0c") == "\\f");
CHECK(json::escape_string("\x15") == "\\u0015"); CHECK(escape_string("\x0d") == "\\r");
CHECK(json::escape_string("\x16") == "\\u0016"); CHECK(escape_string("\x0e") == "\\u000e");
CHECK(json::escape_string("\x17") == "\\u0017"); CHECK(escape_string("\x0f") == "\\u000f");
CHECK(json::escape_string("\x18") == "\\u0018"); CHECK(escape_string("\x10") == "\\u0010");
CHECK(json::escape_string("\x19") == "\\u0019"); CHECK(escape_string("\x11") == "\\u0011");
CHECK(json::escape_string("\x1a") == "\\u001a"); CHECK(escape_string("\x12") == "\\u0012");
CHECK(json::escape_string("\x1b") == "\\u001b"); CHECK(escape_string("\x13") == "\\u0013");
CHECK(json::escape_string("\x1c") == "\\u001c"); CHECK(escape_string("\x14") == "\\u0014");
CHECK(json::escape_string("\x1d") == "\\u001d"); CHECK(escape_string("\x15") == "\\u0015");
CHECK(json::escape_string("\x1e") == "\\u001e"); CHECK(escape_string("\x16") == "\\u0016");
CHECK(json::escape_string("\x1f") == "\\u001f"); CHECK(escape_string("\x17") == "\\u0017");
CHECK(escape_string("\x18") == "\\u0018");
CHECK(escape_string("\x19") == "\\u0019");
CHECK(escape_string("\x1a") == "\\u001a");
CHECK(escape_string("\x1b") == "\\u001b");
CHECK(escape_string("\x1c") == "\\u001c");
CHECK(escape_string("\x1d") == "\\u001d");
CHECK(escape_string("\x1e") == "\\u001e");
CHECK(escape_string("\x1f") == "\\u001f");
} }
} }