Address the review of json_view dump() and comparisons

From gregmarr's review of #5624:

- The output buffer takes string literals without a length.
- write_float() names the IEEE single/double condition once, as the
  library's serializer does.
- equal() checks the types first and compares with !=; the reference to
  the top frame is noted as dangling after pop_back().
- dump.md: one paragraph per case in the notes.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-18 10:06:04 +02:00
1 parent be59fb2a57
commit 7b66cdb9d8
3 files changed
+38 -24

No files matched your search

+4 -2
View File
@@ -60,11 +60,13 @@ Linear in the size of the output text.
The walk over the subtree is iterative, so the nesting depth it can write is limited by available memory only, not by
the call stack -- as for [`materialize()`](materialize.md).
Strings are escaped by the same rules as [`BasicJsonType::dump`](../basic_json/dump.md). With
`#!cpp numbers == number_format::shortest`, floats are written with the library's shortest round-trip conversion,
Strings are escaped by the same rules as [`BasicJsonType::dump`](../basic_json/dump.md).
With `#!cpp numbers == number_format::shortest`, floats are written with the library's shortest round-trip conversion,
exactly as [`BasicJsonType::dump`](../basic_json/dump.md) would (e.g. `#!cpp 1.5`, `#!cpp 100.0`, `#!cpp 1e+100`), and
integers are copied from the source text -- already canonical in JSON, so this matches their shortest form too --
except that `#!cpp -0` is written as `#!cpp 0`, the way [`BasicJsonType::parse()`](../basic_json/parse.md) reads it.
`#!cpp number_format::source` copies every number exactly as written in the source text instead, with no exception
for `#!cpp -0` -- `#!cpp 1.50`, `#!cpp 1E2`, `#!cpp -0.0`, `#!cpp -0`, or all digits of an integer literal with more
digits than any number type holds (such a literal is itself classified as a float, see
+9 -6
View File
@@ -201,6 +201,11 @@ class json_side
const BasicJsonType* m_json;
};
inline bool is_number(value_t t) noexcept
{
return t == value_t::number_integer || t == value_t::number_unsigned || t == value_t::number_float;
}
/// whether two sides are equal; iterative, so that the nesting depth is
/// limited by memory only
template<typename BasicJsonType, typename A, typename B>
@@ -225,8 +230,6 @@ bool equal(const A& a0, const B& b0)
{
const value_t ta = a.type();
const value_t tb = b.type();
const bool numbers = (ta == value_t::number_integer || ta == value_t::number_unsigned || ta == value_t::number_float)
&& (tb == value_t::number_integer || tb == value_t::number_unsigned || tb == value_t::number_float);
if (ta == value_t::discarded || tb == value_t::discarded)
{
// basic_json decides (JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
@@ -237,13 +240,13 @@ bool equal(const A& a0, const B& b0)
}
else
{
if (!numbers && ta != tb)
if (ta != tb && !(is_number(ta) && is_number(tb))) // numbers of different types can be equal
{
return false;
}
if (ta == value_t::string)
{
if (!(a.string() == b.string()))
if (a.string() != b.string())
{
return false;
}
@@ -272,7 +275,7 @@ bool equal(const A& a0, const B& b0)
}
stack.push_back(std::move(f));
}
else if (!(a.scalar() == b.scalar())) // numbers (also of different types), null, boolean
else if (a.scalar() != b.scalar()) // numbers (also of different types), null, boolean
{
return false;
}
@@ -289,7 +292,7 @@ bool equal(const A& a0, const B& b0)
const std::size_t count = f.object ? f.members_a.size() : f.elements_a.size();
if (f.next == count)
{
stack.pop_back();
stack.pop_back(); // (f dangles now, and is bound again after continue)
continue;
}
if (f.object)
+25 -16
View File
@@ -69,6 +69,13 @@ class output_buffer
m_pos += n;
}
/// a string literal, without its terminating NUL
template<std::size_t N>
NLOHMANN_VIEW_ALWAYS_INLINE void put(const char (&s)[N])
{
put(s, N - 1);
}
void put_repeated(char c, std::size_t n)
{
reserve(n);
@@ -170,7 +177,7 @@ class view_serializer
frame& f = stack.back();
if (f.pos == f.end)
{
const bool object = f.object;
const bool object = f.object; // (f dangles after pop_back)
stack.pop_back();
newline(stack.size());
m_out.put(object ? '}' : ']');
@@ -187,7 +194,7 @@ class view_serializer
write_string(*f.pos);
if (m_style.pretty)
{
m_out.put(": ", 2);
m_out.put(": ");
}
else
{
@@ -220,16 +227,16 @@ class view_serializer
switch (static_cast<value_t>(n.kind))
{
case value_t::null:
m_out.put("null", 4);
m_out.put("null");
break;
case value_t::boolean:
if ((n.flags & node_flags::is_true) != 0)
{
m_out.put("true", 4);
m_out.put("true");
}
else
{
m_out.put("false", 5);
m_out.put("false");
}
break;
case value_t::string:
@@ -274,12 +281,14 @@ class view_serializer
{
if (!std::isfinite(x))
{
m_out.put("null", 4);
m_out.put("null");
return;
}
write_float(x, std::integral_constant < bool,
(std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128)
|| (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024) > {});
static constexpr bool is_ieee_single_or_double
= std::numeric_limits<number_float_t>::is_iec559
&& ((std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128)
|| (std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024));
write_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
}
void write_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
@@ -364,25 +373,25 @@ class view_serializer
switch (codepoint)
{
case 0x08:
m_out.put("\\b", 2);
m_out.put("\\b");
return;
case 0x09:
m_out.put("\\t", 2);
m_out.put("\\t");
return;
case 0x0A:
m_out.put("\\n", 2);
m_out.put("\\n");
return;
case 0x0C:
m_out.put("\\f", 2);
m_out.put("\\f");
return;
case 0x0D:
m_out.put("\\r", 2);
m_out.put("\\r");
return;
case 0x22:
m_out.put("\\\"", 2);
m_out.put("\\\"");
return;
case 0x5C:
m_out.put("\\\\", 2);
m_out.put("\\\\");
return;
default:
break;