mirror of
https://github.com/nlohmann/json.git
synced 2026-10-25 15:28:54 +00:00
* Add shared detail helpers for the #5891 cleanup Add detail::is_primitive(value_t), detail::hex4_lower(), detail::utf8_shape(), detail::input_format_name(), detail::empty_input_message(), the exception ids 116, 319, 320 and 416 to detail::exception_id, and make detail::is_ascii_copyable() constexpr. Later commits switch the duplicated code over to them. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.4: BSON writers share one write-pass UTF-8 sanitizer (#5891) write_bson_entry_header and write_bson_string re-derived the error-handler policy differently. Both now use sanitize_utf8_for_bson_write, which treats strict like keep (the size pass already validated) and sanitizes replace/ignore into the bytes the size pass measured. No context plumbing is needed. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.1.1: one MessagePack container prefix helper (#5891) Array and object prefixes differed only in the fix marker and the 16/32-bit markers; both now delegate to write_msgpack_container_prefix. Output is byte-identical. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.1.2: one iterative walker for CBOR and MessagePack (#5891) write_cbor_iterative and write_msgpack_iterative (and their *_value_or_push helpers) were identical but for the head, scalar, and key writers. They are now one template over a small policy struct, so everything inlines. BON8 keeps its own walker (open string run, closing marker), noted in a comment. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.1.3, 2.1.8: one UBJSON container start helper, shared frame base (#5891) write_ubjson_start_array/_object are now write_ubjson_start_container<IsArray>, keeping the array-only begin()+1 scan and excessive_valueless rule. ubjson_frame derives from binary_container_frame instead of duplicating it. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.1.4: write_bytes helper for the byte-string write sites (#5891) The 13 oa.write_characters(reinterpret_cast<const CharType*>(x.data()), n) sites now call write_bytes(const void*, size_t); copy_characters sites and the buffer-based writes are untouched. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.1.5: shared object-key writer for CBOR and MessagePack (#5891) write_cbor_key and write_msgpack_key were the same SFINAE pair apart from the string/scalar writers; both now delegate to write_object_key<Walk>. UBJSON key and to_format_length stay as they are. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.10: writer-side format names from input_format_name (#5891) The CBOR/MessagePack/UBJSON/BSON/BJData/BON8 literals in throw_on_discarded and throw_integer_out_of_range calls now come from detail::input_format_name; the texts are identical, and UBJSON vs BJData still follows use_bjdata. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * serializer: constexpr ascii table, hex4_lower, dedupe helpers (#5891) 1.1: build ascii_copyable() at compile time from is_ascii_copyable and test find_ascii_copyable_run against it for every byte. 1.2/2.5.4: use detail::hex4_lower in both \uXXXX writers. 2.1.6: factor the repeated flush/emit blocks in dump_escaped_impl into always-inline helpers. 2.1.7: comment why the escape copy does not use put_buffer. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.6: use string_t-aware to_string/concat in meta() (#5891) Replaces the four std::to_string sites; rendered text unchanged. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.7: use detail::is_primitive in json.hpp switches (#5891) has_no_children, set_parents, max_size and the two diff walkers. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.1: factor insert()/iterator throw helpers (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.2: share swap() overloads via swap_impl (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.4: factor primitive reset and range check in erase()/range ctor (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.6: share push_back array bodies via push_back_array_impl (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.7: route parse/accept/sax_parse through *_impl helpers (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.8: share the vector-returning to_* bodies via to_binary_vector (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.10: merge destroy_string/destroy_binary into destroy_pointer (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.4.1/1.7: exceptions: share what_impl, flatten diagnostics switch (#5891) The four create() bodies build their message through one protected static helper; the diagnostics parent walk handles array/object and ignores all other types. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.4.2/2.4.3/1.7: json_pointer: share slash and escape checks, drop primitive case lists (#5891) The leading-slash and invalid '~' escape checks are shared helpers; the reference-token walks test object and array first and treat everything else as primitive. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.7/2.4.9: iter_impl, iteration_proxy: drop primitive case lists, share compare preamble (#5891) Object and array stay the first branches; everything else is the primitive case. operator== and operator< share the different-container check. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.4.5/2.4.6: from_json: share get_number_value and reserve_if_possible (#5891) The three number cases of get_arithmetic_value and the arithmetic from_json share one helper (boolean stays accepted only by the latter); the array and object reserve overload pairs become one. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.4.8: ordered_map: share erase_impl between the key erase overloads (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.7: json_pointer: flatten walk drops the primitive case list (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.2/1.3/1.5/2.5.1/2.5.2/2.5.5: share helpers in the view code (#5891) - detail::exception_id at every numeric id; throw_* helpers take it - hex_byte, hex4_lower, utf8_shape replace local hex/table code - throw_array_index_out_of_range/_past_end, throw_key_not_found - has_native_float_format replaces the hand-written IEEE check No change of messages, ids or output. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.5: use utf8_shape in get_bon8_string, add exhaustive UTF-8 validator test (#5891) get_bon8_string takes the valid range of the second byte from utf8_shape() (0xF5..0xF7 stay errors, unget behavior unchanged). validate_one_utf8 keeps its if-chain (hot bulk path); the new test checks decode, validate_one_utf8, utf8_shape, json::accept and the BON8 reader against each other for every lead and second byte. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.1: get_cbor_sized_bytes for the CBOR chunk getters (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.2: get_string_with_length for the plain-order string sites (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.3, 2.3.4, 2.3.10: unsupported_key_type, empty_input_message, input_format_name in the readers (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.7, 2.3.9: read_object_key and leave_container_and_check for CBOR/MessagePack/BON8 (#5891) UBJSON keys (different getter signature) and its container tail stay as is. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.5: json_sax_dom_error_state base for the DOM SAX parsers (#5891) Holds errored, allow_exceptions, parse_error() and is_errored(). The scalar handlers stay in each class (parse hot path). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.6: table of control-character messages in the lexer (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.8: finish_input helper for the strict-EOF block in the parser (#5891) sax_parse keeps its if (result) guard and return value. Also adds the clang-tidy NOLINT for the 2.3.6 message table. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.5.7: share parse_array_index between json_pointer and json_view (#5891) Move json_pointer::parse_array_index() and its status enum to namespace detail (json_pointer keeps a forwarding member), and let the json_view pointer resolution use it instead of its own copy of the RFC 6901 index grammar. Both accepted the same tokens with the same outcome. Also update the comment on detail::decode(), which listed four of the five UTF-8 validators and now points to the test that keeps them in sync. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Amalgamate (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.4.5: keep the inlined number cases in from_json(ArithmeticType&) (#5891) Calling get_number_value() from from_json(ArithmeticType&) made GetArray<std::array<int, 1000>> 2.4x slower (it changes the inlining of the std::array conversion), so that overload keeps its own switch, with a comment. get_arithmetic_value() still uses the shared helper. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.5.7: force-inline detail::parse_array_index (#5891) Moving parse_array_index out of json_pointer stopped clang from inlining it into the pointer resolution (JsonPointerAt/citm_catalog +17%, JsonPointerAt/twitter +8%); JSON_HEDLEY_ALWAYS_INLINE restores the baseline. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.5: keep get_bon8_string's own UTF-8 ranges (#5891) Taking the second-byte range from utf8_shape() made BON8 decoding 2-3% slower (FromBinaryShape/scalars, FromBinaryBuffer, alignment-pinned builds), so the byte path keeps its hand-written ranges. The exhaustive validator test still checks that it accepts the same sequences as the other validators. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Amalgamate (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix CI failures: constexpr, NRVO, cppcheck, IWYU (#5891) - json_sax_dom_error_state::is_errored() cannot be constexpr: the class is not a literal type (GCC 4.8, GCC -Wpedantic, Clang 3.5). - to_binary_vector: destroy the vector writer before returning, as the output_vector_sink sets the size of the vector in its destructor. Without NRVO (MSVC Debug in C++14 mode), the vector was moved out before it was resized. - parser::sax_parse: return false instead of result after the early exit (cppcheck identicalConditionAfterEarlyExit). - json.hpp: export detail/exceptions.hpp for IWYU, so json_view.hpp can name exception_id without including a detail header. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review: throw helpers take input_format_t, simpler type checks - throw_on_discarded() and throw_integer_out_of_range() take an input_format_t and call input_format_name() themselves - has_no_children(), set_parents() and max_size() check array, then object, instead of calling is_primitive() first - parse_impl() chains like accept_impl(); sax_parse_impl() uses if/else - std::array for the control character messages; negation<> in write_object_key(); clarify the is_primitive() comment Signed-off-by: Niels Lohmann <mail@nlohmann.me> * json_view: use exception_id in the value() overloads Signed-off-by: Niels Lohmann <mail@nlohmann.me> * json_sax: assert the invariant of place_container for the static analyzer Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
277 lines
9.0 KiB
C++
277 lines
9.0 KiB
C++
// __ _____ _____ _____
|
|
// __| | __| | | | JSON for Modern C++
|
|
// | | |__ | | | | | | version 3.12.0
|
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
//
|
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#pragma once
|
|
|
|
#include <array> // array
|
|
#include <cmath> // isnan, ldexp, trunc
|
|
#include <cstddef> // size_t
|
|
#include <cstdint> // uint8_t
|
|
#include <limits> // numeric_limits
|
|
#include <string> // string
|
|
|
|
#include <nlohmann/detail/macro_scope.hpp>
|
|
#if JSON_HAS_THREE_WAY_COMPARISON
|
|
#include <compare> // partial_ordering
|
|
#endif
|
|
|
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
namespace detail
|
|
{
|
|
|
|
///////////////////////////
|
|
// JSON type enumeration //
|
|
///////////////////////////
|
|
|
|
/*!
|
|
@brief the JSON type enumeration
|
|
|
|
This enumeration collects the different JSON types. It is internally used to
|
|
distinguish the stored values, and the functions @ref basic_json::is_null(),
|
|
@ref basic_json::is_object(), @ref basic_json::is_array(),
|
|
@ref basic_json::is_string(), @ref basic_json::is_boolean(),
|
|
@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
|
|
@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
|
|
@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
|
|
@ref basic_json::is_structured() rely on it.
|
|
|
|
@note There are three enumeration entries (number_integer, number_unsigned, and
|
|
number_float), because the library distinguishes these three types for numbers:
|
|
@ref basic_json::number_unsigned_t is used for unsigned integers,
|
|
@ref basic_json::number_integer_t is used for signed integers, and
|
|
@ref basic_json::number_float_t is used for floating-point numbers or to
|
|
approximate integers which do not fit in the limits of their respective type.
|
|
|
|
@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON
|
|
value with the default value for a given type
|
|
|
|
@since version 1.0.0
|
|
*/
|
|
enum class value_t : std::uint8_t
|
|
{
|
|
null, ///< null value
|
|
object, ///< object (unordered set of name/value pairs)
|
|
array, ///< array (ordered collection of values)
|
|
string, ///< string value
|
|
boolean, ///< boolean value
|
|
number_integer, ///< number value (signed integer)
|
|
number_unsigned, ///< number value (unsigned integer)
|
|
number_float, ///< number value (floating-point)
|
|
binary, ///< binary array (ordered collection of bytes)
|
|
discarded ///< discarded by the parser callback function
|
|
};
|
|
|
|
/*!
|
|
@brief the name of a JSON type, as returned by basic_json::type_name()
|
|
|
|
Used in exception messages; also by code that reports types without a
|
|
basic_json value at hand (such as the zero-copy view).
|
|
*/
|
|
inline const char* value_type_name(const value_t t) noexcept
|
|
{
|
|
switch (t)
|
|
{
|
|
case value_t::null:
|
|
return "null";
|
|
case value_t::object:
|
|
return "object";
|
|
case value_t::array:
|
|
return "array";
|
|
case value_t::string:
|
|
return "string";
|
|
case value_t::boolean:
|
|
return "boolean";
|
|
case value_t::binary:
|
|
return "binary";
|
|
case value_t::discarded:
|
|
return "discarded";
|
|
case value_t::number_integer:
|
|
case value_t::number_unsigned:
|
|
case value_t::number_float:
|
|
return "number";
|
|
default:
|
|
return "invalid";
|
|
}
|
|
}
|
|
|
|
/*!
|
|
@brief comparison operator for JSON types
|
|
|
|
Returns an ordering that is similar to Python:
|
|
- order: null < boolean < number < object < array < string < binary
|
|
- furthermore, each type is not smaller than itself
|
|
- discarded values are not comparable
|
|
- binary is represented as a b"" string in python and directly comparable to a
|
|
string; however, making a binary array directly comparable with a string would
|
|
be surprising behavior in a JSON file.
|
|
|
|
@since version 1.0.0
|
|
*/
|
|
#if JSON_HAS_THREE_WAY_COMPARISON
|
|
inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD*
|
|
#else
|
|
inline bool operator<(const value_t lhs, const value_t rhs) noexcept
|
|
#endif
|
|
{
|
|
static constexpr std::array<std::uint8_t, 9> order = {{
|
|
0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
|
|
1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,
|
|
6 /* binary */
|
|
}
|
|
};
|
|
|
|
const auto l_index = static_cast<std::size_t>(lhs);
|
|
const auto r_index = static_cast<std::size_t>(rhs);
|
|
#if JSON_HAS_THREE_WAY_COMPARISON
|
|
if (l_index < order.size() && r_index < order.size())
|
|
{
|
|
return order[l_index] <=> order[r_index]; // *NOPAD*
|
|
}
|
|
return std::partial_ordering::unordered;
|
|
#else
|
|
return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];
|
|
#endif
|
|
}
|
|
|
|
// GCC selects the built-in operator< over an operator rewritten from
|
|
// a user-defined spaceship operator
|
|
// Clang, MSVC, and ICC select the rewritten candidate
|
|
// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200)
|
|
#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__)
|
|
inline bool operator<(const value_t lhs, const value_t rhs) noexcept
|
|
{
|
|
return std::is_lt(lhs <=> rhs); // *NOPAD*
|
|
}
|
|
#endif
|
|
|
|
/*!
|
|
@brief whether a value of the given type owns heap memory
|
|
|
|
Strings, binaries, objects, and arrays are stored as pointers to allocated
|
|
values; all other types are stored directly in the union. Every enumerator is
|
|
listed so that -Wswitch-enum flags this function when a new type is added.
|
|
*/
|
|
inline bool owns_memory(const value_t t) noexcept
|
|
{
|
|
switch (t)
|
|
{
|
|
case value_t::object:
|
|
case value_t::array:
|
|
case value_t::string:
|
|
case value_t::binary:
|
|
return true;
|
|
|
|
case value_t::null:
|
|
case value_t::boolean:
|
|
case value_t::number_integer:
|
|
case value_t::number_unsigned:
|
|
case value_t::number_float:
|
|
case value_t::discarded:
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
@brief whether a value of the given type is neither an object nor an array
|
|
|
|
Unlike basic_json::is_primitive(), this includes value_t::discarded: it names
|
|
the types that iterators walk with a primitive_iterator_t (a single value, or no
|
|
value for null and discarded) and that JSON Pointer resolution and the
|
|
serializer treat as leaves. Every enumerator is listed so that -Wswitch-enum
|
|
flags this function when a new type is added.
|
|
*/
|
|
inline bool is_primitive(const value_t t) noexcept
|
|
{
|
|
switch (t)
|
|
{
|
|
case value_t::object:
|
|
case value_t::array:
|
|
return false;
|
|
|
|
case value_t::null:
|
|
case value_t::string:
|
|
case value_t::boolean:
|
|
case value_t::number_integer:
|
|
case value_t::number_unsigned:
|
|
case value_t::number_float:
|
|
case value_t::binary:
|
|
case value_t::discarded:
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
@brief compare an integer with a floating point number without precision loss
|
|
|
|
Widening the integer to the floating point type loses precision beyond the
|
|
float's mantissa, which makes equality intransitive: both 2^63-2 and 2^63-1
|
|
round to 2^63, so each compares equal to that float while differing from each
|
|
other. Ordering built on that is not a strict weak ordering, so sorting such
|
|
values, or using them as keys in an ordered container, is undefined behavior.
|
|
|
|
Returns a value to be compared against zero with the original operator, which
|
|
reproduces the exact ordering. A NaN operand is returned as is, so comparing it
|
|
against zero keeps NaN's semantics: false for the relational operators and
|
|
unordered for `<=>`.
|
|
*/
|
|
template<typename IntegerType, typename FloatType>
|
|
FloatType compare_integer_with_float(const IntegerType i, const FloatType f) noexcept
|
|
{
|
|
const auto ordered = [](int c) noexcept
|
|
{
|
|
return static_cast<FloatType>(c);
|
|
};
|
|
|
|
if (std::isnan(f))
|
|
{
|
|
return f;
|
|
}
|
|
|
|
// values of IntegerType lie in [-bound, bound) when signed and in
|
|
// [0, bound) when unsigned; digits excludes the sign bit, so bound is a
|
|
// power of two that the float represents exactly; the signedness comes
|
|
// from numeric_limits as well, because std::is_signed is false for class
|
|
// types such as 128-bit or multiprecision integers
|
|
using limits = std::numeric_limits<IntegerType>;
|
|
const FloatType bound = std::ldexp(static_cast<FloatType>(1), limits::digits);
|
|
if (f >= bound)
|
|
{
|
|
return ordered(-1);
|
|
}
|
|
if (limits::is_signed ? (f < -bound) : (f < static_cast<FloatType>(0)))
|
|
{
|
|
return ordered(1);
|
|
}
|
|
|
|
// f is now within the integer's range, so truncating it is exact
|
|
const FloatType truncated = std::trunc(f);
|
|
const auto as_integer = static_cast<IntegerType>(truncated);
|
|
if (i != as_integer)
|
|
{
|
|
return ordered(i < as_integer ? -1 : 1);
|
|
}
|
|
|
|
// the integer parts agree, so any fractional part decides
|
|
const FloatType fraction = f - truncated;
|
|
if (fraction > static_cast<FloatType>(0))
|
|
{
|
|
return ordered(-1);
|
|
}
|
|
if (fraction < static_cast<FloatType>(0))
|
|
{
|
|
return ordered(1);
|
|
}
|
|
return ordered(0);
|
|
}
|
|
|
|
} // namespace detail
|
|
NLOHMANN_JSON_NAMESPACE_END
|