From d7b9472173624f58365e42d50a608d5d4d5f7e8d Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 1 Nov 2026 22:32:41 +0100 Subject: [PATCH] Remove unreachable code found by the coverage analysis Remove code that no input can reach, and mark the lines that cannot be covered with LCOV_EXCL_LINE: - BJData writer: the dry_run parameter of the ND-array element writers was always true; replace them by range checks. - BON8 writer: call throw_integer_out_of_range() directly instead of write_bon8_value() for values that always throw. - binary_writer: integer_bits() is only called for integers of up to 64 bits. - serializer, view serializer, lexer: replace guards that the callers make unreachable by assertions or by the simpler code (surrogate pairs under ensure_ascii, next_unget, '-' at the end of the buffer). - json_view: drop unreachable branches in scalar(), set(), check_utf8_scan(), append_input_impl() and unescape(). - Mark exception-cleanup braces, 2 GiB limits, big-endian and 32-bit code, and fall-through case labels with LCOV_EXCL_LINE. No behavior changes for any input that reaches these functions. Signed-off-by: Niels Lohmann --- .../nlohmann/detail/conversions/from_json.hpp | 2 +- .../nlohmann/detail/input/input_adapters.hpp | 2 +- include/nlohmann/detail/input/json_sax.hpp | 2 +- include/nlohmann/detail/input/lexer.hpp | 23 +- include/nlohmann/detail/input/parser.hpp | 2 +- include/nlohmann/detail/json_pointer.hpp | 8 +- .../nlohmann/detail/output/binary_writer.hpp | 152 ++++------- include/nlohmann/detail/output/serializer.hpp | 14 +- include/nlohmann/detail/string_concat.hpp | 2 +- include/nlohmann/detail/string_escape.hpp | 4 - include/nlohmann/detail/string_utils.hpp | 6 +- include/nlohmann/detail/view/compare.hpp | 27 +- include/nlohmann/detail/view/edit.hpp | 4 +- include/nlohmann/detail/view/edit_storage.hpp | 2 +- include/nlohmann/detail/view/errors.hpp | 2 + include/nlohmann/detail/view/materialize.hpp | 11 +- include/nlohmann/detail/view/serializer.hpp | 24 +- include/nlohmann/json.hpp | 26 +- include/nlohmann/json_view.hpp | 6 +- single_include/nlohmann/json.hpp | 243 +++++++----------- single_include/nlohmann/json_view.hpp | 76 ++---- 21 files changed, 234 insertions(+), 404 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 8fb9d149b..09f0713ea 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -91,7 +91,7 @@ const typename BasicJsonType::binary_t& binary_value(const BasicJsonType& j) } template::value, int> = 0> -typename View::basic_json_type::binary_t binary_value(const View& /*unused*/) +typename View::basic_json_type::binary_t binary_value(const View& /*unused*/) // LCOV_EXCL_LINE { return {}; // LCOV_EXCL_LINE } diff --git a/include/nlohmann/detail/input/input_adapters.hpp b/include/nlohmann/detail/input/input_adapters.hpp index af1a6d537..e9cdb7e09 100644 --- a/include/nlohmann/detail/input/input_adapters.hpp +++ b/include/nlohmann/detail/input/input_adapters.hpp @@ -572,7 +572,7 @@ class input_stream_adapter } else { - current_in_get_area = true; + current_in_get_area = true; // LCOV_EXCL_LINE (called only when the get area is exhausted) } #endif return res; diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 0ceec61b7..7c6a947a7 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -999,7 +999,7 @@ class json_sax_dom_parser : public json_sax_dom_error_state ordered_data.members[f.ordered.dest_member].second = std::move(value); } - static void place_member(const frame& /*f*/, BasicJsonType&& /*value*/, std::false_type /*indexed*/) noexcept {} + static void place_member(const frame& /*f*/, BasicJsonType&& /*value*/, std::false_type /*indexed*/) noexcept {} // LCOV_EXCL_LINE /// store the members of a new object directly in it bool start_stored_object(std::size_t len) diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 66cb14b4f..7811c9951 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -437,11 +437,10 @@ class lexer : public lexer_base template char_int_type get_after_string_run(std::true_type /*bulk*/, std::integral_constant keep) { - // a pending unget must be consumed through the normal path first - if (next_unget) - { - return get(); - } + // scan_string() is entered from scan_after_whitespace(), whose current + // was read by skip_whitespace() (its first get() clears a pending + // unget), and nothing in scan_string() calls unget() + JSON_ASSERT(!next_unget); const std::size_t remaining = ia.bulk_remaining(); if (remaining == 0) { @@ -1516,11 +1515,9 @@ scan_number_done: */ token_type scan_number_bulk_contiguous() { - // a pending unget offsets the buffer position from current; fall back - if (next_unget) - { - return token_type::uninitialized; - } + // called right after scan_after_whitespace() read current (see + // get_after_string_run()), so no unget is pending + JSON_ASSERT(!next_unget); const std::size_t rem = ia.bulk_remaining(); if (rem == 0) { @@ -1541,11 +1538,7 @@ scan_number_done: if (data[0] == '-') { number_type = token_type::value_integer; - i = 1; - if (i >= avail) - { - return token_type::uninitialized; - } + i = 1; // avail >= 2: rem == 0 returned above } const std::size_t integer_begin = i; if (data[i] == '0') diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp index 023327166..5e0995b05 100644 --- a/include/nlohmann/detail/input/parser.hpp +++ b/include/nlohmann/detail/input/parser.hpp @@ -598,7 +598,7 @@ class parser } return error_msg; - } + } // LCOV_EXCL_LINE (exception cleanup only) private: /// callback function diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 6df49fbc0..ec30e17c5 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -304,7 +304,7 @@ class json_pointer result.reference_tokens.insert(result.reference_tokens.end(), lhs.reference_tokens.begin(), lhs.reference_tokens.end()); result.reference_tokens.insert(result.reference_tokens.end(), rhs.reference_tokens.begin(), rhs.reference_tokens.end()); return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ @@ -315,7 +315,7 @@ class json_pointer result.reference_tokens.insert(result.reference_tokens.end(), lhs.reference_tokens.begin(), lhs.reference_tokens.end()); result.reference_tokens.push_back(std::move(token)); return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ @@ -2180,7 +2180,7 @@ class json_pointer { // create an entry in the array current = ¤t->operator[](array_index(token)); - break; + break; // LCOV_EXCL_LINE (needs an array index with 10 or more digits: more than 1e9 elements) } /* @@ -2217,7 +2217,7 @@ class json_pointer json_pointer result; result.reference_tokens = reference_tokens; return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) json_pointer convert()&& { diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 0c3c823ac..377a3f700 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -833,7 +833,7 @@ class binary_writer while (n != 0); std::reverse(result.begin(), result.end()); return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) /*! @brief the decimal digits of the integer @a n, with a leading '-' if it @@ -860,7 +860,7 @@ class binary_writer } std::reverse(result.begin(), result.end()); return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) /*! @brief throws because the integer @a j is too large for @a format @@ -1792,7 +1792,7 @@ class binary_writer std::memcpy(out, &n, sizeof(NumberType)); if (!is_little_endian) { - std::reverse(out, out + sizeof(NumberType)); + std::reverse(out, out + sizeof(NumberType)); // LCOV_EXCL_LINE (big-endian hosts only) } } @@ -1921,7 +1921,7 @@ class binary_writer const string_t& value = *j.m_data.m_value.string; if (JSON_HEDLEY_UNLIKELY(!value_in_range_of(value.size() + 1ul))) { - return false; + return false; // LCOV_EXCL_LINE (a string of 2 GiB or more) } out[0] = to_char_type(0x02); store_little_endian(p, static_cast(value.size() + 1ul)); @@ -2378,7 +2378,7 @@ class binary_writer const std::size_t size = oa.written() - pos; if (JSON_HEDLEY_UNLIKELY(!value_in_range_of(size))) { - return false; + return false; // LCOV_EXCL_LINE (a document of 2 GiB or more) } const auto le_size = static_cast(size); oa.overwrite(pos, is_little_endian ? le_size : byteswap(le_size)); @@ -2507,7 +2507,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!close_bson_container(nested_size_pos))) { - return false; + return false; // LCOV_EXCL_LINE (a document of 2 GiB or more) } continue; } @@ -2522,7 +2522,7 @@ class binary_writer if (JSON_HEDLEY_UNLIKELY(!close_bson_container(current.size_slot))) { - return false; + return false; // LCOV_EXCL_LINE (a document of 2 GiB or more) } if (parents.empty()) { @@ -2779,10 +2779,10 @@ class binary_writer Selected through masks rather than branches, as the type and the sign of the values in an array of mixed signs alternate unpredictably (non-negative - integers are number_unsigned after parsing). Up to 64 bits, the sign is bit - 63 of the result (a narrower integer is sign-extended); computing it with - masks keeps GCC from branching on the type. A wider integer has to be - compared. + integers are number_unsigned after parsing). The sign is bit 63 of the + result (a narrower integer is sign-extended); computing it with masks keeps + GCC from branching on the type. Only called if @ref integers_fit_64_bits(); + a wider number type is written with range checks on the whole value. */ static std::uint64_t integer_bits(const BasicJsonType& j, std::uint64_t& sign_mask) noexcept { @@ -2790,9 +2790,7 @@ class binary_writer const std::uint64_t raw = is_signed ? static_cast(j.m_data.m_value.number_integer) : static_cast(j.m_data.m_value.number_unsigned); - sign_mask = sizeof(typename BasicJsonType::number_integer_t) <= sizeof(std::uint64_t) - ? (std::uint64_t{0} - static_cast(is_signed)) & (std::uint64_t{0} - (raw >> 63u)) - : std::uint64_t{0} - static_cast(is_signed && j.m_data.m_value.number_integer < 0); + sign_mask = (std::uint64_t{0} - static_cast(is_signed)) & (std::uint64_t{0} - (raw >> 63u)); return raw; } @@ -4159,57 +4157,34 @@ class binary_writer } /*! - @brief validate (dry_run) or write one BJData ND-array element of integer dtype @a T - @return whether @a el's value is in range of @a T; always true when @a dry_run is false - */ - template - bool write_bjdata_ndarray_element(const BasicJsonType& el, const bool dry_run) - { - if (dry_run) - { - return bjdata_ndarray_value_in_range(el); - } - using storage_type = typename std::conditional::value, std::uint64_t, std::int64_t>::type; - write_number(static_cast(el.template get()), true); - return true; - } - - /*! - @brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision) + @brief whether the BJData ND-array element @a el can be written with dtype 'd' (single precision) @return whether @a el's value survives narrowing to float and back without any change - (so the ND-array round-trips exactly), or is infinite or NaN; always true when - @a dry_run is false + (so the ND-array round-trips exactly), or is infinite or NaN */ - bool write_bjdata_ndarray_float_element(const BasicJsonType& el, const bool dry_run) + static bool bjdata_ndarray_float_in_range(const BasicJsonType& el) noexcept { const auto dval = static_cast(el.m_data.m_value.number_float); - if (dry_run) - { #ifdef __GNUC__ - JSON_HEDLEY_DIAGNOSTIC_PUSH - JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal") + JSON_HEDLEY_DIAGNOSTIC_PUSH + JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal") #endif - // a value that would be rounded (rather than exactly represented) by the - // narrowing to float is treated like an out-of-range integer element; this - // is the same criterion write_compact_float() uses for CBOR/MessagePack - const bool in_range = std::isnan(dval) || - (dval >= static_cast(std::numeric_limits::lowest()) && - dval <= static_cast((std::numeric_limits::max)()) && - static_cast(static_cast(dval)) == dval) || - std::isinf(dval); + // a value that would be rounded (rather than exactly represented) by the + // narrowing to float is treated like an out-of-range integer element; this + // is the same criterion write_compact_float() uses for CBOR/MessagePack + return std::isnan(dval) || + (dval >= static_cast(std::numeric_limits::lowest()) && + dval <= static_cast((std::numeric_limits::max)()) && + static_cast(static_cast(dval)) == dval) || + std::isinf(dval); #ifdef __GNUC__ - JSON_HEDLEY_DIAGNOSTIC_POP + JSON_HEDLEY_DIAGNOSTIC_POP #endif - return in_range; - } - write_number(static_cast(dval), true); - return true; } /*! @brief write the elements of an ND-array as the number kind @a dtype names (see @ref write_bjdata_ndarray), after they have been found to be - in range by a dry run of @ref write_bjdata_ndarray_elements + in range by @ref bjdata_ndarray_elements_in_range The elements are encoded in blocks into space reserved from the sink. */ @@ -4291,20 +4266,13 @@ class binary_writer } /*! - @brief validate or write every element of a BJData ND-array's `_ArrayData_` + @brief whether every element of a BJData ND-array's `_ArrayData_` is in range for its dtype @param[in] array_data the `_ArrayData_` array @param[in] dtype the ND-array dtype marker, as returned by bjdata_ndarray_type_marker() - @param[in] dry_run true to only range-check each element, false to write it - @return whether every element is in range for @a dtype (always true when @a dry_run is false) + @return whether every element is in range for @a dtype */ - bool write_bjdata_ndarray_elements(const BasicJsonType& array_data, const CharType dtype, const bool dry_run) + static bool bjdata_ndarray_elements_in_range(const BasicJsonType& array_data, const CharType dtype) { - if (!dry_run) - { - write_bjdata_ndarray_values(array_data, dtype); - return true; - } - for (const auto& el : array_data) { bool ok = true; @@ -4312,43 +4280,39 @@ class binary_writer { case 'U': case 'B': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'C': // BJData limits a char to 0..127 (the intersection of the int8 and uint8 ranges) ok = bjdata_ndarray_value_in_range(el) && bjdata_ndarray_value_in_range(el); break; case 'i': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'u': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'I': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'm': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'l': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'M': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'L': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'd': - ok = write_bjdata_ndarray_float_element(el, dry_run); + ok = bjdata_ndarray_float_in_range(el); break; case 'D': default: // 'D' (double) already spans the full range of number_float_t - if (!dry_run) - { - write_number(el.template get(), true); - } break; } if (!ok) @@ -4435,7 +4399,7 @@ class binary_writer const auto dim = el.template get(); if (!value_in_range_of(dim)) { - return true; + return true; // LCOV_EXCL_LINE (size_t is narrower than 64 bits) } const auto dim_size = static_cast(dim); @@ -4485,7 +4449,7 @@ class binary_writer // wrap (integers) or overflow to infinity (the "single" precision // float) instead of being reported, so such an object falls back to // a plain object encoding as well - if (!write_bjdata_ndarray_elements(array_data, dtype, true)) + if (!bjdata_ndarray_elements_in_range(array_data, dtype)) { return true; } @@ -4497,7 +4461,7 @@ class binary_writer write_ubjson(array_size, use_count, use_type, true, true, bjdata_version); - write_bjdata_ndarray_elements(array_data, dtype, false); + write_bjdata_ndarray_values(array_data, dtype); return false; } @@ -4823,8 +4787,8 @@ class binary_writer run are encoded into space reserved from the sink, as in @ref write_array_elements; an integer that BON8 cannot write (an unsigned one above the int64 range, or one of a number type wider than 64 bits) throws - from write_bon8_value, like any other value that is not a number. Kept out - of line, like @ref write_cbor_array. + out_of_range.407, as write_bon8_value does. Kept out of line, like + @ref write_cbor_array. */ template JSON_HEDLEY_NEVER_INLINE @@ -4874,10 +4838,10 @@ class binary_writer string_open = false; } - if (unencodable) + if (JSON_HEDLEY_UNLIKELY(unencodable)) { - write_bon8_value(*it, string_open, depth + 1); - ++it; + // an integer beyond the int64 range, as write_bon8_value would report it + throw_integer_out_of_range(*it, input_format_t::bon8, "int64"); } } } @@ -4912,14 +4876,13 @@ class binary_writer break; case value_t::number_integer: - if (JSON_HEDLEY_LIKELY(integer_fits(value.m_data.m_value.number_integer))) + if (JSON_HEDLEY_UNLIKELY(!integer_fits(value.m_data.m_value.number_integer))) { - write_bon8_integer(static_cast(value.m_data.m_value.number_integer)); - string_open = false; - break; + // beyond 64 bits (number types wider than that) + throw_integer_out_of_range(value, input_format_t::bon8, "int64"); } - // beyond 64 bits (number types wider than that): throws - write_bon8_value(value, string_open, depth + 1); + write_bon8_integer(static_cast(value.m_data.m_value.number_integer)); + string_open = false; break; case value_t::number_float: @@ -4928,14 +4891,13 @@ class binary_writer break; case value_t::number_unsigned: - if (JSON_HEDLEY_LIKELY(integer_fits_max(value.m_data.m_value.number_unsigned))) + if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(value.m_data.m_value.number_unsigned))) { - write_bon8_integer(static_cast(value.m_data.m_value.number_unsigned)); - string_open = false; - break; + // too large for BON8 + throw_integer_out_of_range(value, input_format_t::bon8, "int64"); } - // too large for BON8: throws - write_bon8_value(value, string_open, depth + 1); + write_bon8_integer(static_cast(value.m_data.m_value.number_unsigned)); + string_open = false; break; case value_t::object: diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index 4656dbdf1..fc8e2b384 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -1117,7 +1117,7 @@ class serializer // character if (EnsureAscii) { - JSON_ASSERT(codepoint >= 0x7F); + JSON_ASSERT(codepoint == 0x7F); if (error_handler == error_handler_t::keep) { // this character was buffered as raw bytes below @@ -1128,21 +1128,13 @@ class serializer // character instead bytes = bytes_after_last_accept; } - if (codepoint <= 0xFFFF) - { - write_u_escape(bytes, static_cast(codepoint)); - } - else - { - write_u_escape(bytes, static_cast(0xD7C0u + (codepoint >> 10u))); - write_u_escape(bytes, static_cast(0xDC00u + (codepoint & 0x3FFu))); - } + write_u_escape(bytes, static_cast(codepoint)); } else { // copy byte to buffer (all previous bytes of the // sequence have been copied in the default case below) - string_buffer[bytes++] = s[i]; + string_buffer[bytes++] = s[i]; // LCOV_EXCL_LINE (well-formed sequences are copied in runs) } reserve_string_buffer(bytes); diff --git a/include/nlohmann/detail/string_concat.hpp b/include/nlohmann/detail/string_concat.hpp index 74002c878..d7ad45af3 100644 --- a/include/nlohmann/detail/string_concat.hpp +++ b/include/nlohmann/detail/string_concat.hpp @@ -139,7 +139,7 @@ inline OutStringType concat(Args && ... args) str.reserve(concat_length(args...)); concat_into(str, std::forward(args)...); return str; -} +} // LCOV_EXCL_LINE (exception cleanup only) } // namespace detail NLOHMANN_JSON_NAMESPACE_END diff --git a/include/nlohmann/detail/string_escape.hpp b/include/nlohmann/detail/string_escape.hpp index 1a4b6c501..3e6dc13ae 100644 --- a/include/nlohmann/detail/string_escape.hpp +++ b/include/nlohmann/detail/string_escape.hpp @@ -83,10 +83,6 @@ template inline void unescape(StringType& s) { auto pos = s.find_first_of('~', 0); - if (pos == StringType::npos) - { - return; - } StringType result; result.reserve(s.size()); diff --git a/include/nlohmann/detail/string_utils.hpp b/include/nlohmann/detail/string_utils.hpp index 9b44623a5..c7ec18b3d 100644 --- a/include/nlohmann/detail/string_utils.hpp +++ b/include/nlohmann/detail/string_utils.hpp @@ -36,7 +36,7 @@ StringType to_string(std::size_t value) StringType result; int_to_string(result, value); return result; -} +} // LCOV_EXCL_LINE (exception cleanup only) /// @return a byte as two uppercase hexadecimal digits inline std::string hex_byte(const std::uint8_t byte) @@ -46,7 +46,7 @@ inline std::string hex_byte(const std::uint8_t byte) result[0] = nibble_to_hex[byte / 16]; result[1] = nibble_to_hex[byte % 16]; return result; -} +} // LCOV_EXCL_LINE (exception cleanup only) /*! @brief write a UTF-16 code unit as four lowercase hexadecimal digits @@ -390,7 +390,7 @@ inline StringType sanitize_utf8(const StringType& s, const error_handler_t error } return result; -} +} // LCOV_EXCL_LINE (exception cleanup only) } // namespace detail NLOHMANN_JSON_NAMESPACE_END diff --git a/include/nlohmann/detail/view/compare.hpp b/include/nlohmann/detail/view/compare.hpp index 96a14efbb..239871f01 100644 --- a/include/nlohmann/detail/view/compare.hpp +++ b/include/nlohmann/detail/view/compare.hpp @@ -428,28 +428,19 @@ class view_side return string_view_t(m_view.m_doc->str(*m_view.m_node), m_view.m_node->len); } - /// a number, boolean, or null as a basic_json value (no allocation) + /// a number as a basic_json value (no allocation); only called for numbers BasicJsonType scalar() const { - switch (m_view.type()) + const value_t t = m_view.type(); + if (t == value_t::number_integer) { - case value_t::number_integer: - return BasicJsonType(m_view.template get()); - case value_t::number_unsigned: - return BasicJsonType(m_view.template get()); - case value_t::number_float: - return BasicJsonType(m_view.template get()); - case value_t::boolean: - return BasicJsonType(m_view.template get()); - case value_t::null: - case value_t::object: - case value_t::array: - case value_t::string: - case value_t::binary: - case value_t::discarded: - default: - return BasicJsonType(nullptr); + return BasicJsonType(m_view.template get()); } + if (t == value_t::number_unsigned) + { + return BasicJsonType(m_view.template get()); + } + return BasicJsonType(m_view.template get()); } /// start at the first element of this array diff --git a/include/nlohmann/detail/view/edit.hpp b/include/nlohmann/detail/view/edit.hpp index b572b4848..8ddf3f849 100644 --- a/include/nlohmann/detail/view/edit.hpp +++ b/include/nlohmann/detail/view/edit.hpp @@ -96,11 +96,11 @@ NLOHMANN_VIEW_NOINLINE inline void check_utf8_scan(const unsigned char* p, std:: } if (*q >= 0x80) { - check_utf8_bytewise(p, n, static_cast(q - p)); - return; // not reached: the scan stopped at ill-formed UTF-8 + break; // ill-formed UTF-8: reported below } ++q; // a quote, backslash, or control character } + check_utf8_bytewise(p, n, static_cast(q - p)); } inline void check_utf8(const char* s, std::size_t n) diff --git a/include/nlohmann/detail/view/edit_storage.hpp b/include/nlohmann/detail/view/edit_storage.hpp index e5b7ddb90..10898f99f 100644 --- a/include/nlohmann/detail/view/edit_storage.hpp +++ b/include/nlohmann/detail/view/edit_storage.hpp @@ -243,7 +243,7 @@ inline node* find_parent(const document_data& d, const node* target) } if (target == lo) { - return nullptr; + return nullptr; // LCOV_EXCL_LINE (the root, or a new value's root, which is linked) } for (;;) { diff --git a/include/nlohmann/detail/view/errors.hpp b/include/nlohmann/detail/view/errors.hpp index eeab3bbd5..bc86dcf83 100644 --- a/include/nlohmann/detail/view/errors.hpp +++ b/include/nlohmann/detail/view/errors.hpp @@ -420,10 +420,12 @@ struct throwing_sax return true; } + // LCOV_EXCL_START (throw_parse_failure parses JSON only) bool binary(binary_t& /*unused*/) { return true; } + // LCOV_EXCL_STOP bool start_object(std::size_t /*unused*/) { diff --git a/include/nlohmann/detail/view/materialize.hpp b/include/nlohmann/detail/view/materialize.hpp index 1dcd1940c..71b29a13c 100644 --- a/include/nlohmann/detail/view/materialize.hpp +++ b/include/nlohmann/detail/view/materialize.hpp @@ -65,14 +65,9 @@ struct position_source return token{end - start}; } - /// the range [first, last), or none if either is npos + /// the range [first, last) void set(std::size_t first, std::size_t last) noexcept { - if (first == std::string::npos || last == std::string::npos) - { - clear(); - return; - } start = first; end = last; } @@ -191,11 +186,11 @@ struct position_source first = n.off; last = n.off + 4u; break; - case node_kind::object: + case node_kind::object: // LCOV_EXCL_LINE case node_kind::array: case node_kind::link: default: - break; + break; // LCOV_EXCL_LINE } // (a crafted image can point past the text) if (last == std::string::npos || last > d.size) diff --git a/include/nlohmann/detail/view/serializer.hpp b/include/nlohmann/detail/view/serializer.hpp index 2bfa2d2c0..fe6e0a6b1 100644 --- a/include/nlohmann/detail/view/serializer.hpp +++ b/include/nlohmann/detail/view/serializer.hpp @@ -1389,20 +1389,10 @@ class view_serializer /// a float node as dump() writes it void write_float_node(const node& n) { - write_float_node(n, fast_double {}); - } - - void write_float_node(const node& n, std::false_type /*other*/) - { + // (only for types other than double: doubles go through write_double_ahead()) write_float(float_value(m_doc, n)); } - void write_float_node(const node& n, std::true_type /*double*/) - { - m_out.reserve(64); - m_out.set_cursor(write_double_at(m_out.cursor(), n)); - } - /*! @brief (doubles) the float at n as dump() writes it, at w (64 bytes of room) @@ -1497,7 +1487,7 @@ class view_serializer { const string_t dumped = BasicJsonType(string_t(reinterpret_cast(s), n)).dump(); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) static_cast(dumped); - } + } // LCOV_EXCL_LINE (dump() throws: the callers pass only ill-formed UTF-8) template void write_codepoint(std::uint32_t codepoint, const unsigned char* bytes, std::size_t len) @@ -1530,15 +1520,7 @@ class view_serializer } if (codepoint <= 0x1F || (EnsureAscii && codepoint >= 0x7F)) { - if (codepoint <= 0xFFFF) - { - write_u_escape(codepoint); - } - else - { - write_u_escape(0xD7C0u + (codepoint >> 10u)); - write_u_escape(0xDC00u + (codepoint & 0x3FFu)); - } + write_u_escape(codepoint); // (a single byte: the only caller passes s[i]) return; } m_out.put(reinterpret_cast(bytes), len); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) LCOV_EXCL_LINE (printable characters are copied in runs) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 7bed813df..eb3544234 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -400,7 +400,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; #elif defined(__GNUC__) || defined(__GNUG__) result["compiler"] = {{"family", "gcc"}, {"version", detail::concat( - detail::to_string(static_cast(__GNUC__)), '.', + detail::to_string(static_cast(__GNUC__)), '.', // LCOV_EXCL_LINE detail::to_string(static_cast(__GNUC_MINOR__)), '.', detail::to_string(static_cast(__GNUC_PATCHLEVEL__))) } @@ -672,14 +672,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::binary: binary = create(); break; - case value_t::null: + case value_t::null: // LCOV_EXCL_LINE case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::discarded: default: - break; + break; // LCOV_EXCL_LINE } } @@ -1034,14 +1034,14 @@ public: destroy_container(t); break; - case value_t::null: + case value_t::null: // LCOV_EXCL_LINE case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::discarded: default: - break; + break; // LCOV_EXCL_LINE } } }; @@ -1214,9 +1214,9 @@ public: return is_array() ? array_capacity() : object_capacity(); } #else - static constexpr std::size_t container_capacity() noexcept + static constexpr std::size_t container_capacity() noexcept // LCOV_EXCL_LINE { - return detail::unknown_size(); + return detail::unknown_size(); // LCOV_EXCL_LINE } #endif @@ -1402,7 +1402,7 @@ public: /// into this switch is worth a few percent when copying a value made /// mostly of numbers JSON_HEDLEY_ALWAYS_INLINE - static void copy_leaf_value(const basic_json& src, basic_json& dst) + static void copy_leaf_value(const basic_json& src, basic_json& dst) // LCOV_EXCL_LINE { switch (src.m_data.m_type) { @@ -5554,7 +5554,7 @@ public: /// their inlined code at each call site small. Binary values are /// rare and left to equal_general(). JSON_HEDLEY_ALWAYS_INLINE - static int equal_leaf(const_reference lhs, const_reference rhs) noexcept + static int equal_leaf(const_reference lhs, const_reference rhs) noexcept // LCOV_EXCL_LINE { #ifdef __GNUC__ JSON_HEDLEY_DIAGNOSTIC_PUSH @@ -6863,7 +6863,7 @@ public: basic_json result(value_t::object); json_pointer::flatten("", *this, result); return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) /// @brief unflatten a previously flattened JSON value /// @sa https://json.nlohmann.me/api/basic_json/unflatten/ @@ -7576,7 +7576,7 @@ public: result.append(path.data(), path.size()); diff_append_key(result, key); return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) /// @brief the path of the element @a index of the value at @a path static string_t diff_index_path(const string_t& path, const std::size_t index) @@ -7586,7 +7586,7 @@ public: result.append(path.data(), path.size()); diff_append_index(result, index); return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) /// @brief append the "remove" operations for the elements of array /// @a source from @a index on, and the "add" operations for the @@ -7746,7 +7746,7 @@ public: // operations if (source == target) { - return false; + return false; // LCOV_EXCL_LINE (equal objects with the same members in the same order return early) } // slow path: the common keys are in a different relative diff --git a/include/nlohmann/json_view.hpp b/include/nlohmann/json_view.hpp index 043d09036..64aa8f723 100644 --- a/include/nlohmann/json_view.hpp +++ b/include/nlohmann/json_view.hpp @@ -1705,9 +1705,11 @@ class basic_json_document { // (as before: the library's adapter decides what a null stream yields) detail::view::append_adapter(buf, detail::input_adapter(file)); - return; } - detail::view::append_file(buf, file); + else + { + detail::view::append_file(buf, file); + } } #endif diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 1fc214c5f..d731d86a8 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3637,10 +3637,6 @@ template inline void unescape(StringType& s) { auto pos = s.find_first_of('~', 0); - if (pos == StringType::npos) - { - return; - } StringType result; result.reserve(s.size()); @@ -5516,7 +5512,7 @@ inline OutStringType concat(Args && ... args) str.reserve(concat_length(args...)); concat_into(str, std::forward(args)...); return str; -} +} // LCOV_EXCL_LINE (exception cleanup only) } // namespace detail NLOHMANN_JSON_NAMESPACE_END @@ -6236,7 +6232,7 @@ const typename BasicJsonType::binary_t& binary_value(const BasicJsonType& j) } template::value, int> = 0> -typename View::basic_json_type::binary_t binary_value(const View& /*unused*/) +typename View::basic_json_type::binary_t binary_value(const View& /*unused*/) // LCOV_EXCL_LINE { return {}; // LCOV_EXCL_LINE } @@ -7162,7 +7158,7 @@ StringType to_string(std::size_t value) StringType result; int_to_string(result, value); return result; -} +} // LCOV_EXCL_LINE (exception cleanup only) /// @return a byte as two uppercase hexadecimal digits inline std::string hex_byte(const std::uint8_t byte) @@ -7172,7 +7168,7 @@ inline std::string hex_byte(const std::uint8_t byte) result[0] = nibble_to_hex[byte / 16]; result[1] = nibble_to_hex[byte % 16]; return result; -} +} // LCOV_EXCL_LINE (exception cleanup only) /*! @brief write a UTF-16 code unit as four lowercase hexadecimal digits @@ -7516,7 +7512,7 @@ inline StringType sanitize_utf8(const StringType& s, const error_handler_t error } return result; -} +} // LCOV_EXCL_LINE (exception cleanup only) } // namespace detail NLOHMANN_JSON_NAMESPACE_END @@ -9683,7 +9679,7 @@ class input_stream_adapter } else { - current_in_get_area = true; + current_in_get_area = true; // LCOV_EXCL_LINE (called only when the get area is exhausted) } #endif return res; @@ -13475,11 +13471,10 @@ class lexer : public lexer_base template char_int_type get_after_string_run(std::true_type /*bulk*/, std::integral_constant keep) { - // a pending unget must be consumed through the normal path first - if (next_unget) - { - return get(); - } + // scan_string() is entered from scan_after_whitespace(), whose current + // was read by skip_whitespace() (its first get() clears a pending + // unget), and nothing in scan_string() calls unget() + JSON_ASSERT(!next_unget); const std::size_t remaining = ia.bulk_remaining(); if (remaining == 0) { @@ -14554,11 +14549,9 @@ scan_number_done: */ token_type scan_number_bulk_contiguous() { - // a pending unget offsets the buffer position from current; fall back - if (next_unget) - { - return token_type::uninitialized; - } + // called right after scan_after_whitespace() read current (see + // get_after_string_run()), so no unget is pending + JSON_ASSERT(!next_unget); const std::size_t rem = ia.bulk_remaining(); if (rem == 0) { @@ -14579,11 +14572,7 @@ scan_number_done: if (data[0] == '-') { number_type = token_type::value_integer; - i = 1; - if (i >= avail) - { - return token_type::uninitialized; - } + i = 1; // avail >= 2: rem == 0 returned above } const std::size_t integer_begin = i; if (data[i] == '0') @@ -16644,7 +16633,7 @@ class json_sax_dom_parser : public json_sax_dom_error_state ordered_data.members[f.ordered.dest_member].second = std::move(value); } - static void place_member(const frame& /*f*/, BasicJsonType&& /*value*/, std::false_type /*indexed*/) noexcept {} + static void place_member(const frame& /*f*/, BasicJsonType&& /*value*/, std::false_type /*indexed*/) noexcept {} // LCOV_EXCL_LINE /// store the members of a new object directly in it bool start_stored_object(std::size_t len) @@ -23773,7 +23762,7 @@ class parser } return error_msg; - } + } // LCOV_EXCL_LINE (exception cleanup only) private: /// callback function @@ -25084,7 +25073,7 @@ class json_pointer result.reference_tokens.insert(result.reference_tokens.end(), lhs.reference_tokens.begin(), lhs.reference_tokens.end()); result.reference_tokens.insert(result.reference_tokens.end(), rhs.reference_tokens.begin(), rhs.reference_tokens.end()); return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ @@ -25095,7 +25084,7 @@ class json_pointer result.reference_tokens.insert(result.reference_tokens.end(), lhs.reference_tokens.begin(), lhs.reference_tokens.end()); result.reference_tokens.push_back(std::move(token)); return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ @@ -26960,7 +26949,7 @@ class json_pointer { // create an entry in the array current = ¤t->operator[](array_index(token)); - break; + break; // LCOV_EXCL_LINE (needs an array index with 10 or more digits: more than 1e9 elements) } /* @@ -26997,7 +26986,7 @@ class json_pointer json_pointer result; result.reference_tokens = reference_tokens; return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) json_pointer convert()&& { @@ -28767,7 +28756,7 @@ class binary_writer while (n != 0); std::reverse(result.begin(), result.end()); return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) /*! @brief the decimal digits of the integer @a n, with a leading '-' if it @@ -28794,7 +28783,7 @@ class binary_writer } std::reverse(result.begin(), result.end()); return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) /*! @brief throws because the integer @a j is too large for @a format @@ -29726,7 +29715,7 @@ class binary_writer std::memcpy(out, &n, sizeof(NumberType)); if (!is_little_endian) { - std::reverse(out, out + sizeof(NumberType)); + std::reverse(out, out + sizeof(NumberType)); // LCOV_EXCL_LINE (big-endian hosts only) } } @@ -29855,7 +29844,7 @@ class binary_writer const string_t& value = *j.m_data.m_value.string; if (JSON_HEDLEY_UNLIKELY(!value_in_range_of(value.size() + 1ul))) { - return false; + return false; // LCOV_EXCL_LINE (a string of 2 GiB or more) } out[0] = to_char_type(0x02); store_little_endian(p, static_cast(value.size() + 1ul)); @@ -30312,7 +30301,7 @@ class binary_writer const std::size_t size = oa.written() - pos; if (JSON_HEDLEY_UNLIKELY(!value_in_range_of(size))) { - return false; + return false; // LCOV_EXCL_LINE (a document of 2 GiB or more) } const auto le_size = static_cast(size); oa.overwrite(pos, is_little_endian ? le_size : byteswap(le_size)); @@ -30441,7 +30430,7 @@ class binary_writer { if (JSON_HEDLEY_UNLIKELY(!close_bson_container(nested_size_pos))) { - return false; + return false; // LCOV_EXCL_LINE (a document of 2 GiB or more) } continue; } @@ -30456,7 +30445,7 @@ class binary_writer if (JSON_HEDLEY_UNLIKELY(!close_bson_container(current.size_slot))) { - return false; + return false; // LCOV_EXCL_LINE (a document of 2 GiB or more) } if (parents.empty()) { @@ -30713,10 +30702,10 @@ class binary_writer Selected through masks rather than branches, as the type and the sign of the values in an array of mixed signs alternate unpredictably (non-negative - integers are number_unsigned after parsing). Up to 64 bits, the sign is bit - 63 of the result (a narrower integer is sign-extended); computing it with - masks keeps GCC from branching on the type. A wider integer has to be - compared. + integers are number_unsigned after parsing). The sign is bit 63 of the + result (a narrower integer is sign-extended); computing it with masks keeps + GCC from branching on the type. Only called if @ref integers_fit_64_bits(); + a wider number type is written with range checks on the whole value. */ static std::uint64_t integer_bits(const BasicJsonType& j, std::uint64_t& sign_mask) noexcept { @@ -30724,9 +30713,7 @@ class binary_writer const std::uint64_t raw = is_signed ? static_cast(j.m_data.m_value.number_integer) : static_cast(j.m_data.m_value.number_unsigned); - sign_mask = sizeof(typename BasicJsonType::number_integer_t) <= sizeof(std::uint64_t) - ? (std::uint64_t{0} - static_cast(is_signed)) & (std::uint64_t{0} - (raw >> 63u)) - : std::uint64_t{0} - static_cast(is_signed && j.m_data.m_value.number_integer < 0); + sign_mask = (std::uint64_t{0} - static_cast(is_signed)) & (std::uint64_t{0} - (raw >> 63u)); return raw; } @@ -32093,57 +32080,34 @@ class binary_writer } /*! - @brief validate (dry_run) or write one BJData ND-array element of integer dtype @a T - @return whether @a el's value is in range of @a T; always true when @a dry_run is false - */ - template - bool write_bjdata_ndarray_element(const BasicJsonType& el, const bool dry_run) - { - if (dry_run) - { - return bjdata_ndarray_value_in_range(el); - } - using storage_type = typename std::conditional::value, std::uint64_t, std::int64_t>::type; - write_number(static_cast(el.template get()), true); - return true; - } - - /*! - @brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision) + @brief whether the BJData ND-array element @a el can be written with dtype 'd' (single precision) @return whether @a el's value survives narrowing to float and back without any change - (so the ND-array round-trips exactly), or is infinite or NaN; always true when - @a dry_run is false + (so the ND-array round-trips exactly), or is infinite or NaN */ - bool write_bjdata_ndarray_float_element(const BasicJsonType& el, const bool dry_run) + static bool bjdata_ndarray_float_in_range(const BasicJsonType& el) noexcept { const auto dval = static_cast(el.m_data.m_value.number_float); - if (dry_run) - { #ifdef __GNUC__ - JSON_HEDLEY_DIAGNOSTIC_PUSH - JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal") + JSON_HEDLEY_DIAGNOSTIC_PUSH + JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal") #endif - // a value that would be rounded (rather than exactly represented) by the - // narrowing to float is treated like an out-of-range integer element; this - // is the same criterion write_compact_float() uses for CBOR/MessagePack - const bool in_range = std::isnan(dval) || - (dval >= static_cast(std::numeric_limits::lowest()) && - dval <= static_cast((std::numeric_limits::max)()) && - static_cast(static_cast(dval)) == dval) || - std::isinf(dval); + // a value that would be rounded (rather than exactly represented) by the + // narrowing to float is treated like an out-of-range integer element; this + // is the same criterion write_compact_float() uses for CBOR/MessagePack + return std::isnan(dval) || + (dval >= static_cast(std::numeric_limits::lowest()) && + dval <= static_cast((std::numeric_limits::max)()) && + static_cast(static_cast(dval)) == dval) || + std::isinf(dval); #ifdef __GNUC__ - JSON_HEDLEY_DIAGNOSTIC_POP + JSON_HEDLEY_DIAGNOSTIC_POP #endif - return in_range; - } - write_number(static_cast(dval), true); - return true; } /*! @brief write the elements of an ND-array as the number kind @a dtype names (see @ref write_bjdata_ndarray), after they have been found to be - in range by a dry run of @ref write_bjdata_ndarray_elements + in range by @ref bjdata_ndarray_elements_in_range The elements are encoded in blocks into space reserved from the sink. */ @@ -32225,20 +32189,13 @@ class binary_writer } /*! - @brief validate or write every element of a BJData ND-array's `_ArrayData_` + @brief whether every element of a BJData ND-array's `_ArrayData_` is in range for its dtype @param[in] array_data the `_ArrayData_` array @param[in] dtype the ND-array dtype marker, as returned by bjdata_ndarray_type_marker() - @param[in] dry_run true to only range-check each element, false to write it - @return whether every element is in range for @a dtype (always true when @a dry_run is false) + @return whether every element is in range for @a dtype */ - bool write_bjdata_ndarray_elements(const BasicJsonType& array_data, const CharType dtype, const bool dry_run) + static bool bjdata_ndarray_elements_in_range(const BasicJsonType& array_data, const CharType dtype) { - if (!dry_run) - { - write_bjdata_ndarray_values(array_data, dtype); - return true; - } - for (const auto& el : array_data) { bool ok = true; @@ -32246,43 +32203,39 @@ class binary_writer { case 'U': case 'B': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'C': // BJData limits a char to 0..127 (the intersection of the int8 and uint8 ranges) ok = bjdata_ndarray_value_in_range(el) && bjdata_ndarray_value_in_range(el); break; case 'i': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'u': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'I': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'm': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'l': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'M': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'L': - ok = write_bjdata_ndarray_element(el, dry_run); + ok = bjdata_ndarray_value_in_range(el); break; case 'd': - ok = write_bjdata_ndarray_float_element(el, dry_run); + ok = bjdata_ndarray_float_in_range(el); break; case 'D': default: // 'D' (double) already spans the full range of number_float_t - if (!dry_run) - { - write_number(el.template get(), true); - } break; } if (!ok) @@ -32369,7 +32322,7 @@ class binary_writer const auto dim = el.template get(); if (!value_in_range_of(dim)) { - return true; + return true; // LCOV_EXCL_LINE (size_t is narrower than 64 bits) } const auto dim_size = static_cast(dim); @@ -32419,7 +32372,7 @@ class binary_writer // wrap (integers) or overflow to infinity (the "single" precision // float) instead of being reported, so such an object falls back to // a plain object encoding as well - if (!write_bjdata_ndarray_elements(array_data, dtype, true)) + if (!bjdata_ndarray_elements_in_range(array_data, dtype)) { return true; } @@ -32431,7 +32384,7 @@ class binary_writer write_ubjson(array_size, use_count, use_type, true, true, bjdata_version); - write_bjdata_ndarray_elements(array_data, dtype, false); + write_bjdata_ndarray_values(array_data, dtype); return false; } @@ -32757,8 +32710,8 @@ class binary_writer run are encoded into space reserved from the sink, as in @ref write_array_elements; an integer that BON8 cannot write (an unsigned one above the int64 range, or one of a number type wider than 64 bits) throws - from write_bon8_value, like any other value that is not a number. Kept out - of line, like @ref write_cbor_array. + out_of_range.407, as write_bon8_value does. Kept out of line, like + @ref write_cbor_array. */ template JSON_HEDLEY_NEVER_INLINE @@ -32808,10 +32761,10 @@ class binary_writer string_open = false; } - if (unencodable) + if (JSON_HEDLEY_UNLIKELY(unencodable)) { - write_bon8_value(*it, string_open, depth + 1); - ++it; + // an integer beyond the int64 range, as write_bon8_value would report it + throw_integer_out_of_range(*it, input_format_t::bon8, "int64"); } } } @@ -32846,14 +32799,13 @@ class binary_writer break; case value_t::number_integer: - if (JSON_HEDLEY_LIKELY(integer_fits(value.m_data.m_value.number_integer))) + if (JSON_HEDLEY_UNLIKELY(!integer_fits(value.m_data.m_value.number_integer))) { - write_bon8_integer(static_cast(value.m_data.m_value.number_integer)); - string_open = false; - break; + // beyond 64 bits (number types wider than that) + throw_integer_out_of_range(value, input_format_t::bon8, "int64"); } - // beyond 64 bits (number types wider than that): throws - write_bon8_value(value, string_open, depth + 1); + write_bon8_integer(static_cast(value.m_data.m_value.number_integer)); + string_open = false; break; case value_t::number_float: @@ -32862,14 +32814,13 @@ class binary_writer break; case value_t::number_unsigned: - if (JSON_HEDLEY_LIKELY(integer_fits_max(value.m_data.m_value.number_unsigned))) + if (JSON_HEDLEY_UNLIKELY(!integer_fits_max(value.m_data.m_value.number_unsigned))) { - write_bon8_integer(static_cast(value.m_data.m_value.number_unsigned)); - string_open = false; - break; + // too large for BON8 + throw_integer_out_of_range(value, input_format_t::bon8, "int64"); } - // too large for BON8: throws - write_bon8_value(value, string_open, depth + 1); + write_bon8_integer(static_cast(value.m_data.m_value.number_unsigned)); + string_open = false; break; case value_t::object: @@ -36326,7 +36277,7 @@ class serializer // character if (EnsureAscii) { - JSON_ASSERT(codepoint >= 0x7F); + JSON_ASSERT(codepoint == 0x7F); if (error_handler == error_handler_t::keep) { // this character was buffered as raw bytes below @@ -36337,21 +36288,13 @@ class serializer // character instead bytes = bytes_after_last_accept; } - if (codepoint <= 0xFFFF) - { - write_u_escape(bytes, static_cast(codepoint)); - } - else - { - write_u_escape(bytes, static_cast(0xD7C0u + (codepoint >> 10u))); - write_u_escape(bytes, static_cast(0xDC00u + (codepoint & 0x3FFu))); - } + write_u_escape(bytes, static_cast(codepoint)); } else { // copy byte to buffer (all previous bytes of the // sequence have been copied in the default case below) - string_buffer[bytes++] = s[i]; + string_buffer[bytes++] = s[i]; // LCOV_EXCL_LINE (well-formed sequences are copied in runs) } reserve_string_buffer(bytes); @@ -38192,7 +38135,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; #elif defined(__GNUC__) || defined(__GNUG__) result["compiler"] = {{"family", "gcc"}, {"version", detail::concat( - detail::to_string(static_cast(__GNUC__)), '.', + detail::to_string(static_cast(__GNUC__)), '.', // LCOV_EXCL_LINE detail::to_string(static_cast(__GNUC_MINOR__)), '.', detail::to_string(static_cast(__GNUC_PATCHLEVEL__))) } @@ -38464,14 +38407,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::binary: binary = create(); break; - case value_t::null: + case value_t::null: // LCOV_EXCL_LINE case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::discarded: default: - break; + break; // LCOV_EXCL_LINE } } @@ -38826,14 +38769,14 @@ public: destroy_container(t); break; - case value_t::null: + case value_t::null: // LCOV_EXCL_LINE case value_t::boolean: case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: case value_t::discarded: default: - break; + break; // LCOV_EXCL_LINE } } }; @@ -39006,9 +38949,9 @@ public: return is_array() ? array_capacity() : object_capacity(); } #else - static constexpr std::size_t container_capacity() noexcept + static constexpr std::size_t container_capacity() noexcept // LCOV_EXCL_LINE { - return detail::unknown_size(); + return detail::unknown_size(); // LCOV_EXCL_LINE } #endif @@ -39194,7 +39137,7 @@ public: /// into this switch is worth a few percent when copying a value made /// mostly of numbers JSON_HEDLEY_ALWAYS_INLINE - static void copy_leaf_value(const basic_json& src, basic_json& dst) + static void copy_leaf_value(const basic_json& src, basic_json& dst) // LCOV_EXCL_LINE { switch (src.m_data.m_type) { @@ -43346,7 +43289,7 @@ public: /// their inlined code at each call site small. Binary values are /// rare and left to equal_general(). JSON_HEDLEY_ALWAYS_INLINE - static int equal_leaf(const_reference lhs, const_reference rhs) noexcept + static int equal_leaf(const_reference lhs, const_reference rhs) noexcept // LCOV_EXCL_LINE { #ifdef __GNUC__ JSON_HEDLEY_DIAGNOSTIC_PUSH @@ -44655,7 +44598,7 @@ public: basic_json result(value_t::object); json_pointer::flatten("", *this, result); return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) /// @brief unflatten a previously flattened JSON value /// @sa https://json.nlohmann.me/api/basic_json/unflatten/ @@ -45368,7 +45311,7 @@ public: result.append(path.data(), path.size()); diff_append_key(result, key); return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) /// @brief the path of the element @a index of the value at @a path static string_t diff_index_path(const string_t& path, const std::size_t index) @@ -45378,7 +45321,7 @@ public: result.append(path.data(), path.size()); diff_append_index(result, index); return result; - } + } // LCOV_EXCL_LINE (exception cleanup only) /// @brief append the "remove" operations for the elements of array /// @a source from @a index on, and the "add" operations for the @@ -45538,7 +45481,7 @@ public: // operations if (source == target) { - return false; + return false; // LCOV_EXCL_LINE (equal objects with the same members in the same order return early) } // slow path: the common keys are in a different relative diff --git a/single_include/nlohmann/json_view.hpp b/single_include/nlohmann/json_view.hpp index e24f125bf..c9adec745 100644 --- a/single_include/nlohmann/json_view.hpp +++ b/single_include/nlohmann/json_view.hpp @@ -3580,28 +3580,19 @@ class view_side return string_view_t(m_view.m_doc->str(*m_view.m_node), m_view.m_node->len); } - /// a number, boolean, or null as a basic_json value (no allocation) + /// a number as a basic_json value (no allocation); only called for numbers BasicJsonType scalar() const { - switch (m_view.type()) + const value_t t = m_view.type(); + if (t == value_t::number_integer) { - case value_t::number_integer: - return BasicJsonType(m_view.template get()); - case value_t::number_unsigned: - return BasicJsonType(m_view.template get()); - case value_t::number_float: - return BasicJsonType(m_view.template get()); - case value_t::boolean: - return BasicJsonType(m_view.template get()); - case value_t::null: - case value_t::object: - case value_t::array: - case value_t::string: - case value_t::binary: - case value_t::discarded: - default: - return BasicJsonType(nullptr); + return BasicJsonType(m_view.template get()); } + if (t == value_t::number_unsigned) + { + return BasicJsonType(m_view.template get()); + } + return BasicJsonType(m_view.template get()); } /// start at the first element of this array @@ -4478,14 +4469,9 @@ struct position_source return token{end - start}; } - /// the range [first, last), or none if either is npos + /// the range [first, last) void set(std::size_t first, std::size_t last) noexcept { - if (first == std::string::npos || last == std::string::npos) - { - clear(); - return; - } start = first; end = last; } @@ -4604,11 +4590,11 @@ struct position_source first = n.off; last = n.off + 4u; break; - case node_kind::object: + case node_kind::object: // LCOV_EXCL_LINE case node_kind::array: case node_kind::link: default: - break; + break; // LCOV_EXCL_LINE } // (a crafted image can point past the text) if (last == std::string::npos || last > d.size) @@ -5262,10 +5248,12 @@ struct throwing_sax return true; } + // LCOV_EXCL_START (throw_parse_failure parses JSON only) bool binary(binary_t& /*unused*/) { return true; } + // LCOV_EXCL_STOP bool start_object(std::size_t /*unused*/) { @@ -5573,7 +5561,7 @@ inline node* find_parent(const document_data& d, const node* target) } if (target == lo) { - return nullptr; + return nullptr; // LCOV_EXCL_LINE (the root, or a new value's root, which is linked) } for (;;) { @@ -6194,11 +6182,11 @@ NLOHMANN_VIEW_NOINLINE inline void check_utf8_scan(const unsigned char* p, std:: } if (*q >= 0x80) { - check_utf8_bytewise(p, n, static_cast(q - p)); - return; // not reached: the scan stopped at ill-formed UTF-8 + break; // ill-formed UTF-8: reported below } ++q; // a quote, backslash, or control character } + check_utf8_bytewise(p, n, static_cast(q - p)); } inline void check_utf8(const char* s, std::size_t n) @@ -9853,20 +9841,10 @@ class view_serializer /// a float node as dump() writes it void write_float_node(const node& n) { - write_float_node(n, fast_double {}); - } - - void write_float_node(const node& n, std::false_type /*other*/) - { + // (only for types other than double: doubles go through write_double_ahead()) write_float(float_value(m_doc, n)); } - void write_float_node(const node& n, std::true_type /*double*/) - { - m_out.reserve(64); - m_out.set_cursor(write_double_at(m_out.cursor(), n)); - } - /*! @brief (doubles) the float at n as dump() writes it, at w (64 bytes of room) @@ -9961,7 +9939,7 @@ class view_serializer { const string_t dumped = BasicJsonType(string_t(reinterpret_cast(s), n)).dump(); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) static_cast(dumped); - } + } // LCOV_EXCL_LINE (dump() throws: the callers pass only ill-formed UTF-8) template void write_codepoint(std::uint32_t codepoint, const unsigned char* bytes, std::size_t len) @@ -9994,15 +9972,7 @@ class view_serializer } if (codepoint <= 0x1F || (EnsureAscii && codepoint >= 0x7F)) { - if (codepoint <= 0xFFFF) - { - write_u_escape(codepoint); - } - else - { - write_u_escape(0xD7C0u + (codepoint >> 10u)); - write_u_escape(0xDC00u + (codepoint & 0x3FFu)); - } + write_u_escape(codepoint); // (a single byte: the only caller passes s[i]) return; } m_out.put(reinterpret_cast(bytes), len); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) LCOV_EXCL_LINE (printable characters are copied in runs) @@ -11799,9 +11769,11 @@ class basic_json_document { // (as before: the library's adapter decides what a null stream yields) detail::view::append_adapter(buf, detail::input_adapter(file)); - return; } - detail::view::append_file(buf, file); + else + { + detail::view::append_file(buf, file); + } } #endif