mirror of
https://github.com/nlohmann/json.git
synced 2026-10-24 14:58:54 +00:00
Simplify the serializer as suggested in review
- Test JSON_LITTLE_ENDIAN_TARGET (#5876) instead of repeating its condition as JSON_DUMP_SHORT_STRINGS. - Let dump() write a top-level scalar through dump_element(), so that dump_internal() becomes dump_container() and handles objects and arrays only; pass 0 as indentation on the compact paths. - Rename put_quoted() to put_object_key(), its only use. - Share the fast path of put_indent() and put_literal_indent() in put_short_literal_indent(), and use put_literal_indent() for binary values and the closing brackets of dump_iteratively(). - Assert that write_u_escapes() sees sequences of 2 to 4 bytes only. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
3 files changed
+330
-384
No files matched your search
@@ -33,7 +33,6 @@
|
|||||||
#undef JSON_SCAN_SSSE3_TARGET
|
#undef JSON_SCAN_SSSE3_TARGET
|
||||||
#undef JSON_SCAN_VECTOR
|
#undef JSON_SCAN_VECTOR
|
||||||
#undef JSON_SCAN_VECTOR_UTF8
|
#undef JSON_SCAN_VECTOR_UTF8
|
||||||
#undef JSON_DUMP_SHORT_STRINGS
|
|
||||||
|
|
||||||
#ifndef JSON_TEST_KEEP_MACROS
|
#ifndef JSON_TEST_KEEP_MACROS
|
||||||
#undef JSON_CATCH
|
#undef JSON_CATCH
|
||||||
|
|||||||
@@ -34,14 +34,6 @@
|
|||||||
#include <nlohmann/detail/string_utils.hpp>
|
#include <nlohmann/detail/string_utils.hpp>
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
#include <nlohmann/detail/value_t.hpp>
|
||||||
|
|
||||||
// the short-string fast path of the serializer assembles words from little-endian
|
|
||||||
// loads, see serializer::put_short_quoted()
|
|
||||||
#if defined(_MSC_VER) || defined(__x86_64__) || defined(__i386__) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
|
||||||
#define JSON_DUMP_SHORT_STRINGS 1
|
|
||||||
#else
|
|
||||||
#define JSON_DUMP_SHORT_STRINGS 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
@@ -79,7 +71,7 @@ class serializer
|
|||||||
|
|
||||||
None of @a pretty_print_, @a ensure_ascii_ and @a indent_step_ change over
|
None of @a pretty_print_, @a ensure_ascii_ and @a indent_step_ change over
|
||||||
the life of the serializer, so they are captured once here instead of
|
the life of the serializer, so they are captured once here instead of
|
||||||
being threaded through every call to @ref dump, @ref dump_internal and
|
being threaded through every call to @ref dump, @ref dump_container and
|
||||||
@ref dump_iteratively.
|
@ref dump_iteratively.
|
||||||
*/
|
*/
|
||||||
serializer(output_adapter_protocol<char>& s, const char ichar,
|
serializer(output_adapter_protocol<char>& s, const char ichar,
|
||||||
@@ -126,18 +118,18 @@ class serializer
|
|||||||
void dump(const BasicJsonType& val,
|
void dump(const BasicJsonType& val,
|
||||||
const std::size_t current_indent = 0)
|
const std::size_t current_indent = 0)
|
||||||
{
|
{
|
||||||
dump_internal(val, current_indent);
|
dump_element(val, current_indent, 0);
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
/*!
|
/*!
|
||||||
@brief worker for @ref dump
|
@brief serialize the object or array @a val
|
||||||
|
|
||||||
Identical in behavior to the historical @ref dump, but writes into the
|
Writes into the serializer's internal @ref write_buffer instead of issuing
|
||||||
serializer's internal @ref write_buffer instead of issuing a virtual call
|
a virtual call per token; the public @ref dump flushes the buffer once the
|
||||||
per token. The public @ref dump wraps this and flushes the buffer once the
|
top-level value has been serialized. Scalars are written by @ref dump_scalar,
|
||||||
top-level value has been serialized.
|
which @ref dump_element calls without entering this function.
|
||||||
|
|
||||||
Serializing a container descends into its elements, so a value nested deeply
|
Serializing a container descends into its elements, so a value nested deeply
|
||||||
enough used to exhaust the call stack and terminate the process with no
|
enough used to exhaust the call stack and terminate the process with no
|
||||||
@@ -148,145 +140,120 @@ class serializer
|
|||||||
|
|
||||||
@sa https://github.com/nlohmann/json/issues/5387
|
@sa https://github.com/nlohmann/json/issues/5387
|
||||||
*/
|
*/
|
||||||
void dump_internal(const BasicJsonType& val,
|
void dump_container(const BasicJsonType& val,
|
||||||
const std::size_t current_indent = 0,
|
const std::size_t current_indent,
|
||||||
const std::size_t depth = 0)
|
const std::size_t depth)
|
||||||
{
|
{
|
||||||
switch (val.m_data.m_type)
|
JSON_ASSERT(val.m_data.m_type == value_t::object || val.m_data.m_type == value_t::array);
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(depth >= recursion_depth_limit()))
|
||||||
{
|
{
|
||||||
case value_t::object:
|
dump_iteratively(val, current_indent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val.m_data.m_type == value_t::object)
|
||||||
|
{
|
||||||
|
if (val.m_data.m_value.object->empty())
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(depth >= recursion_depth_limit()))
|
put_literal("{}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pretty_print)
|
||||||
|
{
|
||||||
|
// variable to hold indentation for recursive calls
|
||||||
|
const auto new_indent = next_indent(current_indent, indent_step);
|
||||||
|
|
||||||
|
put_literal_indent("{\n", new_indent);
|
||||||
|
|
||||||
|
// first n-1 elements
|
||||||
|
auto i = val.m_data.m_value.object->cbegin();
|
||||||
|
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
|
||||||
{
|
{
|
||||||
dump_iteratively(val, current_indent);
|
put_object_key(i->first, "\": ");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val.m_data.m_value.object->empty())
|
|
||||||
{
|
|
||||||
put_literal("{}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pretty_print)
|
|
||||||
{
|
|
||||||
// variable to hold indentation for recursive calls
|
|
||||||
const auto new_indent = next_indent(current_indent, indent_step);
|
|
||||||
|
|
||||||
put_literal_indent("{\n", new_indent);
|
|
||||||
|
|
||||||
// first n-1 elements
|
|
||||||
auto i = val.m_data.m_value.object->cbegin();
|
|
||||||
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
|
|
||||||
{
|
|
||||||
put_quoted(i->first, "\": ");
|
|
||||||
dump_element(i->second, new_indent, depth + 1);
|
|
||||||
put_literal_indent(",\n", new_indent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// last element
|
|
||||||
JSON_ASSERT(i != val.m_data.m_value.object->cend());
|
|
||||||
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
|
|
||||||
put_quoted(i->first, "\": ");
|
|
||||||
dump_element(i->second, new_indent, depth + 1);
|
dump_element(i->second, new_indent, depth + 1);
|
||||||
|
put_literal_indent(",\n", new_indent);
|
||||||
put_literal_indent("\n", current_indent);
|
|
||||||
put_char('}');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
put_char('{');
|
|
||||||
|
|
||||||
// first n-1 elements
|
|
||||||
auto i = val.m_data.m_value.object->cbegin();
|
|
||||||
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
|
|
||||||
{
|
|
||||||
put_quoted(i->first, "\":");
|
|
||||||
dump_element(i->second, current_indent, depth + 1);
|
|
||||||
put_char(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
// last element
|
|
||||||
JSON_ASSERT(i != val.m_data.m_value.object->cend());
|
|
||||||
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
|
|
||||||
put_quoted(i->first, "\":");
|
|
||||||
dump_element(i->second, current_indent, depth + 1);
|
|
||||||
|
|
||||||
put_char('}');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
// last element
|
||||||
|
JSON_ASSERT(i != val.m_data.m_value.object->cend());
|
||||||
|
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
|
||||||
|
put_object_key(i->first, "\": ");
|
||||||
|
dump_element(i->second, new_indent, depth + 1);
|
||||||
|
|
||||||
|
put_literal_indent("\n", current_indent);
|
||||||
|
put_char('}');
|
||||||
}
|
}
|
||||||
|
else
|
||||||
case value_t::array:
|
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(depth >= recursion_depth_limit()))
|
put_char('{');
|
||||||
|
|
||||||
|
// first n-1 elements
|
||||||
|
auto i = val.m_data.m_value.object->cbegin();
|
||||||
|
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
|
||||||
{
|
{
|
||||||
dump_iteratively(val, current_indent);
|
put_object_key(i->first, "\":");
|
||||||
return;
|
dump_element(i->second, 0, depth + 1);
|
||||||
|
put_char(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (val.m_data.m_value.array->empty())
|
// last element
|
||||||
{
|
JSON_ASSERT(i != val.m_data.m_value.object->cend());
|
||||||
put_literal("[]");
|
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
|
||||||
return;
|
put_object_key(i->first, "\":");
|
||||||
}
|
dump_element(i->second, 0, depth + 1);
|
||||||
|
|
||||||
if (pretty_print)
|
put_char('}');
|
||||||
{
|
}
|
||||||
// variable to hold indentation for recursive calls
|
return;
|
||||||
const auto new_indent = next_indent(current_indent, indent_step);
|
}
|
||||||
|
|
||||||
put_literal_indent("[\n", new_indent);
|
if (val.m_data.m_value.array->empty())
|
||||||
|
{
|
||||||
|
put_literal("[]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// first n-1 elements
|
if (pretty_print)
|
||||||
for (auto i = val.m_data.m_value.array->cbegin();
|
{
|
||||||
i != val.m_data.m_value.array->cend() - 1; ++i)
|
// variable to hold indentation for recursive calls
|
||||||
{
|
const auto new_indent = next_indent(current_indent, indent_step);
|
||||||
dump_element(*i, new_indent, depth + 1);
|
|
||||||
put_literal_indent(",\n", new_indent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// last element
|
put_literal_indent("[\n", new_indent);
|
||||||
JSON_ASSERT(!val.m_data.m_value.array->empty());
|
|
||||||
dump_element(val.m_data.m_value.array->back(), new_indent, depth + 1);
|
|
||||||
|
|
||||||
put_literal_indent("\n", current_indent);
|
// first n-1 elements
|
||||||
put_char(']');
|
for (auto i = val.m_data.m_value.array->cbegin();
|
||||||
}
|
i != val.m_data.m_value.array->cend() - 1; ++i)
|
||||||
else
|
{
|
||||||
{
|
dump_element(*i, new_indent, depth + 1);
|
||||||
put_char('[');
|
put_literal_indent(",\n", new_indent);
|
||||||
|
|
||||||
// first n-1 elements
|
|
||||||
for (auto i = val.m_data.m_value.array->cbegin();
|
|
||||||
i != val.m_data.m_value.array->cend() - 1; ++i)
|
|
||||||
{
|
|
||||||
dump_element(*i, current_indent, depth + 1);
|
|
||||||
put_char(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
// last element
|
|
||||||
JSON_ASSERT(!val.m_data.m_value.array->empty());
|
|
||||||
dump_element(val.m_data.m_value.array->back(), current_indent, depth + 1);
|
|
||||||
|
|
||||||
put_char(']');
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case value_t::string:
|
// last element
|
||||||
case value_t::binary:
|
JSON_ASSERT(!val.m_data.m_value.array->empty());
|
||||||
case value_t::boolean:
|
dump_element(val.m_data.m_value.array->back(), new_indent, depth + 1);
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
put_literal_indent("\n", current_indent);
|
||||||
case value_t::number_float:
|
put_char(']');
|
||||||
case value_t::discarded:
|
}
|
||||||
case value_t::null:
|
else
|
||||||
default:
|
{
|
||||||
dump_scalar(val, current_indent);
|
put_char('[');
|
||||||
return;
|
|
||||||
|
// first n-1 elements
|
||||||
|
for (auto i = val.m_data.m_value.array->cbegin();
|
||||||
|
i != val.m_data.m_value.array->cend() - 1; ++i)
|
||||||
|
{
|
||||||
|
dump_element(*i, 0, depth + 1);
|
||||||
|
put_char(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
// last element
|
||||||
|
JSON_ASSERT(!val.m_data.m_value.array->empty());
|
||||||
|
dump_element(val.m_data.m_value.array->back(), 0, depth + 1);
|
||||||
|
|
||||||
|
put_char(']');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +261,7 @@ class serializer
|
|||||||
/*!
|
/*!
|
||||||
@brief serialize an element of a container
|
@brief serialize an element of a container
|
||||||
|
|
||||||
Most elements are scalars, which need none of the state dump_internal() keeps
|
Most elements are scalars, which need none of the state dump_container() keeps
|
||||||
for a container; writing them from here spares each the entry and exit of
|
for a container; writing them from here spares each the entry and exit of
|
||||||
that function.
|
that function.
|
||||||
*/
|
*/
|
||||||
@@ -304,7 +271,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
if (val.m_data.m_type == value_t::object || val.m_data.m_type == value_t::array)
|
if (val.m_data.m_type == value_t::object || val.m_data.m_type == value_t::array)
|
||||||
{
|
{
|
||||||
dump_internal(val, current_indent, depth);
|
dump_container(val, current_indent, depth);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -315,7 +282,7 @@ class serializer
|
|||||||
/*!
|
/*!
|
||||||
@brief write out @a val and everything below it without the call stack
|
@brief write out @a val and everything below it without the call stack
|
||||||
|
|
||||||
Emits the same bytes as @ref dump_internal, keeping the containers it has
|
Emits the same bytes as @ref dump_container, keeping the containers it has
|
||||||
entered on an explicit stack instead of descending into them. Only reached
|
entered on an explicit stack instead of descending into them. Only reached
|
||||||
for values nested deeper than @ref recursion_depth_limit, which is why it is not
|
for values nested deeper than @ref recursion_depth_limit, which is why it is not
|
||||||
written for speed: walking every value this way measured up to 20% slower on
|
written for speed: walking every value this way measured up to 20% slower on
|
||||||
@@ -343,8 +310,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
if (pretty_print)
|
if (pretty_print)
|
||||||
{
|
{
|
||||||
put_char('\n');
|
put_literal_indent("\n", frame.current_indent);
|
||||||
put_indent(frame.current_indent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
put_char('}');
|
put_char('}');
|
||||||
@@ -373,11 +339,11 @@ class serializer
|
|||||||
|
|
||||||
if (pretty_print)
|
if (pretty_print)
|
||||||
{
|
{
|
||||||
put_quoted(frame.object_it->first, "\": ");
|
put_object_key(frame.object_it->first, "\": ");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
put_quoted(frame.object_it->first, "\":");
|
put_object_key(frame.object_it->first, "\":");
|
||||||
}
|
}
|
||||||
|
|
||||||
const BasicJsonType& element = frame.object_it->second;
|
const BasicJsonType& element = frame.object_it->second;
|
||||||
@@ -396,8 +362,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
if (pretty_print)
|
if (pretty_print)
|
||||||
{
|
{
|
||||||
put_char('\n');
|
put_literal_indent("\n", frame.current_indent);
|
||||||
put_indent(frame.current_indent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
put_char(']');
|
put_char(']');
|
||||||
@@ -538,7 +503,7 @@ class serializer
|
|||||||
/*!
|
/*!
|
||||||
@brief serialize the value @a val, which is neither an object nor an array
|
@brief serialize the value @a val, which is neither an object nor an array
|
||||||
|
|
||||||
Shared by @ref dump_internal and @ref dump_value, so that a value is written
|
Shared by @ref dump_element and @ref dump_value, so that a value is written
|
||||||
the same way however deeply it is nested. A binary value is written out here
|
the same way however deeply it is nested. A binary value is written out here
|
||||||
in full: it looks like an object, but has no elements to descend into.
|
in full: it looks like an object, but has no elements to descend into.
|
||||||
|
|
||||||
@@ -616,7 +581,7 @@ class serializer
|
|||||||
/// other scalars do not pay for its register needs (and the other way round)
|
/// other scalars do not pay for its register needs (and the other way round)
|
||||||
JSON_HEDLEY_NEVER_INLINE void dump_string_value(const string_t& s)
|
JSON_HEDLEY_NEVER_INLINE void dump_string_value(const string_t& s)
|
||||||
{
|
{
|
||||||
#if JSON_DUMP_SHORT_STRINGS
|
#if JSON_LITTLE_ENDIAN_TARGET
|
||||||
if (s.size() <= short_string_max && put_short_quoted(s, "\""))
|
if (s.size() <= short_string_max && put_short_quoted(s, "\""))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -650,19 +615,15 @@ class serializer
|
|||||||
{
|
{
|
||||||
if (pretty_print)
|
if (pretty_print)
|
||||||
{
|
{
|
||||||
put_literal("{\n");
|
|
||||||
|
|
||||||
// variable to hold indentation for the bytes
|
// variable to hold indentation for the bytes
|
||||||
const auto new_indent = next_indent(current_indent, indent_step);
|
const auto new_indent = next_indent(current_indent, indent_step);
|
||||||
|
|
||||||
put_indent(new_indent);
|
put_literal_indent("{\n", new_indent);
|
||||||
|
|
||||||
put_literal("\"bytes\": [");
|
put_literal("\"bytes\": [");
|
||||||
|
|
||||||
if (!binary.empty())
|
if (!binary.empty())
|
||||||
{
|
{
|
||||||
for (auto i = binary.cbegin();
|
for (auto i = binary.cbegin(); i != binary.cend() - 1; ++i)
|
||||||
i != binary.cend() - 1; ++i)
|
|
||||||
{
|
{
|
||||||
dump_byte(*i);
|
dump_byte(*i);
|
||||||
put_literal(", ");
|
put_literal(", ");
|
||||||
@@ -670,8 +631,7 @@ class serializer
|
|||||||
dump_byte(binary.back());
|
dump_byte(binary.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
put_literal("],\n");
|
put_literal_indent("],\n", new_indent);
|
||||||
put_indent(new_indent);
|
|
||||||
|
|
||||||
put_literal("\"subtype\": ");
|
put_literal("\"subtype\": ");
|
||||||
if (binary.has_subtype())
|
if (binary.has_subtype())
|
||||||
@@ -682,8 +642,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
put_literal("null");
|
put_literal("null");
|
||||||
}
|
}
|
||||||
put_char('\n');
|
put_literal_indent("\n", current_indent);
|
||||||
put_indent(current_indent);
|
|
||||||
put_char('}');
|
put_char('}');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -692,8 +651,7 @@ class serializer
|
|||||||
|
|
||||||
if (!binary.empty())
|
if (!binary.empty())
|
||||||
{
|
{
|
||||||
for (auto i = binary.cbegin();
|
for (auto i = binary.cbegin(); i != binary.cend() - 1; ++i)
|
||||||
i != binary.cend() - 1; ++i)
|
|
||||||
{
|
{
|
||||||
dump_byte(*i);
|
dump_byte(*i);
|
||||||
put_char(',');
|
put_char(',');
|
||||||
@@ -731,17 +689,19 @@ class serializer
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/*!
|
/*!
|
||||||
@brief append the string @a s in quotes, followed by @a tail
|
@brief append the object key @a s in quotes, followed by @a tail
|
||||||
|
|
||||||
The quote, the string and @a tail (the closing quote, possibly with the
|
@a tail is the closing quote and the colon, followed by a space when
|
||||||
colon of an object key) are the bulk of what most documents consist of, and
|
pretty-printing. The callers pass it because each of them already sits on
|
||||||
most keys and many values are short and plain. Those are handled in one go
|
the pretty-printing or the compact path; choosing it here would test
|
||||||
by put_short_quoted(); everything else takes the general path.
|
@ref pretty_print once more for every key. Most keys are short and plain
|
||||||
|
and are written in one go by put_short_quoted(); everything else takes the
|
||||||
|
general path.
|
||||||
*/
|
*/
|
||||||
template<std::size_t N>
|
template<std::size_t N>
|
||||||
void put_quoted(const string_t& s, const char (&tail)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
void put_object_key(const string_t& s, const char (&tail)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||||
{
|
{
|
||||||
#if JSON_DUMP_SHORT_STRINGS
|
#if JSON_LITTLE_ENDIAN_TARGET
|
||||||
if (put_short_quoted(s, tail))
|
if (put_short_quoted(s, tail))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -752,7 +712,8 @@ class serializer
|
|||||||
put_literal(tail);
|
put_literal(tail);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if JSON_DUMP_SHORT_STRINGS
|
// put_short_quoted() assembles words from little-endian loads
|
||||||
|
#if JSON_LITTLE_ENDIAN_TARGET
|
||||||
/// the longest string put_short_quoted() takes
|
/// the longest string put_short_quoted() takes
|
||||||
static constexpr std::size_t short_string_max = JSON_SCAN_VECTOR ? 32 : 16;
|
static constexpr std::size_t short_string_max = JSON_SCAN_VECTOR ? 32 : 16;
|
||||||
|
|
||||||
@@ -954,7 +915,7 @@ class serializer
|
|||||||
Complexity: Linear in the length of string @a s.
|
Complexity: Linear in the length of string @a s.
|
||||||
|
|
||||||
@note kept out of line: with the short-string attempts in front of it gone
|
@note kept out of line: with the short-string attempts in front of it gone
|
||||||
from here, compilers would otherwise inline it into @ref put_quoted
|
from here, compilers would otherwise inline it into @ref put_object_key
|
||||||
and then no longer into the loops that call that
|
and then no longer into the loops that call that
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_NEVER_INLINE void dump_escaped(const string_t& s)
|
JSON_HEDLEY_NEVER_INLINE void dump_escaped(const string_t& s)
|
||||||
@@ -968,7 +929,7 @@ class serializer
|
|||||||
// Where put_short_quoted() exists and takes strings of up to 32 bytes,
|
// Where put_short_quoted() exists and takes strings of up to 32 bytes,
|
||||||
// the callers have tried it already, and repeating the attempt only
|
// the callers have tried it already, and repeating the attempt only
|
||||||
// costs time for strings that need escaping or are not ASCII.
|
// costs time for strings that need escaping or are not ASCII.
|
||||||
#if !(JSON_DUMP_SHORT_STRINGS && JSON_SCAN_VECTOR)
|
#if !JSON_LITTLE_ENDIAN_TARGET || !JSON_SCAN_VECTOR
|
||||||
if (s.size() <= 32)
|
if (s.size() <= 32)
|
||||||
{
|
{
|
||||||
const auto& copyable = ascii_copyable();
|
const auto& copyable = ascii_copyable();
|
||||||
@@ -1406,18 +1367,9 @@ class serializer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the usual indentation: four words of the indentation character are
|
// the usual indentation, without a call of memset
|
||||||
// stored unconditionally and only the part wanted is claimed, which
|
if (JSON_HEDLEY_LIKELY(put_short_literal_indent("", indent)))
|
||||||
// spares the call of memset
|
|
||||||
if (JSON_HEDLEY_LIKELY(indent <= 32 && write_buffer_pos + 32 <= write_buffer.size()))
|
|
||||||
{
|
{
|
||||||
const std::uint64_t word = 0x0101010101010101ull * static_cast<unsigned char>(indent_char);
|
|
||||||
char* const d = write_buffer.data() + write_buffer_pos;
|
|
||||||
std::memcpy(d, &word, sizeof(word));
|
|
||||||
std::memcpy(d + 8, &word, sizeof(word));
|
|
||||||
std::memcpy(d + 16, &word, sizeof(word));
|
|
||||||
std::memcpy(d + 24, &word, sizeof(word));
|
|
||||||
write_buffer_pos += indent;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1458,28 +1410,47 @@ class serializer
|
|||||||
@brief append a string literal and then @a indent indentation characters
|
@brief append a string literal and then @a indent indentation characters
|
||||||
|
|
||||||
The usual line break and indentation go into the buffer with one check of
|
The usual line break and indentation go into the buffer with one check of
|
||||||
its room and four stores of the indentation, see @ref put_indent.
|
its room and four stores of the indentation, see @ref put_short_literal_indent.
|
||||||
*/
|
*/
|
||||||
template<std::size_t N>
|
template<std::size_t N>
|
||||||
JSON_HEDLEY_ALWAYS_INLINE void put_literal_indent(const char (&s)[N], const std::size_t indent) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
JSON_HEDLEY_ALWAYS_INLINE void put_literal_indent(const char (&s)[N], const std::size_t indent) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||||
{
|
{
|
||||||
constexpr std::size_t length = N - 1;
|
if (JSON_HEDLEY_LIKELY(put_short_literal_indent(s, indent)))
|
||||||
if (JSON_HEDLEY_LIKELY(indent <= 32 && write_buffer_pos + length + 32 <= write_buffer.size()))
|
|
||||||
{
|
{
|
||||||
const std::uint64_t word = 0x0101010101010101ull * static_cast<unsigned char>(indent_char);
|
|
||||||
char* const d = write_buffer.data() + write_buffer_pos;
|
|
||||||
std::memcpy(d, s, length);
|
|
||||||
std::memcpy(d + length, &word, sizeof(word));
|
|
||||||
std::memcpy(d + length + 8, &word, sizeof(word));
|
|
||||||
std::memcpy(d + length + 16, &word, sizeof(word));
|
|
||||||
std::memcpy(d + length + 24, &word, sizeof(word));
|
|
||||||
write_buffer_pos += length + indent;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
put_literal(s);
|
put_literal(s);
|
||||||
put_indent(indent);
|
put_indent(indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief append a string literal and up to 32 indentation characters in one go
|
||||||
|
|
||||||
|
Four words of the indentation character are stored unconditionally and only
|
||||||
|
the part wanted is claimed, which spares the call of memset. Shared by
|
||||||
|
@ref put_indent (with an empty literal, whose copy compiles to nothing) and
|
||||||
|
@ref put_literal_indent. Returns false, having written nothing, if
|
||||||
|
@a indent is wider or the write buffer lacks the room.
|
||||||
|
*/
|
||||||
|
template<std::size_t N>
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE bool put_short_literal_indent(const char (&s)[N], const std::size_t indent) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||||
|
{
|
||||||
|
constexpr std::size_t length = N - 1;
|
||||||
|
if (indent > 32 || write_buffer_pos + length + 32 > write_buffer.size())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const std::uint64_t word = 0x0101010101010101ull * static_cast<unsigned char>(indent_char);
|
||||||
|
char* const d = write_buffer.data() + write_buffer_pos;
|
||||||
|
std::memcpy(d, s, length);
|
||||||
|
std::memcpy(d + length, &word, sizeof(word));
|
||||||
|
std::memcpy(d + length + 8, &word, sizeof(word));
|
||||||
|
std::memcpy(d + length + 16, &word, sizeof(word));
|
||||||
|
std::memcpy(d + length + 24, &word, sizeof(word));
|
||||||
|
write_buffer_pos += length + indent;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief append a string literal to the write buffer
|
@brief append a string literal to the write buffer
|
||||||
|
|
||||||
@@ -1791,6 +1762,9 @@ class serializer
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// a lead byte of 0x80 or above starts a sequence of 2 to 4 bytes
|
||||||
|
JSON_ASSERT(length >= 2 && length <= 4);
|
||||||
|
|
||||||
const auto b0 = static_cast<std::uint32_t>(data[i]);
|
const auto b0 = static_cast<std::uint32_t>(data[i]);
|
||||||
const auto b1 = static_cast<std::uint32_t>(data[i + 1]);
|
const auto b1 = static_cast<std::uint32_t>(data[i + 1]);
|
||||||
std::uint32_t codepoint = 0;
|
std::uint32_t codepoint = 0;
|
||||||
@@ -1802,7 +1776,7 @@ class serializer
|
|||||||
case 3:
|
case 3:
|
||||||
codepoint = ((b0 & 0x0Fu) << 12u) | ((b1 & 0x3Fu) << 6u) | (static_cast<std::uint32_t>(data[i + 2]) & 0x3Fu);
|
codepoint = ((b0 & 0x0Fu) << 12u) | ((b1 & 0x3Fu) << 6u) | (static_cast<std::uint32_t>(data[i + 2]) & 0x3Fu);
|
||||||
break;
|
break;
|
||||||
default:
|
default: // 4
|
||||||
codepoint = ((b0 & 0x07u) << 18u) | ((b1 & 0x3Fu) << 12u) | ((static_cast<std::uint32_t>(data[i + 2]) & 0x3Fu) << 6u) | (static_cast<std::uint32_t>(data[i + 3]) & 0x3Fu);
|
codepoint = ((b0 & 0x07u) << 18u) | ((b1 & 0x3Fu) << 12u) | ((static_cast<std::uint32_t>(data[i + 2]) & 0x3Fu) << 6u) | (static_cast<std::uint32_t>(data[i + 3]) & 0x3Fu);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+165
-192
@@ -30178,14 +30178,6 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
// #include <nlohmann/detail/value_t.hpp>
|
// #include <nlohmann/detail/value_t.hpp>
|
||||||
|
|
||||||
|
|
||||||
// the short-string fast path of the serializer assembles words from little-endian
|
|
||||||
// loads, see serializer::put_short_quoted()
|
|
||||||
#if defined(_MSC_VER) || defined(__x86_64__) || defined(__i386__) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
|
||||||
#define JSON_DUMP_SHORT_STRINGS 1
|
|
||||||
#else
|
|
||||||
#define JSON_DUMP_SHORT_STRINGS 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
@@ -30223,7 +30215,7 @@ class serializer
|
|||||||
|
|
||||||
None of @a pretty_print_, @a ensure_ascii_ and @a indent_step_ change over
|
None of @a pretty_print_, @a ensure_ascii_ and @a indent_step_ change over
|
||||||
the life of the serializer, so they are captured once here instead of
|
the life of the serializer, so they are captured once here instead of
|
||||||
being threaded through every call to @ref dump, @ref dump_internal and
|
being threaded through every call to @ref dump, @ref dump_container and
|
||||||
@ref dump_iteratively.
|
@ref dump_iteratively.
|
||||||
*/
|
*/
|
||||||
serializer(output_adapter_protocol<char>& s, const char ichar,
|
serializer(output_adapter_protocol<char>& s, const char ichar,
|
||||||
@@ -30270,18 +30262,18 @@ class serializer
|
|||||||
void dump(const BasicJsonType& val,
|
void dump(const BasicJsonType& val,
|
||||||
const std::size_t current_indent = 0)
|
const std::size_t current_indent = 0)
|
||||||
{
|
{
|
||||||
dump_internal(val, current_indent);
|
dump_element(val, current_indent, 0);
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
/*!
|
/*!
|
||||||
@brief worker for @ref dump
|
@brief serialize the object or array @a val
|
||||||
|
|
||||||
Identical in behavior to the historical @ref dump, but writes into the
|
Writes into the serializer's internal @ref write_buffer instead of issuing
|
||||||
serializer's internal @ref write_buffer instead of issuing a virtual call
|
a virtual call per token; the public @ref dump flushes the buffer once the
|
||||||
per token. The public @ref dump wraps this and flushes the buffer once the
|
top-level value has been serialized. Scalars are written by @ref dump_scalar,
|
||||||
top-level value has been serialized.
|
which @ref dump_element calls without entering this function.
|
||||||
|
|
||||||
Serializing a container descends into its elements, so a value nested deeply
|
Serializing a container descends into its elements, so a value nested deeply
|
||||||
enough used to exhaust the call stack and terminate the process with no
|
enough used to exhaust the call stack and terminate the process with no
|
||||||
@@ -30292,145 +30284,120 @@ class serializer
|
|||||||
|
|
||||||
@sa https://github.com/nlohmann/json/issues/5387
|
@sa https://github.com/nlohmann/json/issues/5387
|
||||||
*/
|
*/
|
||||||
void dump_internal(const BasicJsonType& val,
|
void dump_container(const BasicJsonType& val,
|
||||||
const std::size_t current_indent = 0,
|
const std::size_t current_indent,
|
||||||
const std::size_t depth = 0)
|
const std::size_t depth)
|
||||||
{
|
{
|
||||||
switch (val.m_data.m_type)
|
JSON_ASSERT(val.m_data.m_type == value_t::object || val.m_data.m_type == value_t::array);
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(depth >= recursion_depth_limit()))
|
||||||
{
|
{
|
||||||
case value_t::object:
|
dump_iteratively(val, current_indent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val.m_data.m_type == value_t::object)
|
||||||
|
{
|
||||||
|
if (val.m_data.m_value.object->empty())
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(depth >= recursion_depth_limit()))
|
put_literal("{}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pretty_print)
|
||||||
|
{
|
||||||
|
// variable to hold indentation for recursive calls
|
||||||
|
const auto new_indent = next_indent(current_indent, indent_step);
|
||||||
|
|
||||||
|
put_literal_indent("{\n", new_indent);
|
||||||
|
|
||||||
|
// first n-1 elements
|
||||||
|
auto i = val.m_data.m_value.object->cbegin();
|
||||||
|
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
|
||||||
{
|
{
|
||||||
dump_iteratively(val, current_indent);
|
put_object_key(i->first, "\": ");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val.m_data.m_value.object->empty())
|
|
||||||
{
|
|
||||||
put_literal("{}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pretty_print)
|
|
||||||
{
|
|
||||||
// variable to hold indentation for recursive calls
|
|
||||||
const auto new_indent = next_indent(current_indent, indent_step);
|
|
||||||
|
|
||||||
put_literal_indent("{\n", new_indent);
|
|
||||||
|
|
||||||
// first n-1 elements
|
|
||||||
auto i = val.m_data.m_value.object->cbegin();
|
|
||||||
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
|
|
||||||
{
|
|
||||||
put_quoted(i->first, "\": ");
|
|
||||||
dump_element(i->second, new_indent, depth + 1);
|
|
||||||
put_literal_indent(",\n", new_indent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// last element
|
|
||||||
JSON_ASSERT(i != val.m_data.m_value.object->cend());
|
|
||||||
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
|
|
||||||
put_quoted(i->first, "\": ");
|
|
||||||
dump_element(i->second, new_indent, depth + 1);
|
dump_element(i->second, new_indent, depth + 1);
|
||||||
|
put_literal_indent(",\n", new_indent);
|
||||||
put_literal_indent("\n", current_indent);
|
|
||||||
put_char('}');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
put_char('{');
|
|
||||||
|
|
||||||
// first n-1 elements
|
|
||||||
auto i = val.m_data.m_value.object->cbegin();
|
|
||||||
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
|
|
||||||
{
|
|
||||||
put_quoted(i->first, "\":");
|
|
||||||
dump_element(i->second, current_indent, depth + 1);
|
|
||||||
put_char(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
// last element
|
|
||||||
JSON_ASSERT(i != val.m_data.m_value.object->cend());
|
|
||||||
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
|
|
||||||
put_quoted(i->first, "\":");
|
|
||||||
dump_element(i->second, current_indent, depth + 1);
|
|
||||||
|
|
||||||
put_char('}');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
// last element
|
||||||
|
JSON_ASSERT(i != val.m_data.m_value.object->cend());
|
||||||
|
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
|
||||||
|
put_object_key(i->first, "\": ");
|
||||||
|
dump_element(i->second, new_indent, depth + 1);
|
||||||
|
|
||||||
|
put_literal_indent("\n", current_indent);
|
||||||
|
put_char('}');
|
||||||
}
|
}
|
||||||
|
else
|
||||||
case value_t::array:
|
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(depth >= recursion_depth_limit()))
|
put_char('{');
|
||||||
|
|
||||||
|
// first n-1 elements
|
||||||
|
auto i = val.m_data.m_value.object->cbegin();
|
||||||
|
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
|
||||||
{
|
{
|
||||||
dump_iteratively(val, current_indent);
|
put_object_key(i->first, "\":");
|
||||||
return;
|
dump_element(i->second, 0, depth + 1);
|
||||||
|
put_char(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (val.m_data.m_value.array->empty())
|
// last element
|
||||||
{
|
JSON_ASSERT(i != val.m_data.m_value.object->cend());
|
||||||
put_literal("[]");
|
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
|
||||||
return;
|
put_object_key(i->first, "\":");
|
||||||
}
|
dump_element(i->second, 0, depth + 1);
|
||||||
|
|
||||||
if (pretty_print)
|
put_char('}');
|
||||||
{
|
}
|
||||||
// variable to hold indentation for recursive calls
|
return;
|
||||||
const auto new_indent = next_indent(current_indent, indent_step);
|
}
|
||||||
|
|
||||||
put_literal_indent("[\n", new_indent);
|
if (val.m_data.m_value.array->empty())
|
||||||
|
{
|
||||||
|
put_literal("[]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// first n-1 elements
|
if (pretty_print)
|
||||||
for (auto i = val.m_data.m_value.array->cbegin();
|
{
|
||||||
i != val.m_data.m_value.array->cend() - 1; ++i)
|
// variable to hold indentation for recursive calls
|
||||||
{
|
const auto new_indent = next_indent(current_indent, indent_step);
|
||||||
dump_element(*i, new_indent, depth + 1);
|
|
||||||
put_literal_indent(",\n", new_indent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// last element
|
put_literal_indent("[\n", new_indent);
|
||||||
JSON_ASSERT(!val.m_data.m_value.array->empty());
|
|
||||||
dump_element(val.m_data.m_value.array->back(), new_indent, depth + 1);
|
|
||||||
|
|
||||||
put_literal_indent("\n", current_indent);
|
// first n-1 elements
|
||||||
put_char(']');
|
for (auto i = val.m_data.m_value.array->cbegin();
|
||||||
}
|
i != val.m_data.m_value.array->cend() - 1; ++i)
|
||||||
else
|
{
|
||||||
{
|
dump_element(*i, new_indent, depth + 1);
|
||||||
put_char('[');
|
put_literal_indent(",\n", new_indent);
|
||||||
|
|
||||||
// first n-1 elements
|
|
||||||
for (auto i = val.m_data.m_value.array->cbegin();
|
|
||||||
i != val.m_data.m_value.array->cend() - 1; ++i)
|
|
||||||
{
|
|
||||||
dump_element(*i, current_indent, depth + 1);
|
|
||||||
put_char(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
// last element
|
|
||||||
JSON_ASSERT(!val.m_data.m_value.array->empty());
|
|
||||||
dump_element(val.m_data.m_value.array->back(), current_indent, depth + 1);
|
|
||||||
|
|
||||||
put_char(']');
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case value_t::string:
|
// last element
|
||||||
case value_t::binary:
|
JSON_ASSERT(!val.m_data.m_value.array->empty());
|
||||||
case value_t::boolean:
|
dump_element(val.m_data.m_value.array->back(), new_indent, depth + 1);
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
put_literal_indent("\n", current_indent);
|
||||||
case value_t::number_float:
|
put_char(']');
|
||||||
case value_t::discarded:
|
}
|
||||||
case value_t::null:
|
else
|
||||||
default:
|
{
|
||||||
dump_scalar(val, current_indent);
|
put_char('[');
|
||||||
return;
|
|
||||||
|
// first n-1 elements
|
||||||
|
for (auto i = val.m_data.m_value.array->cbegin();
|
||||||
|
i != val.m_data.m_value.array->cend() - 1; ++i)
|
||||||
|
{
|
||||||
|
dump_element(*i, 0, depth + 1);
|
||||||
|
put_char(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
// last element
|
||||||
|
JSON_ASSERT(!val.m_data.m_value.array->empty());
|
||||||
|
dump_element(val.m_data.m_value.array->back(), 0, depth + 1);
|
||||||
|
|
||||||
|
put_char(']');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30438,7 +30405,7 @@ class serializer
|
|||||||
/*!
|
/*!
|
||||||
@brief serialize an element of a container
|
@brief serialize an element of a container
|
||||||
|
|
||||||
Most elements are scalars, which need none of the state dump_internal() keeps
|
Most elements are scalars, which need none of the state dump_container() keeps
|
||||||
for a container; writing them from here spares each the entry and exit of
|
for a container; writing them from here spares each the entry and exit of
|
||||||
that function.
|
that function.
|
||||||
*/
|
*/
|
||||||
@@ -30448,7 +30415,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
if (val.m_data.m_type == value_t::object || val.m_data.m_type == value_t::array)
|
if (val.m_data.m_type == value_t::object || val.m_data.m_type == value_t::array)
|
||||||
{
|
{
|
||||||
dump_internal(val, current_indent, depth);
|
dump_container(val, current_indent, depth);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -30459,7 +30426,7 @@ class serializer
|
|||||||
/*!
|
/*!
|
||||||
@brief write out @a val and everything below it without the call stack
|
@brief write out @a val and everything below it without the call stack
|
||||||
|
|
||||||
Emits the same bytes as @ref dump_internal, keeping the containers it has
|
Emits the same bytes as @ref dump_container, keeping the containers it has
|
||||||
entered on an explicit stack instead of descending into them. Only reached
|
entered on an explicit stack instead of descending into them. Only reached
|
||||||
for values nested deeper than @ref recursion_depth_limit, which is why it is not
|
for values nested deeper than @ref recursion_depth_limit, which is why it is not
|
||||||
written for speed: walking every value this way measured up to 20% slower on
|
written for speed: walking every value this way measured up to 20% slower on
|
||||||
@@ -30487,8 +30454,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
if (pretty_print)
|
if (pretty_print)
|
||||||
{
|
{
|
||||||
put_char('\n');
|
put_literal_indent("\n", frame.current_indent);
|
||||||
put_indent(frame.current_indent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
put_char('}');
|
put_char('}');
|
||||||
@@ -30517,11 +30483,11 @@ class serializer
|
|||||||
|
|
||||||
if (pretty_print)
|
if (pretty_print)
|
||||||
{
|
{
|
||||||
put_quoted(frame.object_it->first, "\": ");
|
put_object_key(frame.object_it->first, "\": ");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
put_quoted(frame.object_it->first, "\":");
|
put_object_key(frame.object_it->first, "\":");
|
||||||
}
|
}
|
||||||
|
|
||||||
const BasicJsonType& element = frame.object_it->second;
|
const BasicJsonType& element = frame.object_it->second;
|
||||||
@@ -30540,8 +30506,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
if (pretty_print)
|
if (pretty_print)
|
||||||
{
|
{
|
||||||
put_char('\n');
|
put_literal_indent("\n", frame.current_indent);
|
||||||
put_indent(frame.current_indent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
put_char(']');
|
put_char(']');
|
||||||
@@ -30682,7 +30647,7 @@ class serializer
|
|||||||
/*!
|
/*!
|
||||||
@brief serialize the value @a val, which is neither an object nor an array
|
@brief serialize the value @a val, which is neither an object nor an array
|
||||||
|
|
||||||
Shared by @ref dump_internal and @ref dump_value, so that a value is written
|
Shared by @ref dump_element and @ref dump_value, so that a value is written
|
||||||
the same way however deeply it is nested. A binary value is written out here
|
the same way however deeply it is nested. A binary value is written out here
|
||||||
in full: it looks like an object, but has no elements to descend into.
|
in full: it looks like an object, but has no elements to descend into.
|
||||||
|
|
||||||
@@ -30760,7 +30725,7 @@ class serializer
|
|||||||
/// other scalars do not pay for its register needs (and the other way round)
|
/// other scalars do not pay for its register needs (and the other way round)
|
||||||
JSON_HEDLEY_NEVER_INLINE void dump_string_value(const string_t& s)
|
JSON_HEDLEY_NEVER_INLINE void dump_string_value(const string_t& s)
|
||||||
{
|
{
|
||||||
#if JSON_DUMP_SHORT_STRINGS
|
#if JSON_LITTLE_ENDIAN_TARGET
|
||||||
if (s.size() <= short_string_max && put_short_quoted(s, "\""))
|
if (s.size() <= short_string_max && put_short_quoted(s, "\""))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -30794,19 +30759,15 @@ class serializer
|
|||||||
{
|
{
|
||||||
if (pretty_print)
|
if (pretty_print)
|
||||||
{
|
{
|
||||||
put_literal("{\n");
|
|
||||||
|
|
||||||
// variable to hold indentation for the bytes
|
// variable to hold indentation for the bytes
|
||||||
const auto new_indent = next_indent(current_indent, indent_step);
|
const auto new_indent = next_indent(current_indent, indent_step);
|
||||||
|
|
||||||
put_indent(new_indent);
|
put_literal_indent("{\n", new_indent);
|
||||||
|
|
||||||
put_literal("\"bytes\": [");
|
put_literal("\"bytes\": [");
|
||||||
|
|
||||||
if (!binary.empty())
|
if (!binary.empty())
|
||||||
{
|
{
|
||||||
for (auto i = binary.cbegin();
|
for (auto i = binary.cbegin(); i != binary.cend() - 1; ++i)
|
||||||
i != binary.cend() - 1; ++i)
|
|
||||||
{
|
{
|
||||||
dump_byte(*i);
|
dump_byte(*i);
|
||||||
put_literal(", ");
|
put_literal(", ");
|
||||||
@@ -30814,8 +30775,7 @@ class serializer
|
|||||||
dump_byte(binary.back());
|
dump_byte(binary.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
put_literal("],\n");
|
put_literal_indent("],\n", new_indent);
|
||||||
put_indent(new_indent);
|
|
||||||
|
|
||||||
put_literal("\"subtype\": ");
|
put_literal("\"subtype\": ");
|
||||||
if (binary.has_subtype())
|
if (binary.has_subtype())
|
||||||
@@ -30826,8 +30786,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
put_literal("null");
|
put_literal("null");
|
||||||
}
|
}
|
||||||
put_char('\n');
|
put_literal_indent("\n", current_indent);
|
||||||
put_indent(current_indent);
|
|
||||||
put_char('}');
|
put_char('}');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -30836,8 +30795,7 @@ class serializer
|
|||||||
|
|
||||||
if (!binary.empty())
|
if (!binary.empty())
|
||||||
{
|
{
|
||||||
for (auto i = binary.cbegin();
|
for (auto i = binary.cbegin(); i != binary.cend() - 1; ++i)
|
||||||
i != binary.cend() - 1; ++i)
|
|
||||||
{
|
{
|
||||||
dump_byte(*i);
|
dump_byte(*i);
|
||||||
put_char(',');
|
put_char(',');
|
||||||
@@ -30875,17 +30833,19 @@ class serializer
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/*!
|
/*!
|
||||||
@brief append the string @a s in quotes, followed by @a tail
|
@brief append the object key @a s in quotes, followed by @a tail
|
||||||
|
|
||||||
The quote, the string and @a tail (the closing quote, possibly with the
|
@a tail is the closing quote and the colon, followed by a space when
|
||||||
colon of an object key) are the bulk of what most documents consist of, and
|
pretty-printing. The callers pass it because each of them already sits on
|
||||||
most keys and many values are short and plain. Those are handled in one go
|
the pretty-printing or the compact path; choosing it here would test
|
||||||
by put_short_quoted(); everything else takes the general path.
|
@ref pretty_print once more for every key. Most keys are short and plain
|
||||||
|
and are written in one go by put_short_quoted(); everything else takes the
|
||||||
|
general path.
|
||||||
*/
|
*/
|
||||||
template<std::size_t N>
|
template<std::size_t N>
|
||||||
void put_quoted(const string_t& s, const char (&tail)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
void put_object_key(const string_t& s, const char (&tail)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||||
{
|
{
|
||||||
#if JSON_DUMP_SHORT_STRINGS
|
#if JSON_LITTLE_ENDIAN_TARGET
|
||||||
if (put_short_quoted(s, tail))
|
if (put_short_quoted(s, tail))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -30896,7 +30856,8 @@ class serializer
|
|||||||
put_literal(tail);
|
put_literal(tail);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if JSON_DUMP_SHORT_STRINGS
|
// put_short_quoted() assembles words from little-endian loads
|
||||||
|
#if JSON_LITTLE_ENDIAN_TARGET
|
||||||
/// the longest string put_short_quoted() takes
|
/// the longest string put_short_quoted() takes
|
||||||
static constexpr std::size_t short_string_max = JSON_SCAN_VECTOR ? 32 : 16;
|
static constexpr std::size_t short_string_max = JSON_SCAN_VECTOR ? 32 : 16;
|
||||||
|
|
||||||
@@ -31098,7 +31059,7 @@ class serializer
|
|||||||
Complexity: Linear in the length of string @a s.
|
Complexity: Linear in the length of string @a s.
|
||||||
|
|
||||||
@note kept out of line: with the short-string attempts in front of it gone
|
@note kept out of line: with the short-string attempts in front of it gone
|
||||||
from here, compilers would otherwise inline it into @ref put_quoted
|
from here, compilers would otherwise inline it into @ref put_object_key
|
||||||
and then no longer into the loops that call that
|
and then no longer into the loops that call that
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_NEVER_INLINE void dump_escaped(const string_t& s)
|
JSON_HEDLEY_NEVER_INLINE void dump_escaped(const string_t& s)
|
||||||
@@ -31112,7 +31073,7 @@ class serializer
|
|||||||
// Where put_short_quoted() exists and takes strings of up to 32 bytes,
|
// Where put_short_quoted() exists and takes strings of up to 32 bytes,
|
||||||
// the callers have tried it already, and repeating the attempt only
|
// the callers have tried it already, and repeating the attempt only
|
||||||
// costs time for strings that need escaping or are not ASCII.
|
// costs time for strings that need escaping or are not ASCII.
|
||||||
#if !(JSON_DUMP_SHORT_STRINGS && JSON_SCAN_VECTOR)
|
#if !JSON_LITTLE_ENDIAN_TARGET || !JSON_SCAN_VECTOR
|
||||||
if (s.size() <= 32)
|
if (s.size() <= 32)
|
||||||
{
|
{
|
||||||
const auto& copyable = ascii_copyable();
|
const auto& copyable = ascii_copyable();
|
||||||
@@ -31550,18 +31511,9 @@ class serializer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the usual indentation: four words of the indentation character are
|
// the usual indentation, without a call of memset
|
||||||
// stored unconditionally and only the part wanted is claimed, which
|
if (JSON_HEDLEY_LIKELY(put_short_literal_indent("", indent)))
|
||||||
// spares the call of memset
|
|
||||||
if (JSON_HEDLEY_LIKELY(indent <= 32 && write_buffer_pos + 32 <= write_buffer.size()))
|
|
||||||
{
|
{
|
||||||
const std::uint64_t word = 0x0101010101010101ull * static_cast<unsigned char>(indent_char);
|
|
||||||
char* const d = write_buffer.data() + write_buffer_pos;
|
|
||||||
std::memcpy(d, &word, sizeof(word));
|
|
||||||
std::memcpy(d + 8, &word, sizeof(word));
|
|
||||||
std::memcpy(d + 16, &word, sizeof(word));
|
|
||||||
std::memcpy(d + 24, &word, sizeof(word));
|
|
||||||
write_buffer_pos += indent;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31602,28 +31554,47 @@ class serializer
|
|||||||
@brief append a string literal and then @a indent indentation characters
|
@brief append a string literal and then @a indent indentation characters
|
||||||
|
|
||||||
The usual line break and indentation go into the buffer with one check of
|
The usual line break and indentation go into the buffer with one check of
|
||||||
its room and four stores of the indentation, see @ref put_indent.
|
its room and four stores of the indentation, see @ref put_short_literal_indent.
|
||||||
*/
|
*/
|
||||||
template<std::size_t N>
|
template<std::size_t N>
|
||||||
JSON_HEDLEY_ALWAYS_INLINE void put_literal_indent(const char (&s)[N], const std::size_t indent) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
JSON_HEDLEY_ALWAYS_INLINE void put_literal_indent(const char (&s)[N], const std::size_t indent) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||||
{
|
{
|
||||||
constexpr std::size_t length = N - 1;
|
if (JSON_HEDLEY_LIKELY(put_short_literal_indent(s, indent)))
|
||||||
if (JSON_HEDLEY_LIKELY(indent <= 32 && write_buffer_pos + length + 32 <= write_buffer.size()))
|
|
||||||
{
|
{
|
||||||
const std::uint64_t word = 0x0101010101010101ull * static_cast<unsigned char>(indent_char);
|
|
||||||
char* const d = write_buffer.data() + write_buffer_pos;
|
|
||||||
std::memcpy(d, s, length);
|
|
||||||
std::memcpy(d + length, &word, sizeof(word));
|
|
||||||
std::memcpy(d + length + 8, &word, sizeof(word));
|
|
||||||
std::memcpy(d + length + 16, &word, sizeof(word));
|
|
||||||
std::memcpy(d + length + 24, &word, sizeof(word));
|
|
||||||
write_buffer_pos += length + indent;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
put_literal(s);
|
put_literal(s);
|
||||||
put_indent(indent);
|
put_indent(indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief append a string literal and up to 32 indentation characters in one go
|
||||||
|
|
||||||
|
Four words of the indentation character are stored unconditionally and only
|
||||||
|
the part wanted is claimed, which spares the call of memset. Shared by
|
||||||
|
@ref put_indent (with an empty literal, whose copy compiles to nothing) and
|
||||||
|
@ref put_literal_indent. Returns false, having written nothing, if
|
||||||
|
@a indent is wider or the write buffer lacks the room.
|
||||||
|
*/
|
||||||
|
template<std::size_t N>
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE bool put_short_literal_indent(const char (&s)[N], const std::size_t indent) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||||
|
{
|
||||||
|
constexpr std::size_t length = N - 1;
|
||||||
|
if (indent > 32 || write_buffer_pos + length + 32 > write_buffer.size())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const std::uint64_t word = 0x0101010101010101ull * static_cast<unsigned char>(indent_char);
|
||||||
|
char* const d = write_buffer.data() + write_buffer_pos;
|
||||||
|
std::memcpy(d, s, length);
|
||||||
|
std::memcpy(d + length, &word, sizeof(word));
|
||||||
|
std::memcpy(d + length + 8, &word, sizeof(word));
|
||||||
|
std::memcpy(d + length + 16, &word, sizeof(word));
|
||||||
|
std::memcpy(d + length + 24, &word, sizeof(word));
|
||||||
|
write_buffer_pos += length + indent;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief append a string literal to the write buffer
|
@brief append a string literal to the write buffer
|
||||||
|
|
||||||
@@ -31935,6 +31906,9 @@ class serializer
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// a lead byte of 0x80 or above starts a sequence of 2 to 4 bytes
|
||||||
|
JSON_ASSERT(length >= 2 && length <= 4);
|
||||||
|
|
||||||
const auto b0 = static_cast<std::uint32_t>(data[i]);
|
const auto b0 = static_cast<std::uint32_t>(data[i]);
|
||||||
const auto b1 = static_cast<std::uint32_t>(data[i + 1]);
|
const auto b1 = static_cast<std::uint32_t>(data[i + 1]);
|
||||||
std::uint32_t codepoint = 0;
|
std::uint32_t codepoint = 0;
|
||||||
@@ -31946,7 +31920,7 @@ class serializer
|
|||||||
case 3:
|
case 3:
|
||||||
codepoint = ((b0 & 0x0Fu) << 12u) | ((b1 & 0x3Fu) << 6u) | (static_cast<std::uint32_t>(data[i + 2]) & 0x3Fu);
|
codepoint = ((b0 & 0x0Fu) << 12u) | ((b1 & 0x3Fu) << 6u) | (static_cast<std::uint32_t>(data[i + 2]) & 0x3Fu);
|
||||||
break;
|
break;
|
||||||
default:
|
default: // 4
|
||||||
codepoint = ((b0 & 0x07u) << 18u) | ((b1 & 0x3Fu) << 12u) | ((static_cast<std::uint32_t>(data[i + 2]) & 0x3Fu) << 6u) | (static_cast<std::uint32_t>(data[i + 3]) & 0x3Fu);
|
codepoint = ((b0 & 0x07u) << 18u) | ((b1 & 0x3Fu) << 12u) | ((static_cast<std::uint32_t>(data[i + 2]) & 0x3Fu) << 6u) | (static_cast<std::uint32_t>(data[i + 3]) & 0x3Fu);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -41137,7 +41111,6 @@ struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-c
|
|||||||
#undef JSON_SCAN_SSSE3_TARGET
|
#undef JSON_SCAN_SSSE3_TARGET
|
||||||
#undef JSON_SCAN_VECTOR
|
#undef JSON_SCAN_VECTOR
|
||||||
#undef JSON_SCAN_VECTOR_UTF8
|
#undef JSON_SCAN_VECTOR_UTF8
|
||||||
#undef JSON_DUMP_SHORT_STRINGS
|
|
||||||
|
|
||||||
#ifndef JSON_TEST_KEEP_MACROS
|
#ifndef JSON_TEST_KEEP_MACROS
|
||||||
#undef JSON_CATCH
|
#undef JSON_CATCH
|
||||||
|
|||||||
Reference in new issue
Block a user