Update dump to be non-recursive

This commit is contained in:
John Fremlin 2024-08-22 21:02:09 -04:00 committed by John Fremlin
parent 960b763ecd
commit ccbafb4388
2 changed files with 572 additions and 464 deletions

View File

@ -104,12 +104,37 @@ class serializer
@param[in] indent_step the indent level @param[in] indent_step the indent level
@param[in] current_indent the current indent level (only used internally) @param[in] current_indent the current indent level (only used internally)
*/ */
void dump(const BasicJsonType& val, void dump(const BasicJsonType& start_val,
const bool pretty_print, const bool pretty_print,
const bool ensure_ascii, const bool ensure_ascii,
const unsigned int indent_step, const unsigned int indent_step,
const unsigned int current_indent = 0) const unsigned int start_indent = 0)
{ {
struct dump_state
{
dump_state(const BasicJsonType& v, unsigned int indent, unsigned int off = 0, decltype(start_val.m_data.m_value.object->cbegin()) it = {})
: val(v), new_indent(indent), offset(off), iterator(it)
{
}
const BasicJsonType& val;
const unsigned int new_indent;
const unsigned int offset;
const decltype(start_val.m_data.m_value.object->cbegin()) iterator;
};
std::vector<dump_state> stack;
stack.push_back(dump_state(start_val, start_indent));
while (!stack.empty())
{
const dump_state& state = stack.back();
const BasicJsonType& val = state.val;
const unsigned int current_indent = state.new_indent;
const unsigned int offset = state.offset;
auto iterator = state.iterator;
stack.pop_back();
switch (val.m_data.m_type) switch (val.m_data.m_type)
{ {
case value_t::object: case value_t::object:
@ -117,32 +142,43 @@ class serializer
if (val.m_data.m_value.object->empty()) if (val.m_data.m_value.object->empty())
{ {
o->write_characters("{}", 2); o->write_characters("{}", 2);
return; continue;
} }
if (pretty_print) if (pretty_print)
{
// variable to hold indentation for recursive calls
unsigned int new_indent = current_indent;
auto i = val.m_data.m_value.object->cbegin();
if (offset == 0)
{ {
o->write_characters("{\n", 2); o->write_characters("{\n", 2);
// variable to hold indentation for recursive calls new_indent = current_indent + indent_step;
const auto new_indent = current_indent + indent_step;
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
{ {
indent_string.resize(indent_string.size() * 2, ' '); indent_string.resize(indent_string.size() * 2, ' ');
} }
}
else if (offset < val.m_data.m_value.object->size())
{
o->write_characters(",\n", 2);
i = iterator;
}
// first n-1 elements // first n-1 elements
auto i = val.m_data.m_value.object->cbegin(); if (offset < val.m_data.m_value.object->size() - 1)
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
{ {
o->write_characters(indent_string.c_str(), new_indent); o->write_characters(indent_string.c_str(), new_indent);
o->write_character('\"'); o->write_character('\"');
dump_escaped(i->first, ensure_ascii); dump_escaped(i->first, ensure_ascii);
o->write_characters("\": ", 3); o->write_characters("\": ", 3);
dump(i->second, true, ensure_ascii, indent_step, new_indent); stack.push_back(dump_state(val, new_indent, offset + 1, std::next(i)));
o->write_characters(",\n", 2); stack.push_back(dump_state(i->second, new_indent, 0));
continue;
} }
else if (offset < val.m_data.m_value.object->size())
{
// last element // last element
JSON_ASSERT(i != val.m_data.m_value.object->cend()); JSON_ASSERT(i != val.m_data.m_value.object->cend());
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend()); JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
@ -150,39 +186,46 @@ class serializer
o->write_character('\"'); o->write_character('\"');
dump_escaped(i->first, ensure_ascii); dump_escaped(i->first, ensure_ascii);
o->write_characters("\": ", 3); o->write_characters("\": ", 3);
dump(i->second, true, ensure_ascii, indent_step, new_indent); stack.push_back(dump_state(val, new_indent, offset + 1, std::next(i)));
stack.push_back(dump_state(i->second, new_indent));
o->write_character('\n'); continue;
o->write_characters(indent_string.c_str(), current_indent); }
o->write_character('}'); else
{
o->write_character('\n');
o->write_characters(indent_string.c_str(), new_indent - indent_step);
o->write_character('}');
}
} }
else else
{ {
o->write_character('{');
// first n-1 elements
auto i = val.m_data.m_value.object->cbegin(); 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) if (offset == 0)
{
o->write_character('{');
}
else if (offset < val.m_data.m_value.object->size())
{
i = iterator;
o->write_character(',');
}
if (offset < val.m_data.m_value.object->size())
{ {
o->write_character('\"'); o->write_character('\"');
dump_escaped(i->first, ensure_ascii); dump_escaped(i->first, ensure_ascii);
o->write_characters("\":", 2); o->write_characters("\":", 2);
dump(i->second, false, ensure_ascii, indent_step, current_indent); stack.push_back(dump_state(val, current_indent, offset + 1, std::next(i)));
o->write_character(','); stack.push_back(dump_state( i->second, current_indent, 0));
continue;
} }
else
// last element {
JSON_ASSERT(i != val.m_data.m_value.object->cend());
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
o->write_character('\"');
dump_escaped(i->first, ensure_ascii);
o->write_characters("\":", 2);
dump(i->second, false, ensure_ascii, indent_step, current_indent);
o->write_character('}'); o->write_character('}');
} }
}
return; continue;
} }
case value_t::array: case value_t::array:
@ -190,58 +233,66 @@ class serializer
if (val.m_data.m_value.array->empty()) if (val.m_data.m_value.array->empty())
{ {
o->write_characters("[]", 2); o->write_characters("[]", 2);
return; continue;
} }
if (pretty_print) if (pretty_print)
{
// variable to hold indentation for recursive calls
unsigned int new_indent = current_indent;
if (offset == 0)
{ {
o->write_characters("[\n", 2); o->write_characters("[\n", 2);
// variable to hold indentation for recursive calls new_indent = current_indent + indent_step;
const auto new_indent = current_indent + indent_step;
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
{ {
indent_string.resize(indent_string.size() * 2, ' '); indent_string.resize(indent_string.size() * 2, ' ');
} }
}
// first n-1 elements else if (offset < val.m_data.m_value.array->size())
for (auto i = val.m_data.m_value.array->cbegin();
i != val.m_data.m_value.array->cend() - 1; ++i)
{ {
o->write_characters(indent_string.c_str(), new_indent);
dump(*i, true, ensure_ascii, indent_step, new_indent);
o->write_characters(",\n", 2); o->write_characters(",\n", 2);
} }
// last element if (offset < val.m_data.m_value.array->size())
JSON_ASSERT(!val.m_data.m_value.array->empty()); {
o->write_characters(indent_string.c_str(), new_indent); o->write_characters(indent_string.c_str(), new_indent);
dump(val.m_data.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); stack.push_back(dump_state(val, new_indent, offset + 1));
stack.push_back(dump_state( ( *val.m_data.m_value.array)[offset], new_indent));
o->write_character('\n'); continue;
o->write_characters(indent_string.c_str(), current_indent);
o->write_character(']');
} }
else else
{ {
o->write_character('['); o->write_character('\n');
o->write_characters(indent_string.c_str(), new_indent - indent_step);
// 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(*i, false, ensure_ascii, indent_step, current_indent);
o->write_character(',');
}
// last element
JSON_ASSERT(!val.m_data.m_value.array->empty());
dump(val.m_data.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
o->write_character(']'); o->write_character(']');
} }
}
else
{
if (offset == 0)
{
o->write_character('[');
}
else if (offset < val.m_data.m_value.array->size())
{
o->write_character(',');
}
if (offset < val.m_data.m_value.array->size())
{
stack.push_back(dump_state( val, current_indent, offset + 1));
return; stack.push_back(dump_state( (* val.m_data.m_value.array)[offset], current_indent));
continue;
}
else
{
o->write_character(']');
}
}
continue;
} }
case value_t::string: case value_t::string:
@ -249,7 +300,7 @@ class serializer
o->write_character('\"'); o->write_character('\"');
dump_escaped( * val.m_data.m_value.string, ensure_ascii); dump_escaped( * val.m_data.m_value.string, ensure_ascii);
o->write_character('\"'); o->write_character('\"');
return; continue;
} }
case value_t::binary: case value_t::binary:
@ -272,7 +323,8 @@ class serializer
if (!val.m_data.m_value.binary->empty()) if (!val.m_data.m_value.binary->empty())
{ {
for (auto i = val.m_data.m_value.binary->cbegin(); for (auto i = val.m_data.m_value.binary->cbegin();
i != val.m_data.m_value.binary->cend() - 1; ++i) i != val.m_data.m_value.binary->cend() - 1;
++i)
{ {
dump_integer( * i); dump_integer( * i);
o->write_characters(", ", 2); o->write_characters(", ", 2);
@ -303,7 +355,8 @@ class serializer
if (!val.m_data.m_value.binary->empty()) if (!val.m_data.m_value.binary->empty())
{ {
for (auto i = val.m_data.m_value.binary->cbegin(); for (auto i = val.m_data.m_value.binary->cbegin();
i != val.m_data.m_value.binary->cend() - 1; ++i) i != val.m_data.m_value.binary->cend() - 1;
++i)
{ {
dump_integer( * i); dump_integer( * i);
o->write_character(','); o->write_character(',');
@ -322,7 +375,7 @@ class serializer
o->write_characters("null}", 5); o->write_characters("null}", 5);
} }
} }
return; continue;
} }
case value_t::boolean: case value_t::boolean:
@ -335,43 +388,44 @@ class serializer
{ {
o->write_characters("false", 5); o->write_characters("false", 5);
} }
return; continue;
} }
case value_t::number_integer: case value_t::number_integer:
{ {
dump_integer(val.m_data.m_value.number_integer); dump_integer(val.m_data.m_value.number_integer);
return; continue;
} }
case value_t::number_unsigned: case value_t::number_unsigned:
{ {
dump_integer(val.m_data.m_value.number_unsigned); dump_integer(val.m_data.m_value.number_unsigned);
return; continue;
} }
case value_t::number_float: case value_t::number_float:
{ {
dump_float(val.m_data.m_value.number_float); dump_float(val.m_data.m_value.number_float);
return; continue;
} }
case value_t::discarded: case value_t::discarded:
{ {
o->write_characters("<discarded>", 11); o->write_characters("<discarded>", 11);
return; continue;
} }
case value_t::null: case value_t::null:
{ {
o->write_characters("null", 4); o->write_characters("null", 4);
return; continue;
} }
default: // LCOV_EXCL_LINE default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
} }
} }
}
JSON_PRIVATE_UNLESS_TESTED: JSON_PRIVATE_UNLESS_TESTED:
/*! /*!

View File

@ -18113,12 +18113,37 @@ class serializer
@param[in] indent_step the indent level @param[in] indent_step the indent level
@param[in] current_indent the current indent level (only used internally) @param[in] current_indent the current indent level (only used internally)
*/ */
void dump(const BasicJsonType& val, void dump(const BasicJsonType& start_val,
const bool pretty_print, const bool pretty_print,
const bool ensure_ascii, const bool ensure_ascii,
const unsigned int indent_step, const unsigned int indent_step,
const unsigned int current_indent = 0) const unsigned int start_indent = 0)
{ {
struct dump_state
{
dump_state(const BasicJsonType& v, unsigned int indent, unsigned int off = 0, decltype(start_val.m_data.m_value.object->cbegin()) it = {})
: val(v), new_indent(indent), offset(off), iterator(it)
{
}
const BasicJsonType& val;
const unsigned int new_indent;
const unsigned int offset;
const decltype(start_val.m_data.m_value.object->cbegin()) iterator;
};
std::vector<dump_state> stack;
stack.push_back(dump_state(start_val, start_indent));
while (!stack.empty())
{
const dump_state& state = stack.back();
const BasicJsonType& val = state.val;
const unsigned int current_indent = state.new_indent;
const unsigned int offset = state.offset;
auto iterator = state.iterator;
stack.pop_back();
switch (val.m_data.m_type) switch (val.m_data.m_type)
{ {
case value_t::object: case value_t::object:
@ -18126,32 +18151,43 @@ class serializer
if (val.m_data.m_value.object->empty()) if (val.m_data.m_value.object->empty())
{ {
o->write_characters("{}", 2); o->write_characters("{}", 2);
return; continue;
} }
if (pretty_print) if (pretty_print)
{
// variable to hold indentation for recursive calls
unsigned int new_indent = current_indent;
auto i = val.m_data.m_value.object->cbegin();
if (offset == 0)
{ {
o->write_characters("{\n", 2); o->write_characters("{\n", 2);
// variable to hold indentation for recursive calls new_indent = current_indent + indent_step;
const auto new_indent = current_indent + indent_step;
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
{ {
indent_string.resize(indent_string.size() * 2, ' '); indent_string.resize(indent_string.size() * 2, ' ');
} }
}
else if (offset < val.m_data.m_value.object->size())
{
o->write_characters(",\n", 2);
i = iterator;
}
// first n-1 elements // first n-1 elements
auto i = val.m_data.m_value.object->cbegin(); if (offset < val.m_data.m_value.object->size() - 1)
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
{ {
o->write_characters(indent_string.c_str(), new_indent); o->write_characters(indent_string.c_str(), new_indent);
o->write_character('\"'); o->write_character('\"');
dump_escaped(i->first, ensure_ascii); dump_escaped(i->first, ensure_ascii);
o->write_characters("\": ", 3); o->write_characters("\": ", 3);
dump(i->second, true, ensure_ascii, indent_step, new_indent); stack.push_back(dump_state(val, new_indent, offset + 1, std::next(i)));
o->write_characters(",\n", 2); stack.push_back(dump_state(i->second, new_indent, 0));
continue;
} }
else if (offset < val.m_data.m_value.object->size())
{
// last element // last element
JSON_ASSERT(i != val.m_data.m_value.object->cend()); JSON_ASSERT(i != val.m_data.m_value.object->cend());
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend()); JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
@ -18159,39 +18195,46 @@ class serializer
o->write_character('\"'); o->write_character('\"');
dump_escaped(i->first, ensure_ascii); dump_escaped(i->first, ensure_ascii);
o->write_characters("\": ", 3); o->write_characters("\": ", 3);
dump(i->second, true, ensure_ascii, indent_step, new_indent); stack.push_back(dump_state(val, new_indent, offset + 1, std::next(i)));
stack.push_back(dump_state(i->second, new_indent));
o->write_character('\n'); continue;
o->write_characters(indent_string.c_str(), current_indent); }
o->write_character('}'); else
{
o->write_character('\n');
o->write_characters(indent_string.c_str(), new_indent - indent_step);
o->write_character('}');
}
} }
else else
{ {
o->write_character('{');
// first n-1 elements
auto i = val.m_data.m_value.object->cbegin(); 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) if (offset == 0)
{
o->write_character('{');
}
else if (offset < val.m_data.m_value.object->size())
{
i = iterator;
o->write_character(',');
}
if (offset < val.m_data.m_value.object->size())
{ {
o->write_character('\"'); o->write_character('\"');
dump_escaped(i->first, ensure_ascii); dump_escaped(i->first, ensure_ascii);
o->write_characters("\":", 2); o->write_characters("\":", 2);
dump(i->second, false, ensure_ascii, indent_step, current_indent); stack.push_back(dump_state(val, current_indent, offset + 1, std::next(i)));
o->write_character(','); stack.push_back(dump_state( i->second, current_indent, 0));
continue;
} }
else
// last element {
JSON_ASSERT(i != val.m_data.m_value.object->cend());
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
o->write_character('\"');
dump_escaped(i->first, ensure_ascii);
o->write_characters("\":", 2);
dump(i->second, false, ensure_ascii, indent_step, current_indent);
o->write_character('}'); o->write_character('}');
} }
}
return; continue;
} }
case value_t::array: case value_t::array:
@ -18199,58 +18242,66 @@ class serializer
if (val.m_data.m_value.array->empty()) if (val.m_data.m_value.array->empty())
{ {
o->write_characters("[]", 2); o->write_characters("[]", 2);
return; continue;
} }
if (pretty_print) if (pretty_print)
{
// variable to hold indentation for recursive calls
unsigned int new_indent = current_indent;
if (offset == 0)
{ {
o->write_characters("[\n", 2); o->write_characters("[\n", 2);
// variable to hold indentation for recursive calls new_indent = current_indent + indent_step;
const auto new_indent = current_indent + indent_step;
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
{ {
indent_string.resize(indent_string.size() * 2, ' '); indent_string.resize(indent_string.size() * 2, ' ');
} }
}
// first n-1 elements else if (offset < val.m_data.m_value.array->size())
for (auto i = val.m_data.m_value.array->cbegin();
i != val.m_data.m_value.array->cend() - 1; ++i)
{ {
o->write_characters(indent_string.c_str(), new_indent);
dump(*i, true, ensure_ascii, indent_step, new_indent);
o->write_characters(",\n", 2); o->write_characters(",\n", 2);
} }
// last element if (offset < val.m_data.m_value.array->size())
JSON_ASSERT(!val.m_data.m_value.array->empty()); {
o->write_characters(indent_string.c_str(), new_indent); o->write_characters(indent_string.c_str(), new_indent);
dump(val.m_data.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); stack.push_back(dump_state(val, new_indent, offset + 1));
stack.push_back(dump_state( ( *val.m_data.m_value.array)[offset], new_indent));
o->write_character('\n'); continue;
o->write_characters(indent_string.c_str(), current_indent);
o->write_character(']');
} }
else else
{ {
o->write_character('['); o->write_character('\n');
o->write_characters(indent_string.c_str(), new_indent - indent_step);
// 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(*i, false, ensure_ascii, indent_step, current_indent);
o->write_character(',');
}
// last element
JSON_ASSERT(!val.m_data.m_value.array->empty());
dump(val.m_data.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
o->write_character(']'); o->write_character(']');
} }
}
else
{
if (offset == 0)
{
o->write_character('[');
}
else if (offset < val.m_data.m_value.array->size())
{
o->write_character(',');
}
if (offset < val.m_data.m_value.array->size())
{
stack.push_back(dump_state( val, current_indent, offset + 1));
return; stack.push_back(dump_state( (* val.m_data.m_value.array)[offset], current_indent));
continue;
}
else
{
o->write_character(']');
}
}
continue;
} }
case value_t::string: case value_t::string:
@ -18258,7 +18309,7 @@ class serializer
o->write_character('\"'); o->write_character('\"');
dump_escaped( * val.m_data.m_value.string, ensure_ascii); dump_escaped( * val.m_data.m_value.string, ensure_ascii);
o->write_character('\"'); o->write_character('\"');
return; continue;
} }
case value_t::binary: case value_t::binary:
@ -18281,7 +18332,8 @@ class serializer
if (!val.m_data.m_value.binary->empty()) if (!val.m_data.m_value.binary->empty())
{ {
for (auto i = val.m_data.m_value.binary->cbegin(); for (auto i = val.m_data.m_value.binary->cbegin();
i != val.m_data.m_value.binary->cend() - 1; ++i) i != val.m_data.m_value.binary->cend() - 1;
++i)
{ {
dump_integer( * i); dump_integer( * i);
o->write_characters(", ", 2); o->write_characters(", ", 2);
@ -18312,7 +18364,8 @@ class serializer
if (!val.m_data.m_value.binary->empty()) if (!val.m_data.m_value.binary->empty())
{ {
for (auto i = val.m_data.m_value.binary->cbegin(); for (auto i = val.m_data.m_value.binary->cbegin();
i != val.m_data.m_value.binary->cend() - 1; ++i) i != val.m_data.m_value.binary->cend() - 1;
++i)
{ {
dump_integer( * i); dump_integer( * i);
o->write_character(','); o->write_character(',');
@ -18331,7 +18384,7 @@ class serializer
o->write_characters("null}", 5); o->write_characters("null}", 5);
} }
} }
return; continue;
} }
case value_t::boolean: case value_t::boolean:
@ -18344,43 +18397,44 @@ class serializer
{ {
o->write_characters("false", 5); o->write_characters("false", 5);
} }
return; continue;
} }
case value_t::number_integer: case value_t::number_integer:
{ {
dump_integer(val.m_data.m_value.number_integer); dump_integer(val.m_data.m_value.number_integer);
return; continue;
} }
case value_t::number_unsigned: case value_t::number_unsigned:
{ {
dump_integer(val.m_data.m_value.number_unsigned); dump_integer(val.m_data.m_value.number_unsigned);
return; continue;
} }
case value_t::number_float: case value_t::number_float:
{ {
dump_float(val.m_data.m_value.number_float); dump_float(val.m_data.m_value.number_float);
return; continue;
} }
case value_t::discarded: case value_t::discarded:
{ {
o->write_characters("<discarded>", 11); o->write_characters("<discarded>", 11);
return; continue;
} }
case value_t::null: case value_t::null:
{ {
o->write_characters("null", 4); o->write_characters("null", 4);
return; continue;
} }
default: // LCOV_EXCL_LINE default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
} }
} }
}
JSON_PRIVATE_UNLESS_TESTED: JSON_PRIVATE_UNLESS_TESTED:
/*! /*!