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 <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-11-01 22:32:41 +01:00
1 parent 04a5accfb9
commit d7b9472173
21 files changed
+234 -404

No files matched your search

@@ -91,7 +91,7 @@ const typename BasicJsonType::binary_t& binary_value(const BasicJsonType& j)
} }
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0> template<typename View, enable_if_t<is_json_view<View>::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 return {}; // LCOV_EXCL_LINE
} }
@@ -572,7 +572,7 @@ class input_stream_adapter
} }
else else
{ {
current_in_get_area = true; current_in_get_area = true; // LCOV_EXCL_LINE (called only when the get area is exhausted)
} }
#endif #endif
return res; return res;
+1 -1
View File
@@ -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); 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 /// store the members of a new object directly in it
bool start_stored_object(std::size_t len) bool start_stored_object(std::size_t len)
+8 -15
View File
@@ -437,11 +437,10 @@ class lexer : public lexer_base<BasicJsonType>
template<bool Keep> template<bool Keep>
char_int_type get_after_string_run(std::true_type /*bulk*/, std::integral_constant<bool, Keep> keep) char_int_type get_after_string_run(std::true_type /*bulk*/, std::integral_constant<bool, Keep> keep)
{ {
// a pending unget must be consumed through the normal path first // scan_string() is entered from scan_after_whitespace(), whose current
if (next_unget) // was read by skip_whitespace() (its first get() clears a pending
{ // unget), and nothing in scan_string() calls unget()
return get(); JSON_ASSERT(!next_unget);
}
const std::size_t remaining = ia.bulk_remaining(); const std::size_t remaining = ia.bulk_remaining();
if (remaining == 0) if (remaining == 0)
{ {
@@ -1516,11 +1515,9 @@ scan_number_done:
*/ */
token_type scan_number_bulk_contiguous() token_type scan_number_bulk_contiguous()
{ {
// a pending unget offsets the buffer position from current; fall back // called right after scan_after_whitespace() read current (see
if (next_unget) // get_after_string_run()), so no unget is pending
{ JSON_ASSERT(!next_unget);
return token_type::uninitialized;
}
const std::size_t rem = ia.bulk_remaining(); const std::size_t rem = ia.bulk_remaining();
if (rem == 0) if (rem == 0)
{ {
@@ -1541,11 +1538,7 @@ scan_number_done:
if (data[0] == '-') if (data[0] == '-')
{ {
number_type = token_type::value_integer; number_type = token_type::value_integer;
i = 1; i = 1; // avail >= 2: rem == 0 returned above
if (i >= avail)
{
return token_type::uninitialized;
}
} }
const std::size_t integer_begin = i; const std::size_t integer_begin = i;
if (data[i] == '0') if (data[i] == '0')
+1 -1
View File
@@ -598,7 +598,7 @@ class parser
} }
return error_msg; return error_msg;
} } // LCOV_EXCL_LINE (exception cleanup only)
private: private:
/// callback function /// callback function
+4 -4
View File
@@ -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(), lhs.reference_tokens.begin(), lhs.reference_tokens.end());
result.reference_tokens.insert(result.reference_tokens.end(), rhs.reference_tokens.begin(), rhs.reference_tokens.end()); result.reference_tokens.insert(result.reference_tokens.end(), rhs.reference_tokens.begin(), rhs.reference_tokens.end());
return result; 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 /// @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/ /// @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.insert(result.reference_tokens.end(), lhs.reference_tokens.begin(), lhs.reference_tokens.end());
result.reference_tokens.push_back(std::move(token)); result.reference_tokens.push_back(std::move(token));
return result; 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 /// @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/ /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
@@ -2180,7 +2180,7 @@ class json_pointer
{ {
// create an entry in the array // create an entry in the array
current = &current->operator[](array_index<BasicJsonType>(token)); current = &current->operator[](array_index<BasicJsonType>(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<string_t> result; json_pointer<string_t> result;
result.reference_tokens = reference_tokens; result.reference_tokens = reference_tokens;
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
json_pointer<string_t> convert()&& json_pointer<string_t> convert()&&
{ {
@@ -833,7 +833,7 @@ class binary_writer
while (n != 0); while (n != 0);
std::reverse(result.begin(), result.end()); std::reverse(result.begin(), result.end());
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
/*! /*!
@brief the decimal digits of the integer @a n, with a leading '-' if it @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()); std::reverse(result.begin(), result.end());
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
/*! /*!
@brief throws because the integer @a j is too large for @a format @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)); std::memcpy(out, &n, sizeof(NumberType));
if (!is_little_endian) 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; const string_t& value = *j.m_data.m_value.string;
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(value.size() + 1ul))) if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(value.size() + 1ul)))
{ {
return false; return false; // LCOV_EXCL_LINE (a string of 2 GiB or more)
} }
out[0] = to_char_type(0x02); out[0] = to_char_type(0x02);
store_little_endian<std::int32_t>(p, static_cast<std::int32_t>(value.size() + 1ul)); store_little_endian<std::int32_t>(p, static_cast<std::int32_t>(value.size() + 1ul));
@@ -2378,7 +2378,7 @@ class binary_writer
const std::size_t size = oa.written() - pos; const std::size_t size = oa.written() - pos;
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(size))) if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(size)))
{ {
return false; return false; // LCOV_EXCL_LINE (a document of 2 GiB or more)
} }
const auto le_size = static_cast<std::uint32_t>(size); const auto le_size = static_cast<std::uint32_t>(size);
oa.overwrite(pos, is_little_endian ? le_size : byteswap(le_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))) 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; continue;
} }
@@ -2522,7 +2522,7 @@ class binary_writer
if (JSON_HEDLEY_UNLIKELY(!close_bson_container(current.size_slot))) 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()) if (parents.empty())
{ {
@@ -2779,10 +2779,10 @@ class binary_writer
Selected through masks rather than branches, as the type and the sign of 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 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 integers are number_unsigned after parsing). The sign is bit 63 of the
63 of the result (a narrower integer is sign-extended); computing it with result (a narrower integer is sign-extended); computing it with masks keeps
masks keeps GCC from branching on the type. A wider integer has to be GCC from branching on the type. Only called if @ref integers_fit_64_bits();
compared. 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 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 const std::uint64_t raw = is_signed
? static_cast<std::uint64_t>(j.m_data.m_value.number_integer) ? static_cast<std::uint64_t>(j.m_data.m_value.number_integer)
: static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned); : static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned);
sign_mask = sizeof(typename BasicJsonType::number_integer_t) <= sizeof(std::uint64_t) sign_mask = (std::uint64_t{0} - static_cast<std::uint64_t>(is_signed)) & (std::uint64_t{0} - (raw >> 63u));
? (std::uint64_t{0} - static_cast<std::uint64_t>(is_signed)) & (std::uint64_t{0} - (raw >> 63u))
: std::uint64_t{0} - static_cast<std::uint64_t>(is_signed && j.m_data.m_value.number_integer < 0);
return raw; 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 @brief whether the BJData ND-array element @a el can be written with dtype 'd' (single precision)
@return whether @a el's value is in range of @a T; always true when @a dry_run is false
*/
template<typename T>
bool write_bjdata_ndarray_element(const BasicJsonType& el, const bool dry_run)
{
if (dry_run)
{
return bjdata_ndarray_value_in_range<T>(el);
}
using storage_type = typename std::conditional<std::is_unsigned<T>::value, std::uint64_t, std::int64_t>::type;
write_number(static_cast<T>(el.template get<storage_type>()), true);
return true;
}
/*!
@brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision)
@return whether @a el's value survives narrowing to float and back without any change @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 (so the ND-array round-trips exactly), or is infinite or NaN
@a dry_run is false
*/ */
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<double>(el.m_data.m_value.number_float); const auto dval = static_cast<double>(el.m_data.m_value.number_float);
if (dry_run)
{
#ifdef __GNUC__ #ifdef __GNUC__
JSON_HEDLEY_DIAGNOSTIC_PUSH JSON_HEDLEY_DIAGNOSTIC_PUSH
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal") JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
#endif #endif
// a value that would be rounded (rather than exactly represented) by the // 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 // narrowing to float is treated like an out-of-range integer element; this
// is the same criterion write_compact_float() uses for CBOR/MessagePack // is the same criterion write_compact_float() uses for CBOR/MessagePack
const bool in_range = std::isnan(dval) || return std::isnan(dval) ||
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) && (dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
dval <= static_cast<double>((std::numeric_limits<float>::max)()) && dval <= static_cast<double>((std::numeric_limits<float>::max)()) &&
static_cast<double>(static_cast<float>(dval)) == dval) || static_cast<double>(static_cast<float>(dval)) == dval) ||
std::isinf(dval); std::isinf(dval);
#ifdef __GNUC__ #ifdef __GNUC__
JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_DIAGNOSTIC_POP
#endif #endif
return in_range;
}
write_number(static_cast<float>(dval), true);
return true;
} }
/*! /*!
@brief write the elements of an ND-array as the number kind @a dtype names @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 (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. 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] array_data the `_ArrayData_` array
@param[in] dtype the ND-array dtype marker, as returned by bjdata_ndarray_type_marker() @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
@return whether every element is in range for @a dtype (always true when @a dry_run is false)
*/ */
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) for (const auto& el : array_data)
{ {
bool ok = true; bool ok = true;
@@ -4312,43 +4280,39 @@ class binary_writer
{ {
case 'U': case 'U':
case 'B': case 'B':
ok = write_bjdata_ndarray_element<std::uint8_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::uint8_t>(el);
break; break;
case 'C': case 'C':
// BJData limits a char to 0..127 (the intersection of the int8 and uint8 ranges) // BJData limits a char to 0..127 (the intersection of the int8 and uint8 ranges)
ok = bjdata_ndarray_value_in_range<std::int8_t>(el) && bjdata_ndarray_value_in_range<std::uint8_t>(el); ok = bjdata_ndarray_value_in_range<std::int8_t>(el) && bjdata_ndarray_value_in_range<std::uint8_t>(el);
break; break;
case 'i': case 'i':
ok = write_bjdata_ndarray_element<std::int8_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::int8_t>(el);
break; break;
case 'u': case 'u':
ok = write_bjdata_ndarray_element<std::uint16_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::uint16_t>(el);
break; break;
case 'I': case 'I':
ok = write_bjdata_ndarray_element<std::int16_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::int16_t>(el);
break; break;
case 'm': case 'm':
ok = write_bjdata_ndarray_element<std::uint32_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::uint32_t>(el);
break; break;
case 'l': case 'l':
ok = write_bjdata_ndarray_element<std::int32_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::int32_t>(el);
break; break;
case 'M': case 'M':
ok = write_bjdata_ndarray_element<std::uint64_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::uint64_t>(el);
break; break;
case 'L': case 'L':
ok = write_bjdata_ndarray_element<std::int64_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::int64_t>(el);
break; break;
case 'd': case 'd':
ok = write_bjdata_ndarray_float_element(el, dry_run); ok = bjdata_ndarray_float_in_range(el);
break; break;
case 'D': case 'D':
default: default:
// 'D' (double) already spans the full range of number_float_t // 'D' (double) already spans the full range of number_float_t
if (!dry_run)
{
write_number(el.template get<double>(), true);
}
break; break;
} }
if (!ok) if (!ok)
@@ -4435,7 +4399,7 @@ class binary_writer
const auto dim = el.template get<std::uint64_t>(); const auto dim = el.template get<std::uint64_t>();
if (!value_in_range_of<std::size_t>(dim)) if (!value_in_range_of<std::size_t>(dim))
{ {
return true; return true; // LCOV_EXCL_LINE (size_t is narrower than 64 bits)
} }
const auto dim_size = static_cast<std::size_t>(dim); const auto dim_size = static_cast<std::size_t>(dim);
@@ -4485,7 +4449,7 @@ class binary_writer
// wrap (integers) or overflow to infinity (the "single" precision // wrap (integers) or overflow to infinity (the "single" precision
// float) instead of being reported, so such an object falls back to // float) instead of being reported, so such an object falls back to
// a plain object encoding as well // 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; return true;
} }
@@ -4497,7 +4461,7 @@ class binary_writer
write_ubjson(array_size, use_count, use_type, true, true, bjdata_version); 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; return false;
} }
@@ -4823,8 +4787,8 @@ class binary_writer
run are encoded into space reserved from the sink, as in run are encoded into space reserved from the sink, as in
@ref write_array_elements; an integer that BON8 cannot write (an unsigned @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 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 out_of_range.407, as write_bon8_value does. Kept out of line, like
of line, like @ref write_cbor_array. @ref write_cbor_array.
*/ */
template<typename ArrayType> template<typename ArrayType>
JSON_HEDLEY_NEVER_INLINE JSON_HEDLEY_NEVER_INLINE
@@ -4874,10 +4838,10 @@ class binary_writer
string_open = false; string_open = false;
} }
if (unencodable) if (JSON_HEDLEY_UNLIKELY(unencodable))
{ {
write_bon8_value(*it, string_open, depth + 1); // an integer beyond the int64 range, as write_bon8_value would report it
++it; throw_integer_out_of_range(*it, input_format_t::bon8, "int64");
} }
} }
} }
@@ -4912,14 +4876,13 @@ class binary_writer
break; break;
case value_t::number_integer: case value_t::number_integer:
if (JSON_HEDLEY_LIKELY(integer_fits<std::int64_t>(value.m_data.m_value.number_integer))) if (JSON_HEDLEY_UNLIKELY(!integer_fits<std::int64_t>(value.m_data.m_value.number_integer)))
{ {
write_bon8_integer(static_cast<std::int64_t>(value.m_data.m_value.number_integer)); // beyond 64 bits (number types wider than that)
string_open = false; throw_integer_out_of_range(value, input_format_t::bon8, "int64");
break;
} }
// beyond 64 bits (number types wider than that): throws write_bon8_integer(static_cast<std::int64_t>(value.m_data.m_value.number_integer));
write_bon8_value(value, string_open, depth + 1); string_open = false;
break; break;
case value_t::number_float: case value_t::number_float:
@@ -4928,14 +4891,13 @@ class binary_writer
break; break;
case value_t::number_unsigned: case value_t::number_unsigned:
if (JSON_HEDLEY_LIKELY(integer_fits_max<std::int64_t>(value.m_data.m_value.number_unsigned))) if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::int64_t>(value.m_data.m_value.number_unsigned)))
{ {
write_bon8_integer(static_cast<std::int64_t>(value.m_data.m_value.number_unsigned)); // too large for BON8
string_open = false; throw_integer_out_of_range(value, input_format_t::bon8, "int64");
break;
} }
// too large for BON8: throws write_bon8_integer(static_cast<std::int64_t>(value.m_data.m_value.number_unsigned));
write_bon8_value(value, string_open, depth + 1); string_open = false;
break; break;
case value_t::object: case value_t::object:
+3 -11
View File
@@ -1117,7 +1117,7 @@ class serializer
// character // character
if (EnsureAscii) if (EnsureAscii)
{ {
JSON_ASSERT(codepoint >= 0x7F); JSON_ASSERT(codepoint == 0x7F);
if (error_handler == error_handler_t::keep) if (error_handler == error_handler_t::keep)
{ {
// this character was buffered as raw bytes below // this character was buffered as raw bytes below
@@ -1128,21 +1128,13 @@ class serializer
// character instead // character instead
bytes = bytes_after_last_accept; bytes = bytes_after_last_accept;
} }
if (codepoint <= 0xFFFF) write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
{
write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
}
else
{
write_u_escape(bytes, static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)));
write_u_escape(bytes, static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));
}
} }
else else
{ {
// copy byte to buffer (all previous bytes of the // copy byte to buffer (all previous bytes of the
// sequence have been copied in the default case below) // 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); reserve_string_buffer(bytes);
+1 -1
View File
@@ -139,7 +139,7 @@ inline OutStringType concat(Args && ... args)
str.reserve(concat_length(args...)); str.reserve(concat_length(args...));
concat_into(str, std::forward<Args>(args)...); concat_into(str, std::forward<Args>(args)...);
return str; return str;
} } // LCOV_EXCL_LINE (exception cleanup only)
} // namespace detail } // namespace detail
NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_END
@@ -83,10 +83,6 @@ template<typename StringType>
inline void unescape(StringType& s) inline void unescape(StringType& s)
{ {
auto pos = s.find_first_of('~', 0); auto pos = s.find_first_of('~', 0);
if (pos == StringType::npos)
{
return;
}
StringType result; StringType result;
result.reserve(s.size()); result.reserve(s.size());
+3 -3
View File
@@ -36,7 +36,7 @@ StringType to_string(std::size_t value)
StringType result; StringType result;
int_to_string(result, value); int_to_string(result, value);
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
/// @return a byte as two uppercase hexadecimal digits /// @return a byte as two uppercase hexadecimal digits
inline std::string hex_byte(const std::uint8_t byte) 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[0] = nibble_to_hex[byte / 16];
result[1] = nibble_to_hex[byte % 16]; result[1] = nibble_to_hex[byte % 16];
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
/*! /*!
@brief write a UTF-16 code unit as four lowercase hexadecimal digits @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; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
} // namespace detail } // namespace detail
NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_END
+9 -18
View File
@@ -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); 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 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<typename BasicJsonType::number_integer_t>());
return BasicJsonType(m_view.template get<typename BasicJsonType::number_integer_t>());
case value_t::number_unsigned:
return BasicJsonType(m_view.template get<typename BasicJsonType::number_unsigned_t>());
case value_t::number_float:
return BasicJsonType(m_view.template get<typename BasicJsonType::number_float_t>());
case value_t::boolean:
return BasicJsonType(m_view.template get<bool>());
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);
} }
if (t == value_t::number_unsigned)
{
return BasicJsonType(m_view.template get<typename BasicJsonType::number_unsigned_t>());
}
return BasicJsonType(m_view.template get<typename BasicJsonType::number_float_t>());
} }
/// start at the first element of this array /// start at the first element of this array
+2 -2
View File
@@ -96,11 +96,11 @@ NLOHMANN_VIEW_NOINLINE inline void check_utf8_scan(const unsigned char* p, std::
} }
if (*q >= 0x80) if (*q >= 0x80)
{ {
check_utf8_bytewise(p, n, static_cast<std::size_t>(q - p)); break; // ill-formed UTF-8: reported below
return; // not reached: the scan stopped at ill-formed UTF-8
} }
++q; // a quote, backslash, or control character ++q; // a quote, backslash, or control character
} }
check_utf8_bytewise(p, n, static_cast<std::size_t>(q - p));
} }
inline void check_utf8(const char* s, std::size_t n) inline void check_utf8(const char* s, std::size_t n)
@@ -243,7 +243,7 @@ inline node* find_parent(const document_data& d, const node* target)
} }
if (target == lo) if (target == lo)
{ {
return nullptr; return nullptr; // LCOV_EXCL_LINE (the root, or a new value's root, which is linked)
} }
for (;;) for (;;)
{ {
+2
View File
@@ -420,10 +420,12 @@ struct throwing_sax
return true; return true;
} }
// LCOV_EXCL_START (throw_parse_failure parses JSON only)
bool binary(binary_t& /*unused*/) bool binary(binary_t& /*unused*/)
{ {
return true; return true;
} }
// LCOV_EXCL_STOP
bool start_object(std::size_t /*unused*/) bool start_object(std::size_t /*unused*/)
{ {
+3 -8
View File
@@ -65,14 +65,9 @@ struct position_source
return token{end - start}; 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 void set(std::size_t first, std::size_t last) noexcept
{ {
if (first == std::string::npos || last == std::string::npos)
{
clear();
return;
}
start = first; start = first;
end = last; end = last;
} }
@@ -191,11 +186,11 @@ struct position_source
first = n.off; first = n.off;
last = n.off + 4u; last = n.off + 4u;
break; break;
case node_kind::object: case node_kind::object: // LCOV_EXCL_LINE
case node_kind::array: case node_kind::array:
case node_kind::link: case node_kind::link:
default: default:
break; break; // LCOV_EXCL_LINE
} }
// (a crafted image can point past the text) // (a crafted image can point past the text)
if (last == std::string::npos || last > d.size) if (last == std::string::npos || last > d.size)
+3 -21
View File
@@ -1389,20 +1389,10 @@ class view_serializer
/// a float node as dump() writes it /// a float node as dump() writes it
void write_float_node(const node& n) void write_float_node(const node& n)
{ {
write_float_node(n, fast_double {}); // (only for types other than double: doubles go through write_double_ahead())
}
void write_float_node(const node& n, std::false_type /*other*/)
{
write_float(float_value<number_float_t>(m_doc, n)); write_float(float_value<number_float_t>(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) @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<const char*>(s), n)).dump(); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) const string_t dumped = BasicJsonType(string_t(reinterpret_cast<const char*>(s), n)).dump(); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
static_cast<void>(dumped); static_cast<void>(dumped);
} } // LCOV_EXCL_LINE (dump() throws: the callers pass only ill-formed UTF-8)
template<bool EnsureAscii> template<bool EnsureAscii>
void write_codepoint(std::uint32_t codepoint, const unsigned char* bytes, std::size_t len) 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 <= 0x1F || (EnsureAscii && codepoint >= 0x7F))
{ {
if (codepoint <= 0xFFFF) write_u_escape(codepoint); // (a single byte: the only caller passes s[i])
{
write_u_escape(codepoint);
}
else
{
write_u_escape(0xD7C0u + (codepoint >> 10u));
write_u_escape(0xDC00u + (codepoint & 0x3FFu));
}
return; return;
} }
m_out.put(reinterpret_cast<const char*>(bytes), len); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) LCOV_EXCL_LINE (printable characters are copied in runs) m_out.put(reinterpret_cast<const char*>(bytes), len); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) LCOV_EXCL_LINE (printable characters are copied in runs)
+13 -13
View File
@@ -400,7 +400,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
#elif defined(__GNUC__) || defined(__GNUG__) #elif defined(__GNUC__) || defined(__GNUG__)
result["compiler"] = {{"family", "gcc"}, {"version", detail::concat<string_t>( result["compiler"] = {{"family", "gcc"}, {"version", detail::concat<string_t>(
detail::to_string<string_t>(static_cast<std::size_t>(__GNUC__)), '.', detail::to_string<string_t>(static_cast<std::size_t>(__GNUC__)), '.', // LCOV_EXCL_LINE
detail::to_string<string_t>(static_cast<std::size_t>(__GNUC_MINOR__)), '.', detail::to_string<string_t>(static_cast<std::size_t>(__GNUC_MINOR__)), '.',
detail::to_string<string_t>(static_cast<std::size_t>(__GNUC_PATCHLEVEL__))) detail::to_string<string_t>(static_cast<std::size_t>(__GNUC_PATCHLEVEL__)))
} }
@@ -672,14 +672,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
case value_t::binary: case value_t::binary:
binary = create<binary_t>(); binary = create<binary_t>();
break; break;
case value_t::null: case value_t::null: // LCOV_EXCL_LINE
case value_t::boolean: case value_t::boolean:
case value_t::number_integer: case value_t::number_integer:
case value_t::number_unsigned: case value_t::number_unsigned:
case value_t::number_float: case value_t::number_float:
case value_t::discarded: case value_t::discarded:
default: default:
break; break; // LCOV_EXCL_LINE
} }
} }
@@ -1034,14 +1034,14 @@ public:
destroy_container(t); destroy_container(t);
break; break;
case value_t::null: case value_t::null: // LCOV_EXCL_LINE
case value_t::boolean: case value_t::boolean:
case value_t::number_integer: case value_t::number_integer:
case value_t::number_unsigned: case value_t::number_unsigned:
case value_t::number_float: case value_t::number_float:
case value_t::discarded: case value_t::discarded:
default: default:
break; break; // LCOV_EXCL_LINE
} }
} }
}; };
@@ -1214,9 +1214,9 @@ public:
return is_array() ? array_capacity() : object_capacity(); return is_array() ? array_capacity() : object_capacity();
} }
#else #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 #endif
@@ -1402,7 +1402,7 @@ public:
/// into this switch is worth a few percent when copying a value made /// into this switch is worth a few percent when copying a value made
/// mostly of numbers /// mostly of numbers
JSON_HEDLEY_ALWAYS_INLINE 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) switch (src.m_data.m_type)
{ {
@@ -5554,7 +5554,7 @@ public:
/// their inlined code at each call site small. Binary values are /// their inlined code at each call site small. Binary values are
/// rare and left to equal_general(). /// rare and left to equal_general().
JSON_HEDLEY_ALWAYS_INLINE 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__ #ifdef __GNUC__
JSON_HEDLEY_DIAGNOSTIC_PUSH JSON_HEDLEY_DIAGNOSTIC_PUSH
@@ -6863,7 +6863,7 @@ public:
basic_json result(value_t::object); basic_json result(value_t::object);
json_pointer::flatten("", *this, result); json_pointer::flatten("", *this, result);
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
/// @brief unflatten a previously flattened JSON value /// @brief unflatten a previously flattened JSON value
/// @sa https://json.nlohmann.me/api/basic_json/unflatten/ /// @sa https://json.nlohmann.me/api/basic_json/unflatten/
@@ -7576,7 +7576,7 @@ public:
result.append(path.data(), path.size()); result.append(path.data(), path.size());
diff_append_key(result, key); diff_append_key(result, key);
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
/// @brief the path of the element @a index of the value at @a path /// @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) 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()); result.append(path.data(), path.size());
diff_append_index(result, index); diff_append_index(result, index);
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
/// @brief append the "remove" operations for the elements of array /// @brief append the "remove" operations for the elements of array
/// @a source from @a index on, and the "add" operations for the /// @a source from @a index on, and the "add" operations for the
@@ -7746,7 +7746,7 @@ public:
// operations // operations
if (source == target) 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 // slow path: the common keys are in a different relative
+4 -2
View File
@@ -1705,9 +1705,11 @@ class basic_json_document
{ {
// (as before: the library's adapter decides what a null stream yields) // (as before: the library's adapter decides what a null stream yields)
detail::view::append_adapter(buf, detail::input_adapter(file)); detail::view::append_adapter(buf, detail::input_adapter(file));
return;
} }
detail::view::append_file(buf, file); else
{
detail::view::append_file(buf, file);
}
} }
#endif #endif
+93 -150
View File
@@ -3637,10 +3637,6 @@ template<typename StringType>
inline void unescape(StringType& s) inline void unescape(StringType& s)
{ {
auto pos = s.find_first_of('~', 0); auto pos = s.find_first_of('~', 0);
if (pos == StringType::npos)
{
return;
}
StringType result; StringType result;
result.reserve(s.size()); result.reserve(s.size());
@@ -5516,7 +5512,7 @@ inline OutStringType concat(Args && ... args)
str.reserve(concat_length(args...)); str.reserve(concat_length(args...));
concat_into(str, std::forward<Args>(args)...); concat_into(str, std::forward<Args>(args)...);
return str; return str;
} } // LCOV_EXCL_LINE (exception cleanup only)
} // namespace detail } // namespace detail
NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_END
@@ -6236,7 +6232,7 @@ const typename BasicJsonType::binary_t& binary_value(const BasicJsonType& j)
} }
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0> template<typename View, enable_if_t<is_json_view<View>::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 return {}; // LCOV_EXCL_LINE
} }
@@ -7162,7 +7158,7 @@ StringType to_string(std::size_t value)
StringType result; StringType result;
int_to_string(result, value); int_to_string(result, value);
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
/// @return a byte as two uppercase hexadecimal digits /// @return a byte as two uppercase hexadecimal digits
inline std::string hex_byte(const std::uint8_t byte) 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[0] = nibble_to_hex[byte / 16];
result[1] = nibble_to_hex[byte % 16]; result[1] = nibble_to_hex[byte % 16];
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
/*! /*!
@brief write a UTF-16 code unit as four lowercase hexadecimal digits @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; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
} // namespace detail } // namespace detail
NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_END
@@ -9683,7 +9679,7 @@ class input_stream_adapter
} }
else else
{ {
current_in_get_area = true; current_in_get_area = true; // LCOV_EXCL_LINE (called only when the get area is exhausted)
} }
#endif #endif
return res; return res;
@@ -13475,11 +13471,10 @@ class lexer : public lexer_base<BasicJsonType>
template<bool Keep> template<bool Keep>
char_int_type get_after_string_run(std::true_type /*bulk*/, std::integral_constant<bool, Keep> keep) char_int_type get_after_string_run(std::true_type /*bulk*/, std::integral_constant<bool, Keep> keep)
{ {
// a pending unget must be consumed through the normal path first // scan_string() is entered from scan_after_whitespace(), whose current
if (next_unget) // was read by skip_whitespace() (its first get() clears a pending
{ // unget), and nothing in scan_string() calls unget()
return get(); JSON_ASSERT(!next_unget);
}
const std::size_t remaining = ia.bulk_remaining(); const std::size_t remaining = ia.bulk_remaining();
if (remaining == 0) if (remaining == 0)
{ {
@@ -14554,11 +14549,9 @@ scan_number_done:
*/ */
token_type scan_number_bulk_contiguous() token_type scan_number_bulk_contiguous()
{ {
// a pending unget offsets the buffer position from current; fall back // called right after scan_after_whitespace() read current (see
if (next_unget) // get_after_string_run()), so no unget is pending
{ JSON_ASSERT(!next_unget);
return token_type::uninitialized;
}
const std::size_t rem = ia.bulk_remaining(); const std::size_t rem = ia.bulk_remaining();
if (rem == 0) if (rem == 0)
{ {
@@ -14579,11 +14572,7 @@ scan_number_done:
if (data[0] == '-') if (data[0] == '-')
{ {
number_type = token_type::value_integer; number_type = token_type::value_integer;
i = 1; i = 1; // avail >= 2: rem == 0 returned above
if (i >= avail)
{
return token_type::uninitialized;
}
} }
const std::size_t integer_begin = i; const std::size_t integer_begin = i;
if (data[i] == '0') 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); 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 /// store the members of a new object directly in it
bool start_stored_object(std::size_t len) bool start_stored_object(std::size_t len)
@@ -23773,7 +23762,7 @@ class parser
} }
return error_msg; return error_msg;
} } // LCOV_EXCL_LINE (exception cleanup only)
private: private:
/// callback function /// 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(), lhs.reference_tokens.begin(), lhs.reference_tokens.end());
result.reference_tokens.insert(result.reference_tokens.end(), rhs.reference_tokens.begin(), rhs.reference_tokens.end()); result.reference_tokens.insert(result.reference_tokens.end(), rhs.reference_tokens.begin(), rhs.reference_tokens.end());
return result; 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 /// @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/ /// @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.insert(result.reference_tokens.end(), lhs.reference_tokens.begin(), lhs.reference_tokens.end());
result.reference_tokens.push_back(std::move(token)); result.reference_tokens.push_back(std::move(token));
return result; 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 /// @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/ /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
@@ -26960,7 +26949,7 @@ class json_pointer
{ {
// create an entry in the array // create an entry in the array
current = &current->operator[](array_index<BasicJsonType>(token)); current = &current->operator[](array_index<BasicJsonType>(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<string_t> result; json_pointer<string_t> result;
result.reference_tokens = reference_tokens; result.reference_tokens = reference_tokens;
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
json_pointer<string_t> convert()&& json_pointer<string_t> convert()&&
{ {
@@ -28767,7 +28756,7 @@ class binary_writer
while (n != 0); while (n != 0);
std::reverse(result.begin(), result.end()); std::reverse(result.begin(), result.end());
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
/*! /*!
@brief the decimal digits of the integer @a n, with a leading '-' if it @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()); std::reverse(result.begin(), result.end());
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
/*! /*!
@brief throws because the integer @a j is too large for @a format @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)); std::memcpy(out, &n, sizeof(NumberType));
if (!is_little_endian) 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; const string_t& value = *j.m_data.m_value.string;
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(value.size() + 1ul))) if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(value.size() + 1ul)))
{ {
return false; return false; // LCOV_EXCL_LINE (a string of 2 GiB or more)
} }
out[0] = to_char_type(0x02); out[0] = to_char_type(0x02);
store_little_endian<std::int32_t>(p, static_cast<std::int32_t>(value.size() + 1ul)); store_little_endian<std::int32_t>(p, static_cast<std::int32_t>(value.size() + 1ul));
@@ -30312,7 +30301,7 @@ class binary_writer
const std::size_t size = oa.written() - pos; const std::size_t size = oa.written() - pos;
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(size))) if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(size)))
{ {
return false; return false; // LCOV_EXCL_LINE (a document of 2 GiB or more)
} }
const auto le_size = static_cast<std::uint32_t>(size); const auto le_size = static_cast<std::uint32_t>(size);
oa.overwrite(pos, is_little_endian ? le_size : byteswap(le_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))) 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; continue;
} }
@@ -30456,7 +30445,7 @@ class binary_writer
if (JSON_HEDLEY_UNLIKELY(!close_bson_container(current.size_slot))) 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()) if (parents.empty())
{ {
@@ -30713,10 +30702,10 @@ class binary_writer
Selected through masks rather than branches, as the type and the sign of 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 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 integers are number_unsigned after parsing). The sign is bit 63 of the
63 of the result (a narrower integer is sign-extended); computing it with result (a narrower integer is sign-extended); computing it with masks keeps
masks keeps GCC from branching on the type. A wider integer has to be GCC from branching on the type. Only called if @ref integers_fit_64_bits();
compared. 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 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 const std::uint64_t raw = is_signed
? static_cast<std::uint64_t>(j.m_data.m_value.number_integer) ? static_cast<std::uint64_t>(j.m_data.m_value.number_integer)
: static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned); : static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned);
sign_mask = sizeof(typename BasicJsonType::number_integer_t) <= sizeof(std::uint64_t) sign_mask = (std::uint64_t{0} - static_cast<std::uint64_t>(is_signed)) & (std::uint64_t{0} - (raw >> 63u));
? (std::uint64_t{0} - static_cast<std::uint64_t>(is_signed)) & (std::uint64_t{0} - (raw >> 63u))
: std::uint64_t{0} - static_cast<std::uint64_t>(is_signed && j.m_data.m_value.number_integer < 0);
return raw; 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 @brief whether the BJData ND-array element @a el can be written with dtype 'd' (single precision)
@return whether @a el's value is in range of @a T; always true when @a dry_run is false
*/
template<typename T>
bool write_bjdata_ndarray_element(const BasicJsonType& el, const bool dry_run)
{
if (dry_run)
{
return bjdata_ndarray_value_in_range<T>(el);
}
using storage_type = typename std::conditional<std::is_unsigned<T>::value, std::uint64_t, std::int64_t>::type;
write_number(static_cast<T>(el.template get<storage_type>()), true);
return true;
}
/*!
@brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision)
@return whether @a el's value survives narrowing to float and back without any change @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 (so the ND-array round-trips exactly), or is infinite or NaN
@a dry_run is false
*/ */
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<double>(el.m_data.m_value.number_float); const auto dval = static_cast<double>(el.m_data.m_value.number_float);
if (dry_run)
{
#ifdef __GNUC__ #ifdef __GNUC__
JSON_HEDLEY_DIAGNOSTIC_PUSH JSON_HEDLEY_DIAGNOSTIC_PUSH
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal") JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
#endif #endif
// a value that would be rounded (rather than exactly represented) by the // 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 // narrowing to float is treated like an out-of-range integer element; this
// is the same criterion write_compact_float() uses for CBOR/MessagePack // is the same criterion write_compact_float() uses for CBOR/MessagePack
const bool in_range = std::isnan(dval) || return std::isnan(dval) ||
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) && (dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
dval <= static_cast<double>((std::numeric_limits<float>::max)()) && dval <= static_cast<double>((std::numeric_limits<float>::max)()) &&
static_cast<double>(static_cast<float>(dval)) == dval) || static_cast<double>(static_cast<float>(dval)) == dval) ||
std::isinf(dval); std::isinf(dval);
#ifdef __GNUC__ #ifdef __GNUC__
JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_DIAGNOSTIC_POP
#endif #endif
return in_range;
}
write_number(static_cast<float>(dval), true);
return true;
} }
/*! /*!
@brief write the elements of an ND-array as the number kind @a dtype names @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 (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. 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] array_data the `_ArrayData_` array
@param[in] dtype the ND-array dtype marker, as returned by bjdata_ndarray_type_marker() @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
@return whether every element is in range for @a dtype (always true when @a dry_run is false)
*/ */
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) for (const auto& el : array_data)
{ {
bool ok = true; bool ok = true;
@@ -32246,43 +32203,39 @@ class binary_writer
{ {
case 'U': case 'U':
case 'B': case 'B':
ok = write_bjdata_ndarray_element<std::uint8_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::uint8_t>(el);
break; break;
case 'C': case 'C':
// BJData limits a char to 0..127 (the intersection of the int8 and uint8 ranges) // BJData limits a char to 0..127 (the intersection of the int8 and uint8 ranges)
ok = bjdata_ndarray_value_in_range<std::int8_t>(el) && bjdata_ndarray_value_in_range<std::uint8_t>(el); ok = bjdata_ndarray_value_in_range<std::int8_t>(el) && bjdata_ndarray_value_in_range<std::uint8_t>(el);
break; break;
case 'i': case 'i':
ok = write_bjdata_ndarray_element<std::int8_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::int8_t>(el);
break; break;
case 'u': case 'u':
ok = write_bjdata_ndarray_element<std::uint16_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::uint16_t>(el);
break; break;
case 'I': case 'I':
ok = write_bjdata_ndarray_element<std::int16_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::int16_t>(el);
break; break;
case 'm': case 'm':
ok = write_bjdata_ndarray_element<std::uint32_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::uint32_t>(el);
break; break;
case 'l': case 'l':
ok = write_bjdata_ndarray_element<std::int32_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::int32_t>(el);
break; break;
case 'M': case 'M':
ok = write_bjdata_ndarray_element<std::uint64_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::uint64_t>(el);
break; break;
case 'L': case 'L':
ok = write_bjdata_ndarray_element<std::int64_t>(el, dry_run); ok = bjdata_ndarray_value_in_range<std::int64_t>(el);
break; break;
case 'd': case 'd':
ok = write_bjdata_ndarray_float_element(el, dry_run); ok = bjdata_ndarray_float_in_range(el);
break; break;
case 'D': case 'D':
default: default:
// 'D' (double) already spans the full range of number_float_t // 'D' (double) already spans the full range of number_float_t
if (!dry_run)
{
write_number(el.template get<double>(), true);
}
break; break;
} }
if (!ok) if (!ok)
@@ -32369,7 +32322,7 @@ class binary_writer
const auto dim = el.template get<std::uint64_t>(); const auto dim = el.template get<std::uint64_t>();
if (!value_in_range_of<std::size_t>(dim)) if (!value_in_range_of<std::size_t>(dim))
{ {
return true; return true; // LCOV_EXCL_LINE (size_t is narrower than 64 bits)
} }
const auto dim_size = static_cast<std::size_t>(dim); const auto dim_size = static_cast<std::size_t>(dim);
@@ -32419,7 +32372,7 @@ class binary_writer
// wrap (integers) or overflow to infinity (the "single" precision // wrap (integers) or overflow to infinity (the "single" precision
// float) instead of being reported, so such an object falls back to // float) instead of being reported, so such an object falls back to
// a plain object encoding as well // 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; return true;
} }
@@ -32431,7 +32384,7 @@ class binary_writer
write_ubjson(array_size, use_count, use_type, true, true, bjdata_version); 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; return false;
} }
@@ -32757,8 +32710,8 @@ class binary_writer
run are encoded into space reserved from the sink, as in run are encoded into space reserved from the sink, as in
@ref write_array_elements; an integer that BON8 cannot write (an unsigned @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 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 out_of_range.407, as write_bon8_value does. Kept out of line, like
of line, like @ref write_cbor_array. @ref write_cbor_array.
*/ */
template<typename ArrayType> template<typename ArrayType>
JSON_HEDLEY_NEVER_INLINE JSON_HEDLEY_NEVER_INLINE
@@ -32808,10 +32761,10 @@ class binary_writer
string_open = false; string_open = false;
} }
if (unencodable) if (JSON_HEDLEY_UNLIKELY(unencodable))
{ {
write_bon8_value(*it, string_open, depth + 1); // an integer beyond the int64 range, as write_bon8_value would report it
++it; throw_integer_out_of_range(*it, input_format_t::bon8, "int64");
} }
} }
} }
@@ -32846,14 +32799,13 @@ class binary_writer
break; break;
case value_t::number_integer: case value_t::number_integer:
if (JSON_HEDLEY_LIKELY(integer_fits<std::int64_t>(value.m_data.m_value.number_integer))) if (JSON_HEDLEY_UNLIKELY(!integer_fits<std::int64_t>(value.m_data.m_value.number_integer)))
{ {
write_bon8_integer(static_cast<std::int64_t>(value.m_data.m_value.number_integer)); // beyond 64 bits (number types wider than that)
string_open = false; throw_integer_out_of_range(value, input_format_t::bon8, "int64");
break;
} }
// beyond 64 bits (number types wider than that): throws write_bon8_integer(static_cast<std::int64_t>(value.m_data.m_value.number_integer));
write_bon8_value(value, string_open, depth + 1); string_open = false;
break; break;
case value_t::number_float: case value_t::number_float:
@@ -32862,14 +32814,13 @@ class binary_writer
break; break;
case value_t::number_unsigned: case value_t::number_unsigned:
if (JSON_HEDLEY_LIKELY(integer_fits_max<std::int64_t>(value.m_data.m_value.number_unsigned))) if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::int64_t>(value.m_data.m_value.number_unsigned)))
{ {
write_bon8_integer(static_cast<std::int64_t>(value.m_data.m_value.number_unsigned)); // too large for BON8
string_open = false; throw_integer_out_of_range(value, input_format_t::bon8, "int64");
break;
} }
// too large for BON8: throws write_bon8_integer(static_cast<std::int64_t>(value.m_data.m_value.number_unsigned));
write_bon8_value(value, string_open, depth + 1); string_open = false;
break; break;
case value_t::object: case value_t::object:
@@ -36326,7 +36277,7 @@ class serializer
// character // character
if (EnsureAscii) if (EnsureAscii)
{ {
JSON_ASSERT(codepoint >= 0x7F); JSON_ASSERT(codepoint == 0x7F);
if (error_handler == error_handler_t::keep) if (error_handler == error_handler_t::keep)
{ {
// this character was buffered as raw bytes below // this character was buffered as raw bytes below
@@ -36337,21 +36288,13 @@ class serializer
// character instead // character instead
bytes = bytes_after_last_accept; bytes = bytes_after_last_accept;
} }
if (codepoint <= 0xFFFF) write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
{
write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
}
else
{
write_u_escape(bytes, static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)));
write_u_escape(bytes, static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));
}
} }
else else
{ {
// copy byte to buffer (all previous bytes of the // copy byte to buffer (all previous bytes of the
// sequence have been copied in the default case below) // 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); 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__}}; result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
#elif defined(__GNUC__) || defined(__GNUG__) #elif defined(__GNUC__) || defined(__GNUG__)
result["compiler"] = {{"family", "gcc"}, {"version", detail::concat<string_t>( result["compiler"] = {{"family", "gcc"}, {"version", detail::concat<string_t>(
detail::to_string<string_t>(static_cast<std::size_t>(__GNUC__)), '.', detail::to_string<string_t>(static_cast<std::size_t>(__GNUC__)), '.', // LCOV_EXCL_LINE
detail::to_string<string_t>(static_cast<std::size_t>(__GNUC_MINOR__)), '.', detail::to_string<string_t>(static_cast<std::size_t>(__GNUC_MINOR__)), '.',
detail::to_string<string_t>(static_cast<std::size_t>(__GNUC_PATCHLEVEL__))) detail::to_string<string_t>(static_cast<std::size_t>(__GNUC_PATCHLEVEL__)))
} }
@@ -38464,14 +38407,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
case value_t::binary: case value_t::binary:
binary = create<binary_t>(); binary = create<binary_t>();
break; break;
case value_t::null: case value_t::null: // LCOV_EXCL_LINE
case value_t::boolean: case value_t::boolean:
case value_t::number_integer: case value_t::number_integer:
case value_t::number_unsigned: case value_t::number_unsigned:
case value_t::number_float: case value_t::number_float:
case value_t::discarded: case value_t::discarded:
default: default:
break; break; // LCOV_EXCL_LINE
} }
} }
@@ -38826,14 +38769,14 @@ public:
destroy_container(t); destroy_container(t);
break; break;
case value_t::null: case value_t::null: // LCOV_EXCL_LINE
case value_t::boolean: case value_t::boolean:
case value_t::number_integer: case value_t::number_integer:
case value_t::number_unsigned: case value_t::number_unsigned:
case value_t::number_float: case value_t::number_float:
case value_t::discarded: case value_t::discarded:
default: default:
break; break; // LCOV_EXCL_LINE
} }
} }
}; };
@@ -39006,9 +38949,9 @@ public:
return is_array() ? array_capacity() : object_capacity(); return is_array() ? array_capacity() : object_capacity();
} }
#else #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 #endif
@@ -39194,7 +39137,7 @@ public:
/// into this switch is worth a few percent when copying a value made /// into this switch is worth a few percent when copying a value made
/// mostly of numbers /// mostly of numbers
JSON_HEDLEY_ALWAYS_INLINE 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) switch (src.m_data.m_type)
{ {
@@ -43346,7 +43289,7 @@ public:
/// their inlined code at each call site small. Binary values are /// their inlined code at each call site small. Binary values are
/// rare and left to equal_general(). /// rare and left to equal_general().
JSON_HEDLEY_ALWAYS_INLINE 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__ #ifdef __GNUC__
JSON_HEDLEY_DIAGNOSTIC_PUSH JSON_HEDLEY_DIAGNOSTIC_PUSH
@@ -44655,7 +44598,7 @@ public:
basic_json result(value_t::object); basic_json result(value_t::object);
json_pointer::flatten("", *this, result); json_pointer::flatten("", *this, result);
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
/// @brief unflatten a previously flattened JSON value /// @brief unflatten a previously flattened JSON value
/// @sa https://json.nlohmann.me/api/basic_json/unflatten/ /// @sa https://json.nlohmann.me/api/basic_json/unflatten/
@@ -45368,7 +45311,7 @@ public:
result.append(path.data(), path.size()); result.append(path.data(), path.size());
diff_append_key(result, key); diff_append_key(result, key);
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
/// @brief the path of the element @a index of the value at @a path /// @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) 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()); result.append(path.data(), path.size());
diff_append_index(result, index); diff_append_index(result, index);
return result; return result;
} } // LCOV_EXCL_LINE (exception cleanup only)
/// @brief append the "remove" operations for the elements of array /// @brief append the "remove" operations for the elements of array
/// @a source from @a index on, and the "add" operations for the /// @a source from @a index on, and the "add" operations for the
@@ -45538,7 +45481,7 @@ public:
// operations // operations
if (source == target) 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 // slow path: the common keys are in a different relative
+24 -52
View File
@@ -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); 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 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<typename BasicJsonType::number_integer_t>());
return BasicJsonType(m_view.template get<typename BasicJsonType::number_integer_t>());
case value_t::number_unsigned:
return BasicJsonType(m_view.template get<typename BasicJsonType::number_unsigned_t>());
case value_t::number_float:
return BasicJsonType(m_view.template get<typename BasicJsonType::number_float_t>());
case value_t::boolean:
return BasicJsonType(m_view.template get<bool>());
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);
} }
if (t == value_t::number_unsigned)
{
return BasicJsonType(m_view.template get<typename BasicJsonType::number_unsigned_t>());
}
return BasicJsonType(m_view.template get<typename BasicJsonType::number_float_t>());
} }
/// start at the first element of this array /// start at the first element of this array
@@ -4478,14 +4469,9 @@ struct position_source
return token{end - start}; 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 void set(std::size_t first, std::size_t last) noexcept
{ {
if (first == std::string::npos || last == std::string::npos)
{
clear();
return;
}
start = first; start = first;
end = last; end = last;
} }
@@ -4604,11 +4590,11 @@ struct position_source
first = n.off; first = n.off;
last = n.off + 4u; last = n.off + 4u;
break; break;
case node_kind::object: case node_kind::object: // LCOV_EXCL_LINE
case node_kind::array: case node_kind::array:
case node_kind::link: case node_kind::link:
default: default:
break; break; // LCOV_EXCL_LINE
} }
// (a crafted image can point past the text) // (a crafted image can point past the text)
if (last == std::string::npos || last > d.size) if (last == std::string::npos || last > d.size)
@@ -5262,10 +5248,12 @@ struct throwing_sax
return true; return true;
} }
// LCOV_EXCL_START (throw_parse_failure parses JSON only)
bool binary(binary_t& /*unused*/) bool binary(binary_t& /*unused*/)
{ {
return true; return true;
} }
// LCOV_EXCL_STOP
bool start_object(std::size_t /*unused*/) 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) if (target == lo)
{ {
return nullptr; return nullptr; // LCOV_EXCL_LINE (the root, or a new value's root, which is linked)
} }
for (;;) for (;;)
{ {
@@ -6194,11 +6182,11 @@ NLOHMANN_VIEW_NOINLINE inline void check_utf8_scan(const unsigned char* p, std::
} }
if (*q >= 0x80) if (*q >= 0x80)
{ {
check_utf8_bytewise(p, n, static_cast<std::size_t>(q - p)); break; // ill-formed UTF-8: reported below
return; // not reached: the scan stopped at ill-formed UTF-8
} }
++q; // a quote, backslash, or control character ++q; // a quote, backslash, or control character
} }
check_utf8_bytewise(p, n, static_cast<std::size_t>(q - p));
} }
inline void check_utf8(const char* s, std::size_t n) 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 /// a float node as dump() writes it
void write_float_node(const node& n) void write_float_node(const node& n)
{ {
write_float_node(n, fast_double {}); // (only for types other than double: doubles go through write_double_ahead())
}
void write_float_node(const node& n, std::false_type /*other*/)
{
write_float(float_value<number_float_t>(m_doc, n)); write_float(float_value<number_float_t>(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) @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<const char*>(s), n)).dump(); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) const string_t dumped = BasicJsonType(string_t(reinterpret_cast<const char*>(s), n)).dump(); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
static_cast<void>(dumped); static_cast<void>(dumped);
} } // LCOV_EXCL_LINE (dump() throws: the callers pass only ill-formed UTF-8)
template<bool EnsureAscii> template<bool EnsureAscii>
void write_codepoint(std::uint32_t codepoint, const unsigned char* bytes, std::size_t len) 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 <= 0x1F || (EnsureAscii && codepoint >= 0x7F))
{ {
if (codepoint <= 0xFFFF) write_u_escape(codepoint); // (a single byte: the only caller passes s[i])
{
write_u_escape(codepoint);
}
else
{
write_u_escape(0xD7C0u + (codepoint >> 10u));
write_u_escape(0xDC00u + (codepoint & 0x3FFu));
}
return; return;
} }
m_out.put(reinterpret_cast<const char*>(bytes), len); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) LCOV_EXCL_LINE (printable characters are copied in runs) m_out.put(reinterpret_cast<const char*>(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) // (as before: the library's adapter decides what a null stream yields)
detail::view::append_adapter(buf, detail::input_adapter(file)); detail::view::append_adapter(buf, detail::input_adapter(file));
return;
} }
detail::view::append_file(buf, file); else
{
detail::view::append_file(buf, file);
}
} }
#endif #endif