mirror of
https://github.com/nlohmann/json.git
synced 2026-11-02 19:05:35 +00:00
Lexer, parser, stream adapter, SAX: 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: - lexer: `next_unget` in get_codepoint_bulk() and scan_literal_bulk(). unget() is only called at the end of scan_number(), in scan_comment() and in skip_bom(); every token scan starts with a plain get() in skip_whitespace() that consumes a pending unget, and nothing between that get() and the bulk helpers ungets (now asserts, as in get_after_string_run()) - lexer: `number_type == value_integer` in convert_integer(): both callers exclude value_float and the type has three values - parser: `!context.empty()` in exception_message(): all callers pass a literal - input_stream_adapter: `refill` in get_character_slow(): the function is only called after get_character() found the get area exhausted, and the variable recomputed that comparison - json_sax_dom_callback_parser: `ref_stack.back()` in key() (the null case returned a few lines earlier, and the callback in between cannot reach ref_stack), `is_structured()` in end_object() (non-null entries are only pushed for objects and arrays), `!key_stack.empty()` in current_key() (a kept object pushed its key before any value) No change in behaviour: a build that aborts on each removed side passes the test suite, the fuzz corpora and 40k random documents through every input adapter in four macro configurations. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
5 files changed
+42
-54
No files matched your search
@@ -545,12 +545,9 @@ class input_stream_adapter
|
||||
std::char_traits<char>::int_type get_character_slow()
|
||||
{
|
||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||
// sbumpc() refills the get area if it is exhausted
|
||||
const bool refill = streambuf_access::get_ptr(sb) == streambuf_access::get_end(sb);
|
||||
if (JSON_HEDLEY_UNLIKELY(refill))
|
||||
{
|
||||
save_token_segment();
|
||||
}
|
||||
// the get area is exhausted (get_character() handles the other case),
|
||||
// so sbumpc() refills it
|
||||
save_token_segment();
|
||||
#endif
|
||||
|
||||
auto res = sb->sbumpc();
|
||||
@@ -563,17 +560,13 @@ class input_stream_adapter
|
||||
is->clear(is->rdstate() | std::ios::eofbit);
|
||||
}
|
||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||
else if (JSON_HEDLEY_UNLIKELY(refill))
|
||||
else
|
||||
{
|
||||
// the consumed character is at gptr() - 1, unless the streambuf
|
||||
// has no get area and returned it from uflow()
|
||||
const char* const next = streambuf_access::get_ptr(sb);
|
||||
after_refill(res, next != nullptr && next != streambuf_access::get_begin(sb));
|
||||
}
|
||||
else
|
||||
{
|
||||
current_in_get_area = true; // LCOV_EXCL_LINE (called only when the get area is exhausted)
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1737,7 +1737,7 @@ class json_sax_dom_callback_parser : public json_sax_dom_error_state
|
||||
key_stack.push_back(val);
|
||||
|
||||
// add discarded value at the given key and store the reference for later
|
||||
if (keep && ref_stack.back())
|
||||
if (keep)
|
||||
{
|
||||
auto& obj = *ref_stack.back()->m_data.m_value.object;
|
||||
// a single lookup: insert the placeholder, or find the existing member
|
||||
@@ -1794,7 +1794,7 @@ class json_sax_dom_callback_parser : public json_sax_dom_error_state
|
||||
|
||||
const string_t object_key = pop_container();
|
||||
|
||||
if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
|
||||
if (!ref_stack.empty() && ref_stack.back())
|
||||
{
|
||||
// remove discarded value
|
||||
remove_discarded_value(*ref_stack.back(), object_key);
|
||||
@@ -1960,8 +1960,7 @@ class json_sax_dom_callback_parser : public json_sax_dom_error_state
|
||||
*/
|
||||
string_t current_key() const
|
||||
{
|
||||
if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_object()
|
||||
&& !key_stack.empty())
|
||||
if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_object())
|
||||
{
|
||||
return key_stack.back();
|
||||
}
|
||||
|
||||
@@ -300,14 +300,16 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
/// calls would (including the position and token bookkeeping of a
|
||||
/// streaming adapter, see bulk_consume) and leaves @a current holding the last of
|
||||
/// the 4 digits, just as the last such get() would; the codepoint is
|
||||
/// written to @a out. Makes no state change and returns false for a
|
||||
/// pending unget, fewer than 4 remaining bytes, or any of the 4 bytes not
|
||||
/// being a hex digit, so the caller falls back unchanged to the
|
||||
/// per-character loop, which then reports the same diagnostic (stopping
|
||||
/// at the first invalid digit) as before this optimization.
|
||||
/// written to @a out. Makes no state change and returns false for fewer
|
||||
/// than 4 remaining bytes or any of the 4 bytes not being a hex digit, so
|
||||
/// the caller falls back unchanged to the per-character loop, which then
|
||||
/// reports the same diagnostic (stopping at the first invalid digit) as
|
||||
/// before this optimization.
|
||||
bool get_codepoint_bulk(std::true_type /*bulk*/, int& out)
|
||||
{
|
||||
if (next_unget || ia.bulk_remaining() < 4)
|
||||
// no unget is pending inside a string (see get_after_string_run())
|
||||
JSON_ASSERT(!next_unget);
|
||||
if (ia.bulk_remaining() < 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1409,7 +1411,7 @@ scan_number_done:
|
||||
return token_type::value_unsigned;
|
||||
}
|
||||
}
|
||||
else if (number_type == token_type::value_integer)
|
||||
else // value_integer (the callers handle value_float themselves)
|
||||
{
|
||||
if (parse_integer_signed(first, last, value_integer))
|
||||
{
|
||||
@@ -1780,7 +1782,10 @@ scan_number_done:
|
||||
/// the error at the right character
|
||||
JSON_HEDLEY_ALWAYS_INLINE bool scan_literal_bulk(const char* rest, const std::size_t length, std::true_type /*bulk*/)
|
||||
{
|
||||
if (next_unget || ia.bulk_remaining() < length
|
||||
// called right after scan_after_whitespace() read current (see
|
||||
// get_after_string_run()), so no unget is pending
|
||||
JSON_ASSERT(!next_unget);
|
||||
if (ia.bulk_remaining() < length
|
||||
|| std::memcmp(ia.bulk_data(), rest, length) != 0)
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -575,10 +575,7 @@ class parser
|
||||
{
|
||||
std::string error_msg = "syntax error ";
|
||||
|
||||
if (!context.empty())
|
||||
{
|
||||
error_msg += concat("while parsing ", context, ' ');
|
||||
}
|
||||
error_msg += concat("while parsing ", context, ' ');
|
||||
|
||||
error_msg += "- ";
|
||||
|
||||
|
||||
Reference in new issue
Block a user