basic_json, iterators, serializer: remove checks that invariants make dead
Branch coverage of the test suite and of 24k mutated inputs showed these conditions as never taken; each is implied by the code around it: - copy_object_level(): `src_it != src_object.cend()`; dst was built from exactly src_object.size() unique keys and src_it advances at most once per member - the general comparison operators (equal_general, less_general, three_way_general): the five same-type scalar cases, which the inline fast path of every operator handles before calling them (now an assert); and the NaN half of compares_unordered() in the mixed-type branch, reached only after all six number pairs returned, where only a discarded operand is unordered - serializer, ensure_ascii: the reset of `bytes` under error_handler_t::keep and the surrogate-pair escape in the UTF8_ACCEPT case; every well-formed multi-byte character is escaped by write_u_escapes() before decode() sees it, so decode() only ever accepts DEL here, a single byte of which nothing was buffered (now asserts) - iter_impl(pointer): the if/else chain assigned value-initialized iterators to the value-initialized m_it No change in behaviour: a build that aborts on each removed side passes the test suite, the fuzz corpora and 23.2M dumps of all 1-, 2- and boundary 4-byte strings in both ensure_ascii modes. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
4 files changed
+36
-60
No files matched your search
@@ -107,20 +107,8 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
explicit iter_impl(pointer object) noexcept : m_object(object)
|
explicit iter_impl(pointer object) noexcept : m_object(object)
|
||||||
{
|
{
|
||||||
JSON_ASSERT(m_object != nullptr);
|
JSON_ASSERT(m_object != nullptr);
|
||||||
|
// m_it is value-initialized: its iterators are singular, and the
|
||||||
if (m_object->is_object())
|
// primitive iterator is in its "uninitialized" state
|
||||||
{
|
|
||||||
m_it.object_iterator = typename object_t::iterator();
|
|
||||||
}
|
|
||||||
else if (m_object->is_array())
|
|
||||||
{
|
|
||||||
m_it.array_iterator = typename array_t::iterator();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// primitive (is_primitive(), including discarded)
|
|
||||||
m_it.primitive_iterator = primitive_iterator_t();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -1113,21 +1113,14 @@ class serializer
|
|||||||
{
|
{
|
||||||
// the code point is at least 0x60 (smaller ones are handled
|
// the code point is at least 0x60 (smaller ones are handled
|
||||||
// above) and is not printable ASCII (which the scanner
|
// above) and is not printable ASCII (which the scanner
|
||||||
// copies): it is DEL (0x7F) under EnsureAscii or a non-ASCII
|
// copies). Under EnsureAscii it is DEL (0x7F): every
|
||||||
// character
|
// well-formed multi-byte character is escaped by
|
||||||
|
// write_u_escapes() above, so decode() only ever accepts a
|
||||||
|
// single byte here, and nothing of it was buffered
|
||||||
if (EnsureAscii)
|
if (EnsureAscii)
|
||||||
{
|
{
|
||||||
JSON_ASSERT(codepoint == 0x7F);
|
JSON_ASSERT(codepoint == 0x7F);
|
||||||
if (error_handler == error_handler_t::keep)
|
JSON_ASSERT(bytes == bytes_after_last_accept);
|
||||||
{
|
|
||||||
// this character was buffered as raw bytes below
|
|
||||||
// in case it turned out to be part of an
|
|
||||||
// ill-formed sequence (which is kept as is); now
|
|
||||||
// that it decoded to a well-formed code point,
|
|
||||||
// undo that and \u-escape it like any other
|
|
||||||
// character instead
|
|
||||||
bytes = bytes_after_last_accept;
|
|
||||||
}
|
|
||||||
write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
|
write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1576,7 +1576,7 @@ public:
|
|||||||
auto src_it = src_object.cbegin();
|
auto src_it = src_object.cbegin();
|
||||||
for (auto& element : *dst.m_data.m_value.object)
|
for (auto& element : *dst.m_data.m_value.object)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_LIKELY(src_it != src_object.cend() && copy_keys_equal(src_it->first, element.first)))
|
if (JSON_HEDLEY_LIKELY(copy_keys_equal(src_it->first, element.first)))
|
||||||
{
|
{
|
||||||
copy_shallow(src_it->second, element.second, worklist);
|
copy_shallow(src_it->second, element.second, worklist);
|
||||||
++src_it;
|
++src_it;
|
||||||
@@ -5404,8 +5404,15 @@ public:
|
|||||||
return (*lhs.m_data.m_value.object) op (*rhs.m_data.m_value.object); \
|
return (*lhs.m_data.m_value.object) op (*rhs.m_data.m_value.object); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
JSON_IMPLEMENT_SCALAR_CASES(op, null_result) \
|
/* same-type scalars never get here: the inline fast path of the operator handles them */ \
|
||||||
\
|
case value_t::null: \
|
||||||
|
case value_t::boolean: \
|
||||||
|
case value_t::number_integer: \
|
||||||
|
case value_t::number_unsigned: \
|
||||||
|
case value_t::number_float: \
|
||||||
|
JSON_ASSERT(false); /* NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) */ \
|
||||||
|
return (unordered_result); \
|
||||||
|
\
|
||||||
case value_t::string: \
|
case value_t::string: \
|
||||||
return (*lhs.m_data.m_value.string) op (*rhs.m_data.m_value.string); \
|
return (*lhs.m_data.m_value.string) op (*rhs.m_data.m_value.string); \
|
||||||
\
|
\
|
||||||
@@ -5445,7 +5452,7 @@ public:
|
|||||||
? (number_integer_t(-1) op number_integer_t(1)) \
|
? (number_integer_t(-1) op number_integer_t(1)) \
|
||||||
: (static_cast<number_unsigned_t>(lhs.m_data.m_value.number_integer) op rhs.m_data.m_value.number_unsigned); \
|
: (static_cast<number_unsigned_t>(lhs.m_data.m_value.number_integer) op rhs.m_data.m_value.number_unsigned); \
|
||||||
} \
|
} \
|
||||||
else if(compares_unordered(lhs, rhs))\
|
else if(lhs_type == value_t::discarded || rhs_type == value_t::discarded)\
|
||||||
{\
|
{\
|
||||||
return (unordered_result);\
|
return (unordered_result);\
|
||||||
}\
|
}\
|
||||||
|
|||||||
Generated
+18
-30
@@ -24063,20 +24063,8 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
explicit iter_impl(pointer object) noexcept : m_object(object)
|
explicit iter_impl(pointer object) noexcept : m_object(object)
|
||||||
{
|
{
|
||||||
JSON_ASSERT(m_object != nullptr);
|
JSON_ASSERT(m_object != nullptr);
|
||||||
|
// m_it is value-initialized: its iterators are singular, and the
|
||||||
if (m_object->is_object())
|
// primitive iterator is in its "uninitialized" state
|
||||||
{
|
|
||||||
m_it.object_iterator = typename object_t::iterator();
|
|
||||||
}
|
|
||||||
else if (m_object->is_array())
|
|
||||||
{
|
|
||||||
m_it.array_iterator = typename array_t::iterator();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// primitive (is_primitive(), including discarded)
|
|
||||||
m_it.primitive_iterator = primitive_iterator_t();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -36267,21 +36255,14 @@ class serializer
|
|||||||
{
|
{
|
||||||
// the code point is at least 0x60 (smaller ones are handled
|
// the code point is at least 0x60 (smaller ones are handled
|
||||||
// above) and is not printable ASCII (which the scanner
|
// above) and is not printable ASCII (which the scanner
|
||||||
// copies): it is DEL (0x7F) under EnsureAscii or a non-ASCII
|
// copies). Under EnsureAscii it is DEL (0x7F): every
|
||||||
// character
|
// well-formed multi-byte character is escaped by
|
||||||
|
// write_u_escapes() above, so decode() only ever accepts a
|
||||||
|
// single byte here, and nothing of it was buffered
|
||||||
if (EnsureAscii)
|
if (EnsureAscii)
|
||||||
{
|
{
|
||||||
JSON_ASSERT(codepoint == 0x7F);
|
JSON_ASSERT(codepoint == 0x7F);
|
||||||
if (error_handler == error_handler_t::keep)
|
JSON_ASSERT(bytes == bytes_after_last_accept);
|
||||||
{
|
|
||||||
// this character was buffered as raw bytes below
|
|
||||||
// in case it turned out to be part of an
|
|
||||||
// ill-formed sequence (which is kept as is); now
|
|
||||||
// that it decoded to a well-formed code point,
|
|
||||||
// undo that and \u-escape it like any other
|
|
||||||
// character instead
|
|
||||||
bytes = bytes_after_last_accept;
|
|
||||||
}
|
|
||||||
write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
|
write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -39305,7 +39286,7 @@ public:
|
|||||||
auto src_it = src_object.cbegin();
|
auto src_it = src_object.cbegin();
|
||||||
for (auto& element : *dst.m_data.m_value.object)
|
for (auto& element : *dst.m_data.m_value.object)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_LIKELY(src_it != src_object.cend() && copy_keys_equal(src_it->first, element.first)))
|
if (JSON_HEDLEY_LIKELY(copy_keys_equal(src_it->first, element.first)))
|
||||||
{
|
{
|
||||||
copy_shallow(src_it->second, element.second, worklist);
|
copy_shallow(src_it->second, element.second, worklist);
|
||||||
++src_it;
|
++src_it;
|
||||||
@@ -43133,8 +43114,15 @@ public:
|
|||||||
return (*lhs.m_data.m_value.object) op (*rhs.m_data.m_value.object); \
|
return (*lhs.m_data.m_value.object) op (*rhs.m_data.m_value.object); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
JSON_IMPLEMENT_SCALAR_CASES(op, null_result) \
|
/* same-type scalars never get here: the inline fast path of the operator handles them */ \
|
||||||
\
|
case value_t::null: \
|
||||||
|
case value_t::boolean: \
|
||||||
|
case value_t::number_integer: \
|
||||||
|
case value_t::number_unsigned: \
|
||||||
|
case value_t::number_float: \
|
||||||
|
JSON_ASSERT(false); /* NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) */ \
|
||||||
|
return (unordered_result); \
|
||||||
|
\
|
||||||
case value_t::string: \
|
case value_t::string: \
|
||||||
return (*lhs.m_data.m_value.string) op (*rhs.m_data.m_value.string); \
|
return (*lhs.m_data.m_value.string) op (*rhs.m_data.m_value.string); \
|
||||||
\
|
\
|
||||||
@@ -43174,7 +43162,7 @@ public:
|
|||||||
? (number_integer_t(-1) op number_integer_t(1)) \
|
? (number_integer_t(-1) op number_integer_t(1)) \
|
||||||
: (static_cast<number_unsigned_t>(lhs.m_data.m_value.number_integer) op rhs.m_data.m_value.number_unsigned); \
|
: (static_cast<number_unsigned_t>(lhs.m_data.m_value.number_integer) op rhs.m_data.m_value.number_unsigned); \
|
||||||
} \
|
} \
|
||||||
else if(compares_unordered(lhs, rhs))\
|
else if(lhs_type == value_t::discarded || rhs_type == value_t::discarded)\
|
||||||
{\
|
{\
|
||||||
return (unordered_result);\
|
return (unordered_result);\
|
||||||
}\
|
}\
|
||||||
|
|||||||
Reference in new issue
Block a user