diff --git a/CMakeLists.txt b/CMakeLists.txt index 3463cd16..cc660c24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,6 @@ cmake_minimum_required(VERSION 3.0) # define the project project(nlohmann_json VERSION 2.1.1 LANGUAGES CXX) -enable_testing() - option(JSON_BuildTests "Build the unit tests" ON) # define project variables @@ -26,6 +24,7 @@ target_include_directories(${JSON_TARGET_NAME} INTERFACE # create and configure the unit test target if(JSON_BuildTests) + enable_testing() add_subdirectory(test) endif() diff --git a/doc/images/scanner.png b/doc/images/scanner.png deleted file mode 100644 index 9c39ef0a..00000000 Binary files a/doc/images/scanner.png and /dev/null differ diff --git a/src/json.hpp b/src/json.hpp index 20c2f618..307fddb3 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -119,36 +119,35 @@ template<typename = void, typename = void> struct adl_serializer; // forward declaration of basic_json (required to split the class) -template <template <typename U, typename V, typename... Args> class ObjectType = - std::map, - template <typename U, typename... Args> class ArrayType = std::vector, - class StringType = std::string, class BooleanType = bool, - class NumberIntegerType = std::int64_t, - class NumberUnsignedType = std::uint64_t, - class NumberFloatType = double, - template <typename U> class AllocatorType = std::allocator, - template <typename T, typename SFINAE = void> class JSONSerializer = - adl_serializer> +template<template<typename U, typename V, typename... Args> class ObjectType = + std::map, + template<typename U, typename... Args> class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template<typename U> class AllocatorType = std::allocator, + template<typename T, typename SFINAE = void> class JSONSerializer = + adl_serializer> class basic_json; // Ugly macros to avoid uglier copy-paste when specializing basic_json // This is only temporary and will be removed in 3.0 -#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ - template <template <typename, typename, typename...> class ObjectType, \ - template <typename, typename...> class ArrayType, \ - class StringType, class BooleanType, class NumberIntegerType, \ - class NumberUnsignedType, class NumberFloatType, \ - template <typename> class AllocatorType, \ - template <typename, typename = void> class JSONSerializer> +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template<template<typename, typename, typename...> class ObjectType, \ + template<typename, typename...> class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template<typename> class AllocatorType, \ + template<typename, typename = void> class JSONSerializer> -#define NLOHMANN_BASIC_JSON_TPL \ - basic_json<ObjectType, ArrayType, StringType, BooleanType, \ - NumberIntegerType, NumberUnsignedType, NumberFloatType, \ +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json<ObjectType, ArrayType, StringType, BooleanType, \ + NumberIntegerType, NumberUnsignedType, NumberFloatType, \ AllocatorType, JSONSerializer> - /*! @brief unnamed namespace with internal helper functions @@ -188,8 +187,7 @@ class exception : public std::exception const int id; protected: - exception(int id_, const char* what_arg) - : id(id_), m(what_arg) {} + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} static std::string name(const std::string& ename, int id) { @@ -341,8 +339,7 @@ class type_error : public exception } private: - type_error(int id_, const char* what_arg) - : exception(id_, what_arg) {} + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} }; /*! @@ -371,8 +368,7 @@ class out_of_range : public exception } private: - out_of_range(int id_, const char* what_arg) - : exception(id_, what_arg) {} + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} }; /*! @@ -397,8 +393,7 @@ class other_error : public exception } private: - other_error(int id_, const char* what_arg) - : exception(id_, what_arg) {} + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} }; @@ -433,15 +428,15 @@ value with the default value for a given type */ enum class value_t : 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) - discarded ///< discarded by the the parser callback function + 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) + discarded ///< discarded by the the parser callback function }; /*! @@ -482,9 +477,10 @@ inline bool operator<(const value_t lhs, const value_t rhs) noexcept // helpers // ///////////// -template <typename> struct is_basic_json : std::false_type {}; +template<typename> struct is_basic_json : std::false_type {}; -NLOHMANN_BASIC_JSON_TPL_DECLARATION struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {}; +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {}; // alias templates to reduce boilerplate template<bool B, typename T = void> @@ -495,7 +491,7 @@ using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::typ // implementation of C++14 index_sequence and affiliates // source: https://stackoverflow.com/a/32223343 -template <std::size_t... Ints> +template<std::size_t... Ints> struct index_sequence { using type = index_sequence; @@ -506,19 +502,19 @@ struct index_sequence } }; -template <class Sequence1, class Sequence2> +template<class Sequence1, class Sequence2> struct merge_and_renumber; -template <std::size_t... I1, std::size_t... I2> +template<std::size_t... I1, std::size_t... I2> struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>> : index_sequence < I1..., (sizeof...(I1) + I2)... > - { }; + {}; -template <std::size_t N> +template<std::size_t N> struct make_index_sequence : merge_and_renumber < typename make_index_sequence < N / 2 >::type, typename make_index_sequence < N - N / 2 >::type > -{ }; +{}; template<> struct make_index_sequence<0> : index_sequence<> { }; template<> struct make_index_sequence<1> : index_sequence<0> { }; @@ -692,8 +688,7 @@ struct external_constructor<value_t::object> template<typename BasicJsonType, typename CompatibleObjectType, enable_if_t<not std::is_same<CompatibleObjectType, - typename BasicJsonType::object_t>::value, - int> = 0> + typename BasicJsonType::object_t>::value, int> = 0> static void construct(BasicJsonType& j, const CompatibleObjectType& obj) { using std::begin; @@ -746,10 +741,8 @@ template<class RealType, class CompatibleObjectType> struct is_compatible_object_type_impl<true, RealType, CompatibleObjectType> { static constexpr auto value = - std::is_constructible<typename RealType::key_type, - typename CompatibleObjectType::key_type>::value and - std::is_constructible<typename RealType::mapped_type, - typename CompatibleObjectType::mapped_type>::value; + std::is_constructible<typename RealType::key_type, typename CompatibleObjectType::key_type>::value and + std::is_constructible<typename RealType::mapped_type, typename CompatibleObjectType::mapped_type>::value; }; template<class BasicJsonType, class CompatibleObjectType> @@ -796,8 +789,7 @@ struct is_compatible_integer_type_impl<true, RealIntegerType, CompatibleNumberIn using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>; static constexpr auto value = - std::is_constructible<RealIntegerType, - CompatibleNumberIntegerType>::value and + std::is_constructible<RealIntegerType, CompatibleNumberIntegerType>::value and CompatibleLimits::is_integer and RealLimits::is_signed == CompatibleLimits::is_signed; }; @@ -959,28 +951,28 @@ void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) external_constructor<value_t::object>::construct(j, std::move(obj)); } -template <typename BasicJsonType, typename T, std::size_t N, - enable_if_t<not std::is_constructible< - typename BasicJsonType::string_t, T (&)[N]>::value, - int> = 0> +template<typename BasicJsonType, typename T, std::size_t N, + enable_if_t<not std::is_constructible< + typename BasicJsonType::string_t, T (&)[N]>::value, + int> = 0> void to_json(BasicJsonType& j, T (&arr)[N]) { external_constructor<value_t::array>::construct(j, arr); } -template <typename BasicJsonType, typename... Args> +template<typename BasicJsonType, typename... Args> void to_json(BasicJsonType& j, const std::pair<Args...>& p) { j = {p.first, p.second}; } -template <typename BasicJsonType, typename Tuple, std::size_t... Idx> +template<typename BasicJsonType, typename Tuple, std::size_t... Idx> void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...>) { j = {std::get<Idx>(t)...}; } -template <typename BasicJsonType, typename... Args> +template<typename BasicJsonType, typename... Args> void to_json(BasicJsonType& j, const std::tuple<Args...>& t) { to_json_tuple_impl(j, t, index_sequence_for<Args...> {}); @@ -1002,25 +994,22 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) { case value_t::number_unsigned: { - val = static_cast<ArithmeticType>( - *j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>()); + val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>()); break; } case value_t::number_integer: { - val = static_cast<ArithmeticType>( - *j.template get_ptr<const typename BasicJsonType::number_integer_t*>()); + val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>()); break; } case value_t::number_float: { - val = static_cast<ArithmeticType>( - *j.template get_ptr<const typename BasicJsonType::number_float_t*>()); + val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>()); break; } default: { - JSON_THROW(type_error::create(302, "type must be number, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); } } } @@ -1028,9 +1017,9 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) template<typename BasicJsonType> void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { - if (not j.is_boolean()) + if (JSON_UNLIKELY(not j.is_boolean())) { - JSON_THROW(type_error::create(302, "type must be boolean, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); } b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>(); } @@ -1038,9 +1027,9 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) template<typename BasicJsonType> void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { - if (not j.is_string()) + if (JSON_UNLIKELY(not j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); } s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); } @@ -1075,9 +1064,9 @@ void from_json(const BasicJsonType& j, EnumType& e) template<typename BasicJsonType> void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr) { - if (not j.is_array()) + if (JSON_UNLIKELY(not j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); } arr = *j.template get_ptr<const typename BasicJsonType::array_t*>(); } @@ -1087,9 +1076,9 @@ template<typename BasicJsonType, typename T, typename Allocator, enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0> void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l) { - if (not j.is_array()) + if (JSON_UNLIKELY(not j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); } for (auto it = j.rbegin(), end = j.rend(); it != end; ++it) @@ -1101,7 +1090,6 @@ void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l) template<typename BasicJsonType, typename CompatibleArrayType> void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0> /*unused*/) { - using std::begin; using std::end; std::transform(j.begin(), j.end(), @@ -1119,7 +1107,6 @@ auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, prio arr.reserve(std::declval<typename CompatibleArrayType::size_type>()), void()) { - using std::begin; using std::end; arr.reserve(j.size()); @@ -1132,7 +1119,7 @@ auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, prio }); } -template <typename BasicJsonType, typename T, std::size_t N> +template<typename BasicJsonType, typename T, std::size_t N> void from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr, priority_tag<2> /*unused*/) { for (std::size_t i = 0; i < N; ++i) @@ -1147,9 +1134,9 @@ template<typename BasicJsonType, typename CompatibleArrayType, not std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value, int> = 0> void from_json(const BasicJsonType& j, CompatibleArrayType& arr) { - if (not j.is_array()) + if (JSON_UNLIKELY(not j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); } from_json_array_impl(j, arr, priority_tag<2> {}); @@ -1159,24 +1146,19 @@ template<typename BasicJsonType, typename CompatibleObjectType, enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0> void from_json(const BasicJsonType& j, CompatibleObjectType& obj) { - if (not j.is_object()) + if (JSON_UNLIKELY(not j.is_object())) { - JSON_THROW(type_error::create(302, "type must be object, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); } auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>(); - using std::begin; - using std::end; using value_type = typename CompatibleObjectType::value_type; std::transform( inner_object->begin(), inner_object->end(), std::inserter(obj, obj.begin()), [](typename BasicJsonType::object_t::value_type const & p) { - return value_type( - p.first, - p.second - .template get<typename CompatibleObjectType::mapped_type>()); + return value_type(p.first, p.second.template get<typename CompatibleObjectType::mapped_type>()); }); } @@ -1218,24 +1200,24 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } default: { - JSON_THROW(type_error::create(302, "type must be number, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); } } } -template <typename BasicJsonType, typename... Args> +template<typename BasicJsonType, typename... Args> void from_json(const BasicJsonType& j, std::pair<Args...>& p) { p = {j.at(0), j.at(1)}; } -template <typename BasicJsonType, typename Tuple, std::size_t... Idx> +template<typename BasicJsonType, typename Tuple, std::size_t... Idx> void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...>) { t = std::make_tuple(j.at(Idx)...); } -template <typename BasicJsonType, typename... Args> +template<typename BasicJsonType, typename... Args> void from_json(const BasicJsonType& j, std::tuple<Args...>& t) { from_json_tuple_impl(j, t, index_sequence_for<Args...> {}); @@ -1320,7 +1302,7 @@ struct input_adapter_protocol using input_adapter_t = std::shared_ptr<input_adapter_protocol>; /// input adapter for cached stream input -template<std::size_t N> +template<std::size_t BufferSize> class cached_input_stream_adapter : public input_adapter_protocol { public: @@ -1341,9 +1323,9 @@ class cached_input_stream_adapter : public input_adapter_protocol { // clear stream flags is.clear(); - // We initially read a lot of characters into the buffer, and we may not - // have processed all of them. Therefore, we need to "rewind" the stream - // after the last processed char. + // We initially read a lot of characters into the buffer, and we may + // not have processed all of them. Therefore, we need to "rewind" the + // stream after the last processed char. is.seekg(start_position); is.ignore(static_cast<std::streamsize>(processed_chars)); // clear stream flags @@ -1424,14 +1406,14 @@ class cached_input_stream_adapter : public input_adapter_protocol const std::streampos start_position; /// internal buffer - std::array<char, N> buffer{{}}; + std::array<char, BufferSize> buffer{{}}; }; /// input adapter for buffer input class input_buffer_adapter : public input_adapter_protocol { public: - input_buffer_adapter(const char* b, std::size_t l) + input_buffer_adapter(const char* b, const std::size_t l) : cursor(b), limit(b + l), start(b) { // skip byte order mark @@ -1485,30 +1467,36 @@ class input_adapter : ia(std::make_shared<cached_input_stream_adapter<16384>>(i)) {} /// input adapter for buffer - input_adapter(const char* b, std::size_t l) - : ia(std::make_shared<input_buffer_adapter>(b, l)) {} + template<typename CharT, + typename std::enable_if< + std::is_pointer<CharT>::value and + std::is_integral< + typename std::remove_pointer<CharT>::type>::value and + sizeof(typename std::remove_pointer<CharT>::type) == 1, + int>::type = 0> + input_adapter(CharT b, std::size_t l) + : ia(std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(b), l)) {} // derived support /// input adapter for string literal - template <typename CharT, - typename std::enable_if< - std::is_pointer<CharT>::value and - std::is_integral< - typename std::remove_pointer<CharT>::type>::value and - sizeof(typename std::remove_pointer<CharT>::type) == 1, - int>::type = 0> + template<typename CharT, + typename std::enable_if< + std::is_pointer<CharT>::value and + std::is_integral< + typename std::remove_pointer<CharT>::type>::value and + sizeof(typename std::remove_pointer<CharT>::type) == 1, + int>::type = 0> input_adapter(CharT b) : input_adapter(reinterpret_cast<const char*>(b), std::strlen(reinterpret_cast<const char*>(b))) {} /// input adapter for iterator range with contiguous storage - template <class IteratorType, - typename std::enable_if< - std::is_same<typename std::iterator_traits< - IteratorType>::iterator_category, - std::random_access_iterator_tag>::value, - int>::type = 0> + template<class IteratorType, + typename std::enable_if< + std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, + std::random_access_iterator_tag>::value, + int>::type = 0> input_adapter(IteratorType first, IteratorType last) { // assertion to check that the iterator range is indeed contiguous, @@ -1534,13 +1522,13 @@ class input_adapter } else { - // the address of first cannot be used - use nullptr + // the address of first cannot be used: use nullptr ia = std::make_shared<input_buffer_adapter>(nullptr, len); } } /// input adapter for array - template <class T, std::size_t N> + template<class T, std::size_t N> input_adapter(T (&array)[N]) : input_adapter(std::begin(array), std::end(array)) {} @@ -1550,9 +1538,7 @@ class input_adapter typename std::enable_if < not std::is_pointer<ContiguousContainer>::value and std::is_base_of<std::random_access_iterator_tag, - typename std::iterator_traits<decltype(std::begin( - std::declval<ContiguousContainer const>()))>:: - iterator_category>::value, + typename std::iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value, int >::type = 0 > input_adapter(const ContiguousContainer& c) : input_adapter(std::begin(c), std::end(c)) {} @@ -1576,7 +1562,7 @@ class input_adapter This class organizes the lexical analysis during JSON deserialization. */ -template <typename BasicJsonType> +template<typename BasicJsonType> class lexer { using number_integer_t = typename BasicJsonType::number_integer_t; @@ -1587,27 +1573,23 @@ class lexer /// token types for the parser enum class token_type { - uninitialized, ///< indicating the scanner is uninitialized - literal_true, ///< the `true` literal - literal_false, ///< the `false` literal - literal_null, ///< the `null` literal - value_string, ///< a string -- use get_string() for actual value - value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for - ///actual value - value_integer, ///< a signed integer -- use get_number_integer() for actual - ///value - value_float, ///< an floating point number -- use get_number_float() for - ///actual value - begin_array, ///< the character for array begin `[` - begin_object, ///< the character for object begin `{` - end_array, ///< the character for array end `]` - end_object, ///< the character for object end `}` - name_separator, ///< the name separator `:` - value_separator, ///< the value separator `,` - parse_error, ///< indicating a parse error - end_of_input, ///< indicating the end of the input buffer - literal_or_value ///< a literal or the begin of a value (only for - ///diagnostics) + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value + value_integer, ///< a signed integer -- use get_number_integer() for actual value + value_float, ///< an floating point number -- use get_number_float() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input, ///< indicating the end of the input buffer + literal_or_value ///< a literal or the begin of a value (only for diagnostics) }; /// return name of values of type token_type (only used for errors) @@ -1682,8 +1664,17 @@ class lexer /*! @brief get codepoint from 4 hex characters following `\u` - @return codepoint or -1 in case of an error (e.g. EOF or non-hex - character) + For input "\u c1 c2 c3 c4" the codepoint is: + (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 + = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) + + Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' + must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The + conversion is done by subtracting the offset (0x30, 0x37, and 0x57) + between the ASCII value of the character and the desired integer value. + + @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or + non-hex character) */ int get_codepoint() { @@ -1691,256 +1682,70 @@ class lexer assert(current == 'u'); int codepoint = 0; - // byte 1: \uXxxx - switch (get()) + for (int factor = 12; factor >= 0; factor -= 4) { - case '0': - break; - case '1': - codepoint += 0x1000; - break; - case '2': - codepoint += 0x2000; - break; - case '3': - codepoint += 0x3000; - break; - case '4': - codepoint += 0x4000; - break; - case '5': - codepoint += 0x5000; - break; - case '6': - codepoint += 0x6000; - break; - case '7': - codepoint += 0x7000; - break; - case '8': - codepoint += 0x8000; - break; - case '9': - codepoint += 0x9000; - break; - case 'A': - case 'a': - codepoint += 0xa000; - break; - case 'B': - case 'b': - codepoint += 0xb000; - break; - case 'C': - case 'c': - codepoint += 0xc000; - break; - case 'D': - case 'd': - codepoint += 0xd000; - break; - case 'E': - case 'e': - codepoint += 0xe000; - break; - case 'F': - case 'f': - codepoint += 0xf000; - break; - default: - return -1; - } - - // byte 2: \uxXxx - switch (get()) - { - case '0': - break; - case '1': - codepoint += 0x0100; - break; - case '2': - codepoint += 0x0200; - break; - case '3': - codepoint += 0x0300; - break; - case '4': - codepoint += 0x0400; - break; - case '5': - codepoint += 0x0500; - break; - case '6': - codepoint += 0x0600; - break; - case '7': - codepoint += 0x0700; - break; - case '8': - codepoint += 0x0800; - break; - case '9': - codepoint += 0x0900; - break; - case 'A': - case 'a': - codepoint += 0x0a00; - break; - case 'B': - case 'b': - codepoint += 0x0b00; - break; - case 'C': - case 'c': - codepoint += 0x0c00; - break; - case 'D': - case 'd': - codepoint += 0x0d00; - break; - case 'E': - case 'e': - codepoint += 0x0e00; - break; - case 'F': - case 'f': - codepoint += 0x0f00; - break; - default: - return -1; - } - - // byte 3: \uxxXx - switch (get()) - { - case '0': - break; - case '1': - codepoint += 0x0010; - break; - case '2': - codepoint += 0x0020; - break; - case '3': - codepoint += 0x0030; - break; - case '4': - codepoint += 0x0040; - break; - case '5': - codepoint += 0x0050; - break; - case '6': - codepoint += 0x0060; - break; - case '7': - codepoint += 0x0070; - break; - case '8': - codepoint += 0x0080; - break; - case '9': - codepoint += 0x0090; - break; - case 'A': - case 'a': - codepoint += 0x00a0; - break; - case 'B': - case 'b': - codepoint += 0x00b0; - break; - case 'C': - case 'c': - codepoint += 0x00c0; - break; - case 'D': - case 'd': - codepoint += 0x00d0; - break; - case 'E': - case 'e': - codepoint += 0x00e0; - break; - case 'F': - case 'f': - codepoint += 0x00f0; - break; - default: - return -1; - } - - // byte 4: \uxxxX - switch (get()) - { - case '0': - break; - case '1': - codepoint += 0x0001; - break; - case '2': - codepoint += 0x0002; - break; - case '3': - codepoint += 0x0003; - break; - case '4': - codepoint += 0x0004; - break; - case '5': - codepoint += 0x0005; - break; - case '6': - codepoint += 0x0006; - break; - case '7': - codepoint += 0x0007; - break; - case '8': - codepoint += 0x0008; - break; - case '9': - codepoint += 0x0009; - break; - case 'A': - case 'a': - codepoint += 0x000a; - break; - case 'B': - case 'b': - codepoint += 0x000b; - break; - case 'C': - case 'c': - codepoint += 0x000c; - break; - case 'D': - case 'd': - codepoint += 0x000d; - break; - case 'E': - case 'e': - codepoint += 0x000e; - break; - case 'F': - case 'f': - codepoint += 0x000f; - break; - default: + get(); + + if (current >= '0' and current <= '9') + { + codepoint += ((current - 0x30) << factor); + } + else if (current >= 'A' and current <= 'F') + { + codepoint += ((current - 0x37) << factor); + } + else if (current >= 'a' and current <= 'f') + { + codepoint += ((current - 0x57) << factor); + } + else + { return -1; + } } + assert(0x0000 <= codepoint and codepoint <= 0xFFFF); return codepoint; } + /*! + @brief check if the next byte(s) are inside a given range + + Adds the current byte and, for each passed range, reads a new byte and + checks if it is inside the range. If a violation was detected, set up an + error message and return false. Otherwise, return true. + + @return true iff no range violation was detected + */ + bool next_byte_in_range(std::initializer_list<int> ranges) + { + assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); + add(current); + + for (auto range = ranges.begin(); range != ranges.end(); ++range) + { + get(); + if (JSON_LIKELY(*range <= current and current <= *(++range))) + { + add(current); + } + else + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; + } + } + + return true; + } + /*! @brief scan a string literal This function scans a string according to Sect. 7 of RFC 7159. While - scanning, bytes are escaped and copied into buffer yytext. Then the function - returns successfully, yytext is null-terminated and yylen contains the - number of bytes in the string. + scanning, bytes are escaped and copied into buffer yytext. Then the + function returns successfully, yytext is null-terminated and yylen + contains the number of bytes in the string. @return token_type::value_string if string could be successfully scanned, token_type::parse_error otherwise @@ -2019,12 +1824,11 @@ class lexer case 'u': { int codepoint; - int codepoint1 = get_codepoint(); + const int codepoint1 = get_codepoint(); if (JSON_UNLIKELY(codepoint1 == -1)) { - error_message = - "invalid string: '\\u' must be followed by 4 hex digits"; + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; return token_type::parse_error; } @@ -2292,36 +2096,21 @@ class lexer case 0xde: case 0xdf: { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) + if (JSON_UNLIKELY(not next_byte_in_range({0x80, 0xBF}))) { - add(current); - continue; + return token_type::parse_error; } - - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; + break; } // U+0800..U+0FFF: bytes E0 A0..BF 80..BF case 0xe0: { - add(current); - get(); - if (JSON_LIKELY(0xa0 <= current and current <= 0xbf)) + if (JSON_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } + return token_type::parse_error; } - - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; + break; } // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF @@ -2341,66 +2130,31 @@ class lexer case 0xee: case 0xef: { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) + if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } + return token_type::parse_error; } - - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; + break; } // U+D000..U+D7FF: bytes ED 80..9F 80..BF case 0xed: { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0x9f)) + if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } + return token_type::parse_error; } - - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; + break; } // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF case 0xf0: { - add(current); - get(); - if (JSON_LIKELY(0x90 <= current and current <= 0xbf)) + if (JSON_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } - } + return token_type::parse_error; } - - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; + break; } // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF @@ -2408,51 +2162,21 @@ class lexer case 0xf2: case 0xf3: { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) + if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } - } + return token_type::parse_error; } - - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; + break; } // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF case 0xf4: { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0x8f)) + if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - get(); - if (JSON_LIKELY(0x80 <= current and current <= 0xbf)) - { - add(current); - continue; - } - } + return token_type::parse_error; } - - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; + break; } // remaining bytes (80..C1 and F5..FF) are ill-formed @@ -2485,34 +2209,23 @@ class lexer This function scans a string according to Sect. 6 of RFC 7159. - The function is realized with a deterministic finite state machine - derived from the grammar described in RFC 7159. Starting in state - "init", the input is read and used to determined the next state. Only - state "done" accepts the number. State "error" is a trap state to model - errors. In the table below, "anything" means any character but the ones - listed before. + The function is realized with a deterministic finite state machine derived + from the grammar described in RFC 7159. Starting in state "init", the + input is read and used to determined the next state. Only state "done" + accepts the number. State "error" is a trap state to model errors. In the + table below, "anything" means any character but the ones listed before. - state | 0 | 1-9 | e E | + | - | . | - anything + state | 0 | 1-9 | e E | + | - | . | anything ---------|----------|----------|----------|---------|---------|----------|----------- - init | zero | any1 | [error] | [error] | minus | [error] | - [error] - minus | zero | any1 | [error] | [error] | [error] | [error] | - [error] - zero | done | done | exponent | done | done | decimal1 | - done - any1 | any1 | any1 | exponent | done | done | decimal1 | - done - decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | - [error] - decimal2 | decimal2 | decimal2 | exponent | done | done | done | - done - exponent | any2 | any2 | [error] | sign | sign | [error] | - [error] - sign | any2 | any2 | [error] | [error] | [error] | [error] | - [error] - any2 | any2 | any2 | done | done | done | done | - done + init | zero | any1 | [error] | [error] | minus | [error] | [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | [error] + zero | done | done | exponent | done | done | decimal1 | done + any1 | any1 | any1 | exponent | done | done | decimal1 | done + decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | done + exponent | any2 | any2 | [error] | sign | sign | [error] | [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] + any2 | any2 | any2 | done | done | done | done | done The state machine is realized with one label per state (prefixed with "scan_number_") and `goto` statements between them. The state machine @@ -2815,8 +2528,8 @@ scan_number_any2: } scan_number_done: - // unget the character after the number (we only read it to know - // that we are done scanning a number) + // unget the character after the number (we only read it to know that + // we are done scanning a number) --chars_read; next_unget = true; @@ -2862,8 +2575,8 @@ scan_number_done: } } - // this code is reached if we parse a floating-point number or if - // an integer conversion above failed + // this code is reached if we parse a floating-point number or if an + // integer conversion above failed strtof(value_float, yytext.data(), nullptr); return token_type::value_float; } @@ -2946,8 +2659,8 @@ scan_number_done: /// return string value const std::string get_string() { - // yytext cannot be returned as char*, because it may contain a - // null byte (parsed as "\u0000") + // yytext cannot be returned as char*, because it may contain a null + // byte (parsed as "\u0000") return std::string(yytext.data(), yylen); } @@ -3011,8 +2724,7 @@ scan_number_done: { get(); } - while (current == ' ' or current == '\t' or current == '\n' or - current == '\r'); + while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); switch (current) { @@ -3106,13 +2818,12 @@ scan_number_done: This class implements a recursive decent parser. */ -template <typename BasicJsonType> +template<typename BasicJsonType> class parser { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; - using lexer_t = lexer<BasicJsonType>; using token_type = typename lexer_t::token_type; @@ -3138,8 +2849,11 @@ class parser /// a parser reading from an input adapter explicit parser(detail::input_adapter_t adapter, - const parser_callback_t cb = nullptr) - : callback(cb), m_lexer(adapter) {} + const parser_callback_t cb = nullptr, + const bool allow_exceptions_ = true) + : callback(cb), m_lexer(adapter), + allow_exceptions(allow_exceptions_) + {} /*! @brief public parser interface @@ -3159,12 +2873,20 @@ class parser parse_internal(true, result); result.assert_invariant(); + // in strict mode, input must be completely read if (strict) { get_token(); expect(token_type::end_of_input); } + // in case of an error, return discarded value + if (errored) + { + result = value_t::discarded; + return; + } + // set top-level value to null if it was discarded by the callback // function if (result.is_discarded()) @@ -3206,6 +2928,9 @@ class parser */ void parse_internal(bool keep, BasicJsonType& result) { + // never parse after a parse error was detected + assert(not errored); + // start with a discarded value if (not result.is_discarded()) { @@ -3239,12 +2964,16 @@ class parser } // parse values + std::string key; BasicJsonType value; while (true) { // store key - expect(token_type::value_string); - const auto key = m_lexer.get_string(); + if (not expect(token_type::value_string)) + { + return; + } + key = m_lexer.get_string(); bool keep_tag = false; if (keep) @@ -3262,16 +2991,25 @@ class parser // parse separator (:) get_token(); - expect(token_type::name_separator); + if (not expect(token_type::name_separator)) + { + return; + } // parse and add value get_token(); value.m_value.destroy(value.m_type); value.m_type = value_t::discarded; parse_internal(keep, value); + + if (JSON_UNLIKELY(errored)) + { + return; + } + if (keep and keep_tag and not value.is_discarded()) { - result[key] = std::move(value); + result.m_value.object->operator[](std::move(key)) = std::move(value); } // comma -> next value @@ -3283,7 +3021,10 @@ class parser } // closing } - expect(token_type::end_object); + if (not expect(token_type::end_object)) + { + return; + } break; } @@ -3326,9 +3067,15 @@ class parser value.m_value.destroy(value.m_type); value.m_type = value_t::discarded; parse_internal(keep, value); + + if (JSON_UNLIKELY(errored)) + { + return; + } + if (keep and not value.is_discarded()) { - result.push_back(std::move(value)); + result.m_value.array->push_back(std::move(value)); } // comma -> next value @@ -3340,7 +3087,10 @@ class parser } // closing ] - expect(token_type::end_array); + if (not expect(token_type::end_array)) + { + return; + } break; } @@ -3401,9 +3151,15 @@ class parser // throw in case of infinity or NAN if (JSON_UNLIKELY(not std::isfinite(result.m_value.number_float))) { - JSON_THROW(out_of_range::create(406, "number overflow parsing '" + - m_lexer.get_token_string() + - "'")); + if (allow_exceptions) + { + JSON_THROW(out_of_range::create(406, "number overflow parsing '" + + m_lexer.get_token_string() + "'")); + } + else + { + expect(token_type::uninitialized); + } } break; } @@ -3411,14 +3167,20 @@ class parser case token_type::parse_error: { // using "uninitialized" to avoid "expected" message - expect(token_type::uninitialized); + if (not expect(token_type::uninitialized)) + { + return; + } break; // LCOV_EXCL_LINE } default: { // the last token was unexpected; we expected a value - expect(token_type::literal_or_value); + if (not expect(token_type::literal_or_value)) + { + return; + } break; // LCOV_EXCL_LINE } } @@ -3432,9 +3194,8 @@ class parser /*! @brief the acutal acceptor - @invariant 1. The last token is not yet processed. Therefore, the - caller of this function must make sure a token has - been read. + @invariant 1. The last token is not yet processed. Therefore, the caller + of this function must make sure a token has been read. 2. When this function returns, the last token is processed. That is, the last read character was already considered. @@ -3524,10 +3285,15 @@ class parser } } + case token_type::value_float: + { + // reject infinity or NAN + return std::isfinite(m_lexer.get_number_float()); + } + case token_type::literal_false: case token_type::literal_null: case token_type::literal_true: - case token_type::value_float: case token_type::value_integer: case token_type::value_string: case token_type::value_unsigned: @@ -3552,14 +3318,23 @@ class parser /*! @throw parse_error.101 if expected token did not occur */ - void expect(token_type t) + bool expect(token_type t) { if (JSON_UNLIKELY(t != last_token)) { errored = true; expected = t; - throw_exception(); + if (allow_exceptions) + { + throw_exception(); + } + else + { + return false; + } } + + return true; } [[noreturn]] void throw_exception() const @@ -3596,6 +3371,8 @@ class parser bool errored = false; /// possible reason for the syntax error token_type expected = token_type::uninitialized; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; }; /////////////// @@ -3616,10 +3393,11 @@ class primitive_iterator_t public: using difference_type = std::ptrdiff_t; - difference_type get_value() const noexcept + constexpr difference_type get_value() const noexcept { return m_it; } + /// set iterator to a defined beginning void set_begin() noexcept { @@ -3644,38 +3422,32 @@ class primitive_iterator_t return (m_it == end_value); } - friend constexpr bool operator==(primitive_iterator_t lhs, - primitive_iterator_t rhs) noexcept + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { - return lhs.m_it == rhs.m_it; + return (lhs.m_it == rhs.m_it); } - friend constexpr bool operator!=(primitive_iterator_t lhs, - primitive_iterator_t rhs) noexcept + friend constexpr bool operator!=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { - return !(lhs == rhs); + return not(lhs == rhs); } - friend constexpr bool operator<(primitive_iterator_t lhs, - primitive_iterator_t rhs) noexcept + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it < rhs.m_it; } - friend constexpr bool operator<=(primitive_iterator_t lhs, - primitive_iterator_t rhs) noexcept + friend constexpr bool operator<=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it <= rhs.m_it; } - friend constexpr bool operator>(primitive_iterator_t lhs, - primitive_iterator_t rhs) noexcept + friend constexpr bool operator>(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it > rhs.m_it; } - friend constexpr bool operator>=(primitive_iterator_t lhs, - primitive_iterator_t rhs) noexcept + friend constexpr bool operator>=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it >= rhs.m_it; } @@ -3687,8 +3459,7 @@ class primitive_iterator_t return result; } - friend constexpr difference_type - operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it - rhs.m_it; } @@ -3747,11 +3518,10 @@ class primitive_iterator_t /*! @brief an iterator value -@note This structure could easily be a union, but MSVC currently does not -allow unions members with complex constructors, see -https://github.com/nlohmann/json/pull/105. +@note This structure could easily be a union, but MSVC currently does not allow +unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. */ -template <typename BasicJsonType> struct internal_iterator +template<typename BasicJsonType> struct internal_iterator { /// iterator for JSON objects typename BasicJsonType::object_t::iterator object_iterator {}; @@ -3761,7 +3531,7 @@ template <typename BasicJsonType> struct internal_iterator primitive_iterator_t primitive_iterator {}; }; -template <typename IteratorType> class iteration_proxy; +template<typename IteratorType> class iteration_proxy; /*! @brief a template for a random access iterator for the @ref basic_json class @@ -3782,7 +3552,7 @@ This class implements a both iterators (iterator and const_iterator) for the @since version 1.0.0, simplified in version 2.0.9 */ -template <typename BasicJsonType> +template<typename BasicJsonType> class iter_impl : public std::iterator<std::random_access_iterator_tag, BasicJsonType> { /// allow basic_json to access private members @@ -3849,13 +3619,12 @@ class iter_impl : public std::iterator<std::random_access_iterator_tag, BasicJso } /*! - @note The conventional copy constructor and copy assignment are - implicitly defined. - Combined with the following converting constructor and assignment, - they support: copy from iterator to iterator, - copy from const iterator to const iterator, - and conversion from iterator to const iterator. - However conversion from const iterator to iterator is not defined. + @note The conventional copy constructor and copy assignment are implicitly + defined. Combined with the following converting constructor and + assignment, they support: (1) copy from iterator to iterator, (2) + copy from const iterator to const iterator, and (3) conversion from + iterator to const iterator. However conversion from const iterator + to iterator is not defined. */ /*! @@ -3864,8 +3633,7 @@ class iter_impl : public std::iterator<std::random_access_iterator_tag, BasicJso @note It is not checked whether @a other is initialized. */ iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept - : m_object(other.m_object), - m_it(other.m_it) {} + : m_object(other.m_object), m_it(other.m_it) {} /*! @brief converting assignment @@ -3978,7 +3746,7 @@ class iter_impl : public std::iterator<std::random_access_iterator_tag, BasicJso default: { - if (m_it.primitive_iterator.is_begin()) + if (JSON_LIKELY(m_it.primitive_iterator.is_begin())) { return *m_object; } @@ -4012,7 +3780,7 @@ class iter_impl : public std::iterator<std::random_access_iterator_tag, BasicJso default: { - if (m_it.primitive_iterator.is_begin()) + if (JSON_LIKELY(m_it.primitive_iterator.is_begin())) { return m_object; } @@ -4115,10 +3883,9 @@ class iter_impl : public std::iterator<std::random_access_iterator_tag, BasicJso bool operator==(const iter_impl& other) const { // if objects are not the same, the comparison is undefined - if (m_object != other.m_object) + if (JSON_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create( - 212, "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); } assert(m_object != nullptr); @@ -4158,10 +3925,9 @@ class iter_impl : public std::iterator<std::random_access_iterator_tag, BasicJso bool operator<(const iter_impl& other) const { // if objects are not the same, the comparison is undefined - if (m_object != other.m_object) + if (JSON_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create( - 212, "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); } assert(m_object != nullptr); @@ -4170,8 +3936,7 @@ class iter_impl : public std::iterator<std::random_access_iterator_tag, BasicJso { case value_t::object: { - JSON_THROW(invalid_iterator::create( - 213, "cannot compare order of object iterators")); + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); } case value_t::array: @@ -4225,8 +3990,7 @@ class iter_impl : public std::iterator<std::random_access_iterator_tag, BasicJso { case value_t::object: { - JSON_THROW(invalid_iterator::create( - 209, "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); } case value_t::array: @@ -4299,8 +4063,7 @@ class iter_impl : public std::iterator<std::random_access_iterator_tag, BasicJso { case value_t::object: { - JSON_THROW(invalid_iterator::create( - 209, "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); } case value_t::array: @@ -4327,8 +4090,7 @@ class iter_impl : public std::iterator<std::random_access_iterator_tag, BasicJso { case value_t::object: { - JSON_THROW(invalid_iterator::create( - 208, "cannot use operator[] for object iterators")); + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); } case value_t::array: @@ -4343,7 +4105,7 @@ class iter_impl : public std::iterator<std::random_access_iterator_tag, BasicJso default: { - if (m_it.primitive_iterator.get_value() == -n) + if (JSON_LIKELY(m_it.primitive_iterator.get_value() == -n)) { return *m_object; } @@ -4361,13 +4123,12 @@ class iter_impl : public std::iterator<std::random_access_iterator_tag, BasicJso { assert(m_object != nullptr); - if (m_object->is_object()) + if (JSON_LIKELY(m_object->is_object())) { return m_it.object_iterator->first; } - JSON_THROW(invalid_iterator::create( - 207, "cannot use key() for non-object iterators")); + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); } /*! @@ -4387,7 +4148,7 @@ class iter_impl : public std::iterator<std::random_access_iterator_tag, BasicJso }; /// proxy class for the iterator_wrapper functions -template <typename IteratorType> class iteration_proxy +template<typename IteratorType> class iteration_proxy { private: /// helper class for iteration @@ -4418,7 +4179,7 @@ template <typename IteratorType> class iteration_proxy } /// inequality operator (needed for range-based for) - bool operator!=(const iteration_proxy_internal& o) const + bool operator!=(const iteration_proxy_internal& o) const noexcept { return anchor != o.anchor; } @@ -4496,7 +4257,7 @@ create @ref const_reverse_iterator). @since version 1.0.0 */ -template <typename Base> +template<typename Base> class json_reverse_iterator : public std::reverse_iterator<Base> { public: @@ -4507,8 +4268,7 @@ class json_reverse_iterator : public std::reverse_iterator<Base> using reference = typename Base::reference; /// create reverse iterator from iterator - json_reverse_iterator( - const typename base_iterator::iterator_type& it) noexcept + json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept : base_iterator(it) {} /// create reverse iterator from base class @@ -4588,21 +4348,20 @@ class json_reverse_iterator : public std::reverse_iterator<Base> ///////////////////// /// abstract output adapter interface -template <typename CharType> class output_adapter +template<typename CharType> struct output_adapter_protocol { - public: virtual void write_character(CharType c) = 0; virtual void write_characters(const CharType* s, std::size_t length) = 0; - virtual ~output_adapter() = default; + virtual ~output_adapter_protocol() = default; }; /// a type to simplify interfaces -template <typename CharType> -using output_adapter_t = std::shared_ptr<output_adapter<CharType>>; +template<typename CharType> +using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>; /// output adapter for byte vectors -template <typename CharType> -class output_vector_adapter : public output_adapter<CharType> +template<typename CharType> +class output_vector_adapter : public output_adapter_protocol<CharType> { public: explicit output_vector_adapter(std::vector<CharType>& vec) : v(vec) {} @@ -4622,8 +4381,8 @@ class output_vector_adapter : public output_adapter<CharType> }; /// output adapter for output streams -template <typename CharType> -class output_stream_adapter : public output_adapter<CharType> +template<typename CharType> +class output_stream_adapter : public output_adapter_protocol<CharType> { public: explicit output_stream_adapter(std::basic_ostream<CharType>& s) : stream(s) {} @@ -4643,11 +4402,11 @@ class output_stream_adapter : public output_adapter<CharType> }; /// output adapter for basic_string -template <typename CharType> -class output_string_adapter : public output_adapter<CharType> +template<typename CharType> +class output_string_adapter : public output_adapter_protocol<CharType> { public: - explicit output_string_adapter(std::string& s) : str(s) {} + explicit output_string_adapter(std::basic_string<CharType>& s) : str(s) {} void write_character(CharType c) override { @@ -4663,23 +4422,26 @@ class output_string_adapter : public output_adapter<CharType> std::basic_string<CharType>& str; }; -template <typename CharType> struct output_adapter_factory +template<typename CharType> +class output_adapter { - static std::shared_ptr<output_adapter<CharType>> - create(std::vector<CharType>& vec) + public: + output_adapter(std::vector<CharType>& vec) + : oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {} + + output_adapter(std::basic_ostream<CharType>& s) + : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {} + + output_adapter(std::basic_string<CharType>& s) + : oa(std::make_shared<output_string_adapter<CharType>>(s)) {} + + operator output_adapter_t<CharType>() { - return std::make_shared<output_vector_adapter<CharType>>(vec); + return oa; } - static std::shared_ptr<output_adapter<CharType>> create(std::ostream& s) - { - return std::make_shared<output_stream_adapter<CharType>>(s); - } - - static std::shared_ptr<output_adapter<CharType>> create(std::string& s) - { - return std::make_shared<output_string_adapter<CharType>>(s); - } + private: + output_adapter_t<CharType> oa = nullptr; }; ////////////////////////////// @@ -4689,7 +4451,7 @@ template <typename CharType> struct output_adapter_factory /*! @brief deserialization of CBOR and MessagePack values */ -template <typename BasicJsonType> +template<typename BasicJsonType> class binary_reader { using number_integer_t = typename BasicJsonType::number_integer_t; @@ -4701,8 +4463,7 @@ class binary_reader @param[in] adapter input adapter to read from */ - explicit binary_reader(input_adapter_t adapter) - : ia(adapter), is_little_endian(little_endianess()) + explicit binary_reader(input_adapter_t adapter) : ia(adapter) { assert(ia); } @@ -5526,9 +5287,8 @@ class binary_reader { std::stringstream ss; ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create( - 112, chars_read, - "error reading MessagePack; last byte: 0x" + ss.str())); + JSON_THROW(parse_error::create(112, chars_read, + "error reading MessagePack; last byte: 0x" + ss.str())); } } } @@ -5572,10 +5332,9 @@ class binary_reader bytes in CBOR and MessagePack are stored in network order (big endian) and therefore need reordering on little endian systems. - @throw parse_error.110 if input has less than `sizeof(NumberType)` - bytes + @throw parse_error.110 if input has less than `sizeof(NumberType)` bytes */ - template <typename NumberType> NumberType get_number() + template<typename NumberType> NumberType get_number() { // step 1: read input into array with system's byte order std::array<uint8_t, sizeof(NumberType)> vec; @@ -5713,8 +5472,7 @@ class binary_reader { std::stringstream ss; ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create( - 113, chars_read, "expected a CBOR string; last byte: 0x" + ss.str())); + JSON_THROW(parse_error::create(113, chars_read, "expected a CBOR string; last byte: 0x" + ss.str())); } } } @@ -5796,9 +5554,8 @@ class binary_reader { std::stringstream ss; ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create( - 113, chars_read, - "expected a MessagePack string; last byte: 0x" + ss.str())); + JSON_THROW(parse_error::create(113, chars_read, + "expected a MessagePack string; last byte: 0x" + ss.str())); } } } @@ -5826,13 +5583,13 @@ class binary_reader std::size_t chars_read = 0; /// whether we can assume little endianess - const bool is_little_endian = true; + const bool is_little_endian = little_endianess(); }; /*! @brief serialization to CBOR and MessagePack values */ -template <typename BasicJsonType> +template<typename BasicJsonType, typename CharType> class binary_writer { public: @@ -5841,8 +5598,7 @@ class binary_writer @param[in] adapter output adapter to write to */ - explicit binary_writer(output_adapter_t<uint8_t> adapter) - : is_little_endian(binary_reader<BasicJsonType>::little_endianess()), oa(adapter) + explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter) { assert(oa); } @@ -5856,13 +5612,15 @@ class binary_writer { case value_t::null: { - oa->write_character(0xf6); + oa->write_character(static_cast<CharType>(0xf6)); break; } case value_t::boolean: { - oa->write_character(j.m_value.boolean ? 0xf5 : 0xf4); + oa->write_character(j.m_value.boolean + ? static_cast<CharType>(0xf5) + : static_cast<CharType>(0xf4)); break; } @@ -5879,22 +5637,22 @@ class binary_writer } else if (j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)()) { - oa->write_character(0x18); + oa->write_character(static_cast<CharType>(0x18)); write_number(static_cast<uint8_t>(j.m_value.number_integer)); } else if (j.m_value.number_integer <= (std::numeric_limits<uint16_t>::max)()) { - oa->write_character(0x19); + oa->write_character(static_cast<CharType>(0x19)); write_number(static_cast<uint16_t>(j.m_value.number_integer)); } else if (j.m_value.number_integer <= (std::numeric_limits<uint32_t>::max)()) { - oa->write_character(0x1a); + oa->write_character(static_cast<CharType>(0x1a)); write_number(static_cast<uint32_t>(j.m_value.number_integer)); } else { - oa->write_character(0x1b); + oa->write_character(static_cast<CharType>(0x1b)); write_number(static_cast<uint64_t>(j.m_value.number_integer)); } } @@ -5909,22 +5667,22 @@ class binary_writer } else if (positive_number <= (std::numeric_limits<uint8_t>::max)()) { - oa->write_character(0x38); + oa->write_character(static_cast<CharType>(0x38)); write_number(static_cast<uint8_t>(positive_number)); } else if (positive_number <= (std::numeric_limits<uint16_t>::max)()) { - oa->write_character(0x39); + oa->write_character(static_cast<CharType>(0x39)); write_number(static_cast<uint16_t>(positive_number)); } else if (positive_number <= (std::numeric_limits<uint32_t>::max)()) { - oa->write_character(0x3a); + oa->write_character(static_cast<CharType>(0x3a)); write_number(static_cast<uint32_t>(positive_number)); } else { - oa->write_character(0x3b); + oa->write_character(static_cast<CharType>(0x3b)); write_number(static_cast<uint64_t>(positive_number)); } } @@ -5939,22 +5697,22 @@ class binary_writer } else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)()) { - oa->write_character(0x18); + oa->write_character(static_cast<CharType>(0x18)); write_number(static_cast<uint8_t>(j.m_value.number_unsigned)); } else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)()) { - oa->write_character(0x19); + oa->write_character(static_cast<CharType>(0x19)); write_number(static_cast<uint16_t>(j.m_value.number_unsigned)); } else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)()) { - oa->write_character(0x1a); + oa->write_character(static_cast<CharType>(0x1a)); write_number(static_cast<uint32_t>(j.m_value.number_unsigned)); } else { - oa->write_character(0x1b); + oa->write_character(static_cast<CharType>(0x1b)); write_number(static_cast<uint64_t>(j.m_value.number_unsigned)); } break; @@ -5963,7 +5721,7 @@ class binary_writer case value_t::number_float: { // Double-Precision Float - oa->write_character(0xfb); + oa->write_character(static_cast<CharType>(0xfb)); write_number(j.m_value.number_float); break; } @@ -5978,30 +5736,30 @@ class binary_writer } else if (N <= 0xff) { - oa->write_character(0x78); + oa->write_character(static_cast<CharType>(0x78)); write_number(static_cast<uint8_t>(N)); } else if (N <= 0xffff) { - oa->write_character(0x79); + oa->write_character(static_cast<CharType>(0x79)); write_number(static_cast<uint16_t>(N)); } else if (N <= 0xffffffff) { - oa->write_character(0x7a); + oa->write_character(static_cast<CharType>(0x7a)); write_number(static_cast<uint32_t>(N)); } // LCOV_EXCL_START else if (N <= 0xffffffffffffffff) { - oa->write_character(0x7b); + oa->write_character(static_cast<CharType>(0x7b)); write_number(static_cast<uint64_t>(N)); } // LCOV_EXCL_STOP // step 2: write the string oa->write_characters( - reinterpret_cast<const uint8_t*>(j.m_value.string->c_str()), + reinterpret_cast<const CharType*>(j.m_value.string->c_str()), j.m_value.string->size()); break; } @@ -6016,23 +5774,23 @@ class binary_writer } else if (N <= 0xff) { - oa->write_character(0x98); + oa->write_character(static_cast<CharType>(0x98)); write_number(static_cast<uint8_t>(N)); } else if (N <= 0xffff) { - oa->write_character(0x99); + oa->write_character(static_cast<CharType>(0x99)); write_number(static_cast<uint16_t>(N)); } else if (N <= 0xffffffff) { - oa->write_character(0x9a); + oa->write_character(static_cast<CharType>(0x9a)); write_number(static_cast<uint32_t>(N)); } // LCOV_EXCL_START else if (N <= 0xffffffffffffffff) { - oa->write_character(0x9b); + oa->write_character(static_cast<CharType>(0x9b)); write_number(static_cast<uint64_t>(N)); } // LCOV_EXCL_STOP @@ -6055,23 +5813,23 @@ class binary_writer } else if (N <= 0xff) { - oa->write_character(0xb8); + oa->write_character(static_cast<CharType>(0xb8)); write_number(static_cast<uint8_t>(N)); } else if (N <= 0xffff) { - oa->write_character(0xb9); + oa->write_character(static_cast<CharType>(0xb9)); write_number(static_cast<uint16_t>(N)); } else if (N <= 0xffffffff) { - oa->write_character(0xba); + oa->write_character(static_cast<CharType>(0xba)); write_number(static_cast<uint32_t>(N)); } // LCOV_EXCL_START else if (N <= 0xffffffffffffffff) { - oa->write_character(0xbb); + oa->write_character(static_cast<CharType>(0xbb)); write_number(static_cast<uint64_t>(N)); } // LCOV_EXCL_STOP @@ -6102,14 +5860,16 @@ class binary_writer case value_t::null: { // nil - oa->write_character(0xc0); + oa->write_character(static_cast<CharType>(0xc0)); break; } case value_t::boolean: { // true and false - oa->write_character(j.m_value.boolean ? 0xc3 : 0xc2); + oa->write_character(j.m_value.boolean + ? static_cast<CharType>(0xc3) + : static_cast<CharType>(0xc2)); break; } @@ -6118,9 +5878,8 @@ class binary_writer if (j.m_value.number_integer >= 0) { // MessagePack does not differentiate between positive - // signed integers and unsigned integers. Therefore, we - // used the code from the value_t::number_unsigned case - // here. + // signed integers and unsigned integers. Therefore, we used + // the code from the value_t::number_unsigned case here. if (j.m_value.number_unsigned < 128) { // positive fixnum @@ -6129,25 +5888,25 @@ class binary_writer else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)()) { // uint 8 - oa->write_character(0xcc); + oa->write_character(static_cast<CharType>(0xcc)); write_number(static_cast<uint8_t>(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)()) { // uint 16 - oa->write_character(0xcd); + oa->write_character(static_cast<CharType>(0xcd)); write_number(static_cast<uint16_t>(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)()) { // uint 32 - oa->write_character(0xce); + oa->write_character(static_cast<CharType>(0xce)); write_number(static_cast<uint32_t>(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)()) { // uint 64 - oa->write_character(0xcf); + oa->write_character(static_cast<CharType>(0xcf)); write_number(static_cast<uint64_t>(j.m_value.number_integer)); } } @@ -6162,28 +5921,28 @@ class binary_writer j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)()) { // int 8 - oa->write_character(0xd0); + oa->write_character(static_cast<CharType>(0xd0)); write_number(static_cast<int8_t>(j.m_value.number_integer)); } else if (j.m_value.number_integer >= (std::numeric_limits<int16_t>::min)() and j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)()) { // int 16 - oa->write_character(0xd1); + oa->write_character(static_cast<CharType>(0xd1)); write_number(static_cast<int16_t>(j.m_value.number_integer)); } else if (j.m_value.number_integer >= (std::numeric_limits<int32_t>::min)() and j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)()) { // int 32 - oa->write_character(0xd2); + oa->write_character(static_cast<CharType>(0xd2)); write_number(static_cast<int32_t>(j.m_value.number_integer)); } else if (j.m_value.number_integer >= (std::numeric_limits<int64_t>::min)() and j.m_value.number_integer <= (std::numeric_limits<int64_t>::max)()) { // int 64 - oa->write_character(0xd3); + oa->write_character(static_cast<CharType>(0xd3)); write_number(static_cast<int64_t>(j.m_value.number_integer)); } } @@ -6200,25 +5959,25 @@ class binary_writer else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)()) { // uint 8 - oa->write_character(0xcc); + oa->write_character(static_cast<CharType>(0xcc)); write_number(static_cast<uint8_t>(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)()) { // uint 16 - oa->write_character(0xcd); + oa->write_character(static_cast<CharType>(0xcd)); write_number(static_cast<uint16_t>(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)()) { // uint 32 - oa->write_character(0xce); + oa->write_character(static_cast<CharType>(0xce)); write_number(static_cast<uint32_t>(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)()) { // uint 64 - oa->write_character(0xcf); + oa->write_character(static_cast<CharType>(0xcf)); write_number(static_cast<uint64_t>(j.m_value.number_integer)); } break; @@ -6227,7 +5986,7 @@ class binary_writer case value_t::number_float: { // float 64 - oa->write_character(0xcb); + oa->write_character(static_cast<CharType>(0xcb)); write_number(j.m_value.number_float); break; } @@ -6244,25 +6003,25 @@ class binary_writer else if (N <= 255) { // str 8 - oa->write_character(0xd9); + oa->write_character(static_cast<CharType>(0xd9)); write_number(static_cast<uint8_t>(N)); } else if (N <= 65535) { // str 16 - oa->write_character(0xda); + oa->write_character(static_cast<CharType>(0xda)); write_number(static_cast<uint16_t>(N)); } else if (N <= 4294967295) { // str 32 - oa->write_character(0xdb); + oa->write_character(static_cast<CharType>(0xdb)); write_number(static_cast<uint32_t>(N)); } // step 2: write the string oa->write_characters( - reinterpret_cast<const uint8_t*>(j.m_value.string->c_str()), + reinterpret_cast<const CharType*>(j.m_value.string->c_str()), j.m_value.string->size()); break; } @@ -6279,13 +6038,13 @@ class binary_writer else if (N <= 0xffff) { // array 16 - oa->write_character(0xdc); + oa->write_character(static_cast<CharType>(0xdc)); write_number(static_cast<uint16_t>(N)); } else if (N <= 0xffffffff) { // array 32 - oa->write_character(0xdd); + oa->write_character(static_cast<CharType>(0xdd)); write_number(static_cast<uint32_t>(N)); } @@ -6309,13 +6068,13 @@ class binary_writer else if (N <= 65535) { // map 16 - oa->write_character(0xde); + oa->write_character(static_cast<CharType>(0xde)); write_number(static_cast<uint16_t>(N)); } else if (N <= 4294967295) { // map 32 - oa->write_character(0xdf); + oa->write_character(static_cast<CharType>(0xdf)); write_number(static_cast<uint32_t>(N)); } @@ -6342,14 +6101,14 @@ class binary_writer @param[in] n number of type @a NumberType @tparam NumberType the type of the number - @note This function needs to respect the system's endianess, because - bytes in CBOR and MessagePack are stored in network order (big - endian) and therefore need reordering on little endian systems. + @note This function needs to respect the system's endianess, because bytes + in CBOR and MessagePack are stored in network order (big endian) and + therefore need reordering on little endian systems. */ - template <typename NumberType> void write_number(NumberType n) + template<typename NumberType> void write_number(NumberType n) { // step 1: write number to array of length NumberType - std::array<uint8_t, sizeof(NumberType)> vec; + std::array<CharType, sizeof(NumberType)> vec; std::memcpy(vec.data(), &n, sizeof(NumberType)); // step 2: write array to output (with possible reordering) @@ -6364,17 +6123,17 @@ class binary_writer private: /// whether we can assume little endianess - const bool is_little_endian = true; + const bool is_little_endian = binary_reader<BasicJsonType>::little_endianess(); /// the output - output_adapter_t<uint8_t> oa = nullptr; + output_adapter_t<CharType> oa = nullptr; }; /////////////////// // serialization // /////////////////// -template <typename BasicJsonType> +template<typename BasicJsonType> class serializer { using string_t = typename BasicJsonType::string_t; @@ -6399,10 +6158,10 @@ class serializer /*! @brief internal implementation of the serialization function - This function is called by the public member function dump and - organizes the serialization internally. The indentation level is - propagated as additional parameter. In case of arrays and objects, the - function is called recursively. + This function is called by the public member function dump and organizes + the serialization internally. The indentation level is propagated as + additional parameter. In case of arrays and objects, the function is + called recursively. - strings and object keys are escaped using `escape_string()` - integer numbers are converted implicitly via `operator<<` @@ -6505,9 +6264,9 @@ class serializer // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - if (indent_string.size() < new_indent) + if (JSON_UNLIKELY(indent_string.size() < new_indent)) { - indent_string.resize(new_indent, ' '); + indent_string.resize(indent_string.size() * 2, ' '); } // first n-1 elements @@ -6699,10 +6458,10 @@ class serializer if (bytes == 3) { - // codepoints that need 4 bytes (i.e., 3 - // additional bytes) in UTF-8 needs a surrogate - // pair when \u escaping is used: - // from 4 bytes to \uxxxx\uxxxx (12 bytes) + // codepoints that need 4 bytes (i.e., 3 additional + // bytes) in UTF-8 needs a surrogate pair when \u + // escaping is used: from 4 bytes to \uxxxx\uxxxx + // (12 bytes) res += (12 - bytes - 1); } else @@ -6776,13 +6535,14 @@ class serializer /*! @brief dump escaped string - Escape a string by replacing certain special characters by a sequence - of an escape character (backslash) and another character and other - control characters by a sequence of "\u" followed by a four-digit hex + Escape a string by replacing certain special characters by a sequence of an + escape character (backslash) and another character and other control + characters by a sequence of "\u" followed by a four-digit hex representation. The escaped string is written to output stream @a o. @param[in] s the string to escape - @param[in] ensure_ascii whether to escape non-ASCII characters with \uXXXX sequences + @param[in] ensure_ascii whether to escape non-ASCII characters with + \uXXXX sequences @complexity Linear in the length of string @a s. */ @@ -6983,8 +6743,8 @@ class serializer /*! @brief dump a floating-point number - Dump a given floating-point number to output stream @a o. Works - internally with @a number_buffer. + Dump a given floating-point number to output stream @a o. Works internally + with @a number_buffer. @param[in] x floating-point number to dump */ @@ -7052,7 +6812,7 @@ class serializer std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1, [](char c) { - return c == '.' or c == 'e'; + return (c == '.' or c == 'e'); }); if (value_is_int_like) @@ -7274,7 +7034,7 @@ class json_pointer */ std::string pop_back() { - if (is_root()) + if (JSON_UNLIKELY(is_root())) { JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); } @@ -7292,7 +7052,7 @@ class json_pointer json_pointer top() const { - if (is_root()) + if (JSON_UNLIKELY(is_root())) { JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); } @@ -7390,12 +7150,11 @@ class json_pointer } // check if nonempty reference string begins with slash - if (reference_string[0] != '/') + if (JSON_UNLIKELY(reference_string[0] != '/')) { - JSON_THROW(detail::parse_error::create( - 107, 1, - "JSON pointer must be empty or begin with '/' - was: '" + - reference_string + "'")); + JSON_THROW(detail::parse_error::create(107, 1, + "JSON pointer must be empty or begin with '/' - was: '" + + reference_string + "'")); } // extract the reference tokens: @@ -7426,12 +7185,11 @@ class json_pointer assert(reference_token[pos] == '~'); // ~ must be followed by 0 or 1 - if (pos == reference_token.size() - 1 or - (reference_token[pos + 1] != '0' and - reference_token[pos + 1] != '1')) + if (JSON_UNLIKELY(pos == reference_token.size() - 1 or + (reference_token[pos + 1] != '0' and + reference_token[pos + 1] != '1'))) { - JSON_THROW(detail::parse_error::create( - 108, 0, "escape character '~' must be followed with '0' or '1'")); + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); } } @@ -7467,21 +7225,18 @@ class json_pointer {} } - /// escape tilde and slash + /// escape "~"" to "~0" and "/" to "~1" static std::string escape(std::string s) { - // escape "~"" to "~0" and "/" to "~1" replace_substring(s, "~", "~0"); replace_substring(s, "/", "~1"); return s; } - /// unescape tilde and slash + /// unescape "~1" to tilde and "~0" to slash (order is important!) static void unescape(std::string& s) { - // first transform any occurrence of the sequence '~1' to '/' replace_substring(s, "~1", "/"); - // then transform any occurrence of the sequence '~0' to '~' replace_substring(s, "~0", "~"); } @@ -7610,9 +7365,10 @@ class basic_json friend ::nlohmann::json_pointer; friend ::nlohmann::detail::parser<basic_json>; friend ::nlohmann::detail::serializer<basic_json>; - template <typename BasicJsonType> + template<typename BasicJsonType> friend class ::nlohmann::detail::iter_impl; - friend ::nlohmann::detail::binary_writer<basic_json>; + template<typename BasicJsonType, typename CharType> + friend class ::nlohmann::detail::binary_writer; /// workaround type for MSVC using basic_json_t = NLOHMANN_BASIC_JSON_TPL; @@ -7621,19 +7377,19 @@ class basic_json using parser = ::nlohmann::detail::parser<basic_json>; using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; - template <typename BasicJsonType> + template<typename BasicJsonType> using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>; - template <typename BasicJsonType> + template<typename BasicJsonType> using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>; - template <typename Iterator> + template<typename Iterator> using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>; template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>; - template <typename CharType> + template<typename CharType> using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>; using binary_reader = ::nlohmann::detail::binary_reader<basic_json>; - using binary_writer = ::nlohmann::detail::binary_writer<basic_json>; + template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>; using serializer = ::nlohmann::detail::serializer<basic_json>; @@ -8727,7 +8483,7 @@ class basic_json bool is_an_object = std::all_of(init.begin(), init.end(), [](const detail::json_ref<basic_json>& element_ref) { - return element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string(); + return (element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string()); }); // adjust type if type deduction is not wanted @@ -8740,7 +8496,7 @@ class basic_json } // if object is wanted but impossible, throw an exception - if (manual_type == value_t::object and not is_an_object) + if (JSON_UNLIKELY(manual_type == value_t::object and not is_an_object)) { JSON_THROW(type_error::create(301, "cannot create object from initializer list")); } @@ -8925,7 +8681,7 @@ class basic_json assert(last.m_object != nullptr); // make sure iterator fits the current value - if (first.m_object != last.m_object) + if (JSON_UNLIKELY(first.m_object != last.m_object)) { JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); } @@ -8942,7 +8698,8 @@ class basic_json case value_t::number_unsigned: case value_t::string: { - if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) + if (JSON_UNLIKELY(not first.m_it.primitive_iterator.is_begin() + or not last.m_it.primitive_iterator.is_end())) { JSON_THROW(invalid_iterator::create(204, "iterators out of range")); } @@ -9004,7 +8761,7 @@ class basic_json default: { JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + - first.m_object->type_name())); + std::string(first.m_object->type_name()))); } } @@ -9237,7 +8994,7 @@ class basic_json const bool ensure_ascii = false) const { string_t result; - serializer s(detail::output_adapter_factory<char>::create(result), indent_char); + serializer s(detail::output_adapter<char>(result), indent_char); if (indent >= 0) { @@ -9350,7 +9107,7 @@ class basic_json */ constexpr bool is_null() const noexcept { - return m_type == value_t::null; + return (m_type == value_t::null); } /*! @@ -9372,7 +9129,7 @@ class basic_json */ constexpr bool is_boolean() const noexcept { - return m_type == value_t::boolean; + return (m_type == value_t::boolean); } /*! @@ -9431,7 +9188,7 @@ class basic_json */ constexpr bool is_number_integer() const noexcept { - return m_type == value_t::number_integer or m_type == value_t::number_unsigned; + return (m_type == value_t::number_integer or m_type == value_t::number_unsigned); } /*! @@ -9459,7 +9216,7 @@ class basic_json */ constexpr bool is_number_unsigned() const noexcept { - return m_type == value_t::number_unsigned; + return (m_type == value_t::number_unsigned); } /*! @@ -9487,7 +9244,7 @@ class basic_json */ constexpr bool is_number_float() const noexcept { - return m_type == value_t::number_float; + return (m_type == value_t::number_float); } /*! @@ -9509,7 +9266,7 @@ class basic_json */ constexpr bool is_object() const noexcept { - return m_type == value_t::object; + return (m_type == value_t::object); } /*! @@ -9531,7 +9288,7 @@ class basic_json */ constexpr bool is_array() const noexcept { - return m_type == value_t::array; + return (m_type == value_t::array); } /*! @@ -9553,7 +9310,7 @@ class basic_json */ constexpr bool is_string() const noexcept { - return m_type == value_t::string; + return (m_type == value_t::string); } /*! @@ -9580,7 +9337,7 @@ class basic_json */ constexpr bool is_discarded() const noexcept { - return m_type == value_t::discarded; + return (m_type == value_t::discarded); } /*! @@ -9616,12 +9373,12 @@ class basic_json /// get a boolean (explicit) boolean_t get_impl(boolean_t* /*unused*/) const { - if (is_boolean()) + if (JSON_LIKELY(is_boolean())) { return m_value.boolean; } - JSON_THROW(type_error::create(302, "type must be boolean, but is " + type_name())); + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()))); } /// get a pointer to the value (object) @@ -9722,18 +9479,15 @@ class basic_json template<typename ReferenceType, typename ThisType> static ReferenceType get_ref_impl(ThisType& obj) { - // helper type - using PointerType = typename std::add_pointer<ReferenceType>::type; - // delegate the call to get_ptr<>() - auto ptr = obj.template get_ptr<PointerType>(); + auto ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>(); - if (ptr != nullptr) + if (JSON_LIKELY(ptr != nullptr)) { return *ptr; } - JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name())); + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()))); } public: @@ -10129,7 +9883,7 @@ class basic_json reference at(size_type idx) { // at only works for arrays - if (is_array()) + if (JSON_LIKELY(is_array())) { JSON_TRY { @@ -10143,7 +9897,7 @@ class basic_json } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); } } @@ -10176,7 +9930,7 @@ class basic_json const_reference at(size_type idx) const { // at only works for arrays - if (is_array()) + if (JSON_LIKELY(is_array())) { JSON_TRY { @@ -10190,7 +9944,7 @@ class basic_json } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); } } @@ -10227,7 +9981,7 @@ class basic_json reference at(const typename object_t::key_type& key) { // at only works for objects - if (is_object()) + if (JSON_LIKELY(is_object())) { JSON_TRY { @@ -10241,7 +9995,7 @@ class basic_json } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); } } @@ -10278,7 +10032,7 @@ class basic_json const_reference at(const typename object_t::key_type& key) const { // at only works for objects - if (is_object()) + if (JSON_LIKELY(is_object())) { JSON_TRY { @@ -10292,7 +10046,7 @@ class basic_json } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); } } @@ -10332,7 +10086,7 @@ class basic_json } // operator[] only works for arrays - if (is_array()) + if (JSON_LIKELY(is_array())) { // fill up array with null values if given idx is outside range if (idx >= m_value.array->size()) @@ -10345,7 +10099,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); } /*! @@ -10370,12 +10124,12 @@ class basic_json const_reference operator[](size_type idx) const { // const operator[] only works for arrays - if (is_array()) + if (JSON_LIKELY(is_array())) { return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); } /*! @@ -10416,12 +10170,12 @@ class basic_json } // operator[] only works for objects - if (is_object()) + if (JSON_LIKELY(is_object())) { return m_value.object->operator[](key); } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); } /*! @@ -10457,13 +10211,13 @@ class basic_json const_reference operator[](const typename object_t::key_type& key) const { // const operator[] only works for objects - if (is_object()) + if (JSON_LIKELY(is_object())) { assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); } /*! @@ -10505,12 +10259,12 @@ class basic_json } // at only works for objects - if (is_object()) + if (JSON_LIKELY(is_object())) { return m_value.object->operator[](key); } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); } /*! @@ -10547,13 +10301,13 @@ class basic_json const_reference operator[](T* key) const { // at only works for objects - if (is_object()) + if (JSON_LIKELY(is_object())) { assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); } /*! @@ -10609,7 +10363,7 @@ class basic_json ValueType value(const typename object_t::key_type& key, ValueType default_value) const { // at only works for objects - if (is_object()) + if (JSON_LIKELY(is_object())) { // if key is found, return value and given default value otherwise const auto it = find(key); @@ -10622,7 +10376,7 @@ class basic_json } else { - JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } } @@ -10681,7 +10435,7 @@ class basic_json ValueType value(const json_pointer& ptr, ValueType default_value) const { // at only works for objects - if (is_object()) + if (JSON_LIKELY(is_object())) { // if pointer resolves a value, return it or use default value JSON_TRY @@ -10694,7 +10448,7 @@ class basic_json } } - JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } /*! @@ -10845,7 +10599,7 @@ class basic_json IteratorType erase(IteratorType pos) { // make sure iterator fits the current value - if (this != pos.m_object) + if (JSON_UNLIKELY(this != pos.m_object)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } @@ -10860,7 +10614,7 @@ class basic_json case value_t::number_unsigned: case value_t::string: { - if (not pos.m_it.primitive_iterator.is_begin()) + if (JSON_UNLIKELY(not pos.m_it.primitive_iterator.is_begin())) { JSON_THROW(invalid_iterator::create(205, "iterator out of range")); } @@ -10892,7 +10646,7 @@ class basic_json default: { - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); } } @@ -10952,7 +10706,7 @@ class basic_json IteratorType erase(IteratorType first, IteratorType last) { // make sure iterator fits the current value - if (this != first.m_object or this != last.m_object) + if (JSON_UNLIKELY(this != first.m_object or this != last.m_object)) { JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); } @@ -10967,7 +10721,8 @@ class basic_json case value_t::number_unsigned: case value_t::string: { - if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) + if (JSON_LIKELY(not first.m_it.primitive_iterator.is_begin() + or not last.m_it.primitive_iterator.is_end())) { JSON_THROW(invalid_iterator::create(204, "iterators out of range")); } @@ -11001,7 +10756,7 @@ class basic_json default: { - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); } } @@ -11040,12 +10795,12 @@ class basic_json size_type erase(const typename object_t::key_type& key) { // this erase only works for objects - if (is_object()) + if (JSON_LIKELY(is_object())) { return m_value.object->erase(key); } - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); } /*! @@ -11075,9 +10830,9 @@ class basic_json void erase(const size_type idx) { // this erase only works for arrays - if (is_array()) + if (JSON_LIKELY(is_array())) { - if (idx >= size()) + if (JSON_UNLIKELY(idx >= size())) { JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); } @@ -11086,7 +10841,7 @@ class basic_json } else { - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); } } @@ -11800,9 +11555,9 @@ class basic_json void push_back(basic_json&& val) { // push_back only works for null objects or arrays - if (not(is_null() or is_array())) + if (JSON_UNLIKELY(not(is_null() or is_array()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); } // transform null object into an array @@ -11836,9 +11591,9 @@ class basic_json void push_back(const basic_json& val) { // push_back only works for null objects or arrays - if (not(is_null() or is_array())) + if (JSON_UNLIKELY(not(is_null() or is_array()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); } // transform null object into an array @@ -11886,9 +11641,9 @@ class basic_json void push_back(const typename object_t::value_type& val) { // push_back only works for null objects or objects - if (not(is_null() or is_object())) + if (JSON_UNLIKELY(not(is_null() or is_object()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); } // transform null object into an object @@ -11987,9 +11742,9 @@ class basic_json void emplace_back(Args&& ... args) { // emplace_back only works for null objects or arrays - if (not(is_null() or is_array())) + if (JSON_UNLIKELY(not(is_null() or is_array()))) { - JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + type_name())); + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()))); } // transform null object into an array @@ -12035,9 +11790,9 @@ class basic_json std::pair<iterator, bool> emplace(Args&& ... args) { // emplace only works for null objects or arrays - if (not(is_null() or is_object())) + if (JSON_UNLIKELY(not(is_null() or is_object()))) { - JSON_THROW(type_error::create(311, "cannot use emplace() with " + type_name())); + JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()))); } // transform null object into an object @@ -12083,10 +11838,10 @@ class basic_json iterator insert(const_iterator pos, const basic_json& val) { // insert only works for arrays - if (is_array()) + if (JSON_LIKELY(is_array())) { // check if iterator pos fits to this JSON value - if (pos.m_object != this) + if (JSON_UNLIKELY(pos.m_object != this)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } @@ -12097,7 +11852,7 @@ class basic_json return result; } - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); } /*! @@ -12136,10 +11891,10 @@ class basic_json iterator insert(const_iterator pos, size_type cnt, const basic_json& val) { // insert only works for arrays - if (is_array()) + if (JSON_LIKELY(is_array())) { // check if iterator pos fits to this JSON value - if (pos.m_object != this) + if (JSON_UNLIKELY(pos.m_object != this)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } @@ -12150,7 +11905,7 @@ class basic_json return result; } - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); } /*! @@ -12186,24 +11941,24 @@ class basic_json iterator insert(const_iterator pos, const_iterator first, const_iterator last) { // insert only works for arrays - if (not is_array()) + if (JSON_UNLIKELY(not is_array())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); } // check if iterator pos fits to this JSON value - if (pos.m_object != this) + if (JSON_UNLIKELY(pos.m_object != this)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } // check if range iterators belong to the same JSON object - if (first.m_object != last.m_object) + if (JSON_UNLIKELY(first.m_object != last.m_object)) { JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); } - if (first.m_object == this or last.m_object == this) + if (JSON_UNLIKELY(first.m_object == this or last.m_object == this)) { JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); } @@ -12244,13 +11999,13 @@ class basic_json iterator insert(const_iterator pos, initializer_list_t ilist) { // insert only works for arrays - if (not is_array()) + if (JSON_UNLIKELY(not is_array())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); } // check if iterator pos fits to this JSON value - if (pos.m_object != this) + if (JSON_UNLIKELY(pos.m_object != this)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } @@ -12287,19 +12042,20 @@ class basic_json void insert(const_iterator first, const_iterator last) { // insert only works for objects - if (not is_object()) + if (JSON_UNLIKELY(not is_object())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); } // check if range iterators belong to the same JSON object - if (first.m_object != last.m_object) + if (JSON_UNLIKELY(first.m_object != last.m_object)) { JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); } // passed iterators must belong to objects - if (not first.m_object->is_object() or not first.m_object->is_object()) + if (JSON_UNLIKELY(not first.m_object->is_object() + or not first.m_object->is_object())) { JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); } @@ -12359,13 +12115,13 @@ class basic_json void swap(array_t& other) { // swap only works for arrays - if (is_array()) + if (JSON_LIKELY(is_array())) { std::swap(*(m_value.array), other); } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); } } @@ -12392,13 +12148,13 @@ class basic_json void swap(object_t& other) { // swap only works for objects - if (is_object()) + if (JSON_LIKELY(is_object())) { std::swap(*(m_value.object), other); } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); } } @@ -12425,13 +12181,13 @@ class basic_json void swap(string_t& other) { // swap only works for strings - if (is_string()) + if (JSON_LIKELY(is_string())) { std::swap(*(m_value.string), other); } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); } } @@ -12480,11 +12236,11 @@ class basic_json { case value_t::array: { - return *lhs.m_value.array == *rhs.m_value.array; + return (*lhs.m_value.array == *rhs.m_value.array); } case value_t::object: { - return *lhs.m_value.object == *rhs.m_value.object; + return (*lhs.m_value.object == *rhs.m_value.object); } case value_t::null: { @@ -12492,23 +12248,23 @@ class basic_json } case value_t::string: { - return *lhs.m_value.string == *rhs.m_value.string; + return (*lhs.m_value.string == *rhs.m_value.string); } case value_t::boolean: { - return lhs.m_value.boolean == rhs.m_value.boolean; + return (lhs.m_value.boolean == rhs.m_value.boolean); } case value_t::number_integer: { - return lhs.m_value.number_integer == rhs.m_value.number_integer; + return (lhs.m_value.number_integer == rhs.m_value.number_integer); } case value_t::number_unsigned: { - return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + return (lhs.m_value.number_unsigned == rhs.m_value.number_unsigned); } case value_t::number_float: { - return lhs.m_value.number_float == rhs.m_value.number_float; + return (lhs.m_value.number_float == rhs.m_value.number_float); } default: { @@ -12518,27 +12274,27 @@ class basic_json } else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) { - return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float; + return (static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float); } else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) { - return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer); + return (lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer)); } else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) { - return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float; + return (static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float); } else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) { - return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned); + return (lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned)); } else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) { - return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; + return (static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer); } else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) { - return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned); + return (lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned)); } return false; @@ -12912,7 +12668,7 @@ class basic_json o.width(0); // do the actual serialization - serializer s(detail::output_adapter_factory<char>::create(o), o.fill()); + serializer s(detail::output_adapter<char>(o), o.fill()); s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation)); return o; } @@ -13003,10 +12759,11 @@ class basic_json @since version 2.0.3 (contiguous containers) */ static basic_json parse(detail::input_adapter i, - const parser_callback_t cb = nullptr) + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true) { basic_json result; - parser(i, cb).parse(true, result); + parser(i, cb, allow_exceptions).parse(true, result); return result; } @@ -13014,10 +12771,11 @@ class basic_json @copydoc basic_json parse(detail::input_adapter, const parser_callback_t) */ static basic_json parse(detail::input_adapter& i, - const parser_callback_t cb = nullptr) + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true) { basic_json result; - parser(i, cb).parse(true, result); + parser(i, cb, allow_exceptions).parse(true, result); return result; } @@ -13081,10 +12839,11 @@ class basic_json std::random_access_iterator_tag, typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0> static basic_json parse(IteratorType first, IteratorType last, - const parser_callback_t cb = nullptr) + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true) { basic_json result; - parser(detail::input_adapter(first, last), cb).parse(true, result); + parser(detail::input_adapter(first, last), cb, allow_exceptions).parse(true, result); return result; } @@ -13107,8 +12866,7 @@ class basic_json JSON_DEPRECATED friend std::istream& operator<<(basic_json& j, std::istream& i) { - parser(detail::input_adapter(i)).parse(false, j); - return i; + return operator>>(i, j); } /*! @@ -13161,9 +12919,9 @@ class basic_json @liveexample{The following code exemplifies `type_name()` for all JSON types.,type_name} - @since version 1.0.0, public since 2.1.0 + @since version 1.0.0, public since 2.1.0, const char* since 3.0.0 */ - std::string type_name() const + const char* type_name() const { { switch (m_type) @@ -13291,11 +13049,20 @@ class basic_json static std::vector<uint8_t> to_cbor(const basic_json& j) { std::vector<uint8_t> result; - binary_writer bw(detail::output_adapter_factory<uint8_t>::create(result)); - bw.write_cbor(j); + to_cbor(j, result); return result; } + static void to_cbor(const basic_json& j, detail::output_adapter<uint8_t> o) + { + binary_writer<uint8_t>(o).write_cbor(j); + } + + static void to_cbor(const basic_json& j, detail::output_adapter<char> o) + { + binary_writer<char>(o).write_cbor(j); + } + /*! @brief create a MessagePack serialization of a given JSON value @@ -13373,11 +13140,20 @@ class basic_json static std::vector<uint8_t> to_msgpack(const basic_json& j) { std::vector<uint8_t> result; - binary_writer bw(detail::output_adapter_factory<uint8_t>::create(result)); - bw.write_msgpack(j); + to_msgpack(j, result); return result; } + static void to_msgpack(const basic_json& j, detail::output_adapter<uint8_t> o) + { + binary_writer<uint8_t>(o).write_msgpack(j); + } + + static void to_msgpack(const basic_json& j, detail::output_adapter<char> o) + { + binary_writer<char>(o).write_msgpack(j); + } + /*! @brief create a JSON value from a byte vector in CBOR format @@ -13472,6 +13248,11 @@ class basic_json return br.parse_cbor(); } + static basic_json from_cbor(detail::input_adapter i) + { + return binary_reader(i).parse_cbor(); + } + /*! @brief create a JSON value from a byte vector in MessagePack format @@ -13547,6 +13328,11 @@ class basic_json return br.parse_msgpack(); } + static basic_json from_msgpack(detail::input_adapter i) + { + return binary_reader(i).parse_msgpack(); + } + /// @} ////////////////////////// @@ -13901,7 +13687,7 @@ class basic_json else { const auto idx = std::stoi(last_path); - if (static_cast<size_type>(idx) > parent.size()) + if (JSON_UNLIKELY(static_cast<size_type>(idx) > parent.size())) { // avoid undefined behavior JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); @@ -13936,7 +13722,7 @@ class basic_json { // perform range check auto it = parent.find(last_path); - if (it != parent.end()) + if (JSON_LIKELY(it != parent.end())) { parent.erase(it); } @@ -13953,7 +13739,7 @@ class basic_json }; // type check: top level value must be an array - if (not json_patch.is_array()) + if (JSON_UNLIKELY(not json_patch.is_array())) { JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); } @@ -13973,13 +13759,13 @@ class basic_json const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; // check if desired value is present - if (it == val.m_value.object->end()) + if (JSON_UNLIKELY(it == val.m_value.object->end())) { JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'")); } // check if result is of type string - if (string_type and not it->second.is_string()) + if (JSON_UNLIKELY(string_type and not it->second.is_string())) { JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'")); } @@ -13989,7 +13775,7 @@ class basic_json }; // type check: every element of the array must be an object - if (not val.is_object()) + if (JSON_UNLIKELY(not val.is_object())) { JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); } @@ -14062,7 +13848,7 @@ class basic_json } // throw an exception if test fails - if (not success) + if (JSON_UNLIKELY(not success)) { JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump())); } @@ -14114,8 +13900,7 @@ class basic_json @since version 2.0.0 */ - static basic_json diff(const basic_json& source, - const basic_json& target, + static basic_json diff(const basic_json& source, const basic_json& target, const std::string& path = "") { // the patch @@ -14132,9 +13917,7 @@ class basic_json // different types: replace value result.push_back( { - {"op", "replace"}, - {"path", path}, - {"value", target} + {"op", "replace"}, {"path", path}, {"value", target} }); } else @@ -14204,8 +13987,7 @@ class basic_json // found a key that is not in o -> remove it result.push_back(object( { - {"op", "remove"}, - {"path", path + "/" + key} + {"op", "remove"}, {"path", path + "/" + key} })); } } @@ -14219,8 +14001,7 @@ class basic_json const auto key = json_pointer::escape(it.key()); result.push_back( { - {"op", "add"}, - {"path", path + "/" + key}, + {"op", "add"}, {"path", path + "/" + key}, {"value", it.value()} }); } @@ -14234,9 +14015,7 @@ class basic_json // both primitive type: replace value result.push_back( { - {"op", "replace"}, - {"path", path}, - {"value", target} + {"op", "replace"}, {"path", path}, {"value", target} }); break; } @@ -14274,8 +14053,8 @@ json_pointer::get_and_create(NLOHMANN_BASIC_JSON_TPL& j) const using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type; auto result = &j; - // in case no reference tokens exist, return a reference to the - // JSON value j which will be overwritten by a primitive value + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value for (const auto& reference_token : reference_tokens) { switch (result->m_type) @@ -14307,23 +14086,20 @@ json_pointer::get_and_create(NLOHMANN_BASIC_JSON_TPL& j) const // create an entry in the array JSON_TRY { - result = &result->operator[]( - static_cast<size_type>(std::stoi(reference_token))); + result = &result->operator[](static_cast<size_type>(std::stoi(reference_token))); } JSON_CATCH(std::invalid_argument&) { - JSON_THROW(detail::parse_error::create( - 109, 0, "array index '" + reference_token + "' is not a number")); + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } /* - The following code is only reached if there exists a - reference token _and_ the current value is primitive. In - this case, we have an error situation, because primitive - values may only occur as single value; that is, with an - empty list of reference tokens. + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + single value; that is, with an empty list of reference tokens. */ default: { @@ -14353,16 +14129,10 @@ json_pointer::get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const return (x >= '0' and x <= '9'); }); - // change value to array for numbers or "-" or to object - // otherwise - if (nums or reference_token == "-") - { - *ptr = detail::value_t::array; - } - else - { - *ptr = detail::value_t::object; - } + // change value to array for numbers or "-" or to object otherwise + *ptr = (nums or reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; } switch (ptr->m_type) @@ -14377,7 +14147,7 @@ json_pointer::get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const case detail::value_t::array: { // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') + if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) { JSON_THROW(detail::parse_error::create(106, 0, "array index '" + reference_token + @@ -14399,8 +14169,7 @@ json_pointer::get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const } JSON_CATCH(std::invalid_argument&) { - JSON_THROW(detail::parse_error::create( - 109, 0, "array index '" + reference_token + "' is not a number")); + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } } break; @@ -14408,8 +14177,7 @@ json_pointer::get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const default: { - JSON_THROW(detail::out_of_range::create( - 404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -14435,17 +14203,16 @@ json_pointer::get_checked(NLOHMANN_BASIC_JSON_TPL* ptr) const case detail::value_t::array: { - if (reference_token == "-") + if (JSON_UNLIKELY(reference_token == "-")) { // "-" always fails the range check - JSON_THROW(detail::out_of_range::create( - 402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') + if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) { JSON_THROW(detail::parse_error::create(106, 0, "array index '" + reference_token + @@ -14459,16 +14226,14 @@ json_pointer::get_checked(NLOHMANN_BASIC_JSON_TPL* ptr) const } JSON_CATCH(std::invalid_argument&) { - JSON_THROW(detail::parse_error::create( - 109, 0, "array index '" + reference_token + "' is not a number")); + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } default: { - JSON_THROW(detail::out_of_range::create( - 404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -14494,17 +14259,16 @@ json_pointer::get_unchecked(const NLOHMANN_BASIC_JSON_TPL* ptr) const case detail::value_t::array: { - if (reference_token == "-") + if (JSON_UNLIKELY(reference_token == "-")) { // "-" cannot be used for const access - JSON_THROW(detail::out_of_range::create( - 402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') + if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) { JSON_THROW(detail::parse_error::create(106, 0, "array index '" + reference_token + @@ -14519,16 +14283,14 @@ json_pointer::get_unchecked(const NLOHMANN_BASIC_JSON_TPL* ptr) const } JSON_CATCH(std::invalid_argument&) { - JSON_THROW(detail::parse_error::create( - 109, 0, "array index '" + reference_token + "' is not a number")); + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } default: { - JSON_THROW(detail::out_of_range::create( - 404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -14554,17 +14316,16 @@ json_pointer::get_checked(const NLOHMANN_BASIC_JSON_TPL* ptr) const case detail::value_t::array: { - if (reference_token == "-") + if (JSON_UNLIKELY(reference_token == "-")) { // "-" always fails the range check - JSON_THROW(detail::out_of_range::create( - 402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') + if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) { JSON_THROW(detail::parse_error::create(106, 0, "array index '" + reference_token + @@ -14578,16 +14339,14 @@ json_pointer::get_checked(const NLOHMANN_BASIC_JSON_TPL* ptr) const } JSON_CATCH(std::invalid_argument&) { - JSON_THROW(detail::parse_error::create( - 109, 0, "array index '" + reference_token + "' is not a number")); + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } default: { - JSON_THROW(detail::out_of_range::create( - 404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -14633,8 +14392,7 @@ void json_pointer::flatten(const std::string& reference_string, // iterate object and use keys as reference string for (const auto& element : *value.m_value.object) { - flatten(reference_string + "/" + escape(element.first), element.second, - result); + flatten(reference_string + "/" + escape(element.first), element.second, result); } } break; @@ -14653,7 +14411,7 @@ NLOHMANN_BASIC_JSON_TPL_DECLARATION NLOHMANN_BASIC_JSON_TPL json_pointer::unflatten(const NLOHMANN_BASIC_JSON_TPL& value) { - if (not value.is_object()) + if (JSON_UNLIKELY(not value.is_object())) { JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); } @@ -14663,16 +14421,15 @@ json_pointer::unflatten(const NLOHMANN_BASIC_JSON_TPL& value) // iterate the JSON object values for (const auto& element : *value.m_value.object) { - if (not element.second.is_primitive()) + if (JSON_UNLIKELY(not element.second.is_primitive())) { JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); } - // assign value to reference pointed to by JSON pointer; Note - // that if the JSON pointer is "" (i.e., points to the whole - // value), function get_and_create returns a reference to - // result itself. An assignment will then create a primitive - // value. + // assign value to reference pointed to by JSON pointer; Note that if + // the JSON pointer is "" (i.e., points to the whole value), function + // get_and_create returns a reference to result itself. An assignment + // will then create a primitive value. json_pointer(element.first).get_and_create(result) = element.second; } @@ -14681,12 +14438,12 @@ json_pointer::unflatten(const NLOHMANN_BASIC_JSON_TPL& value) inline bool operator==(json_pointer const& lhs, json_pointer const& rhs) noexcept { - return lhs.reference_tokens == rhs.reference_tokens; + return (lhs.reference_tokens == rhs.reference_tokens); } inline bool operator!=(json_pointer const& lhs, json_pointer const& rhs) noexcept { - return !(lhs == rhs); + return not (lhs == rhs); } } // namespace nlohmann @@ -14731,7 +14488,7 @@ struct hash<nlohmann::json> }; /// specialization for std::less<value_t> -template <> +template<> struct less<::nlohmann::detail::value_t> { /*! diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 5ab6a2e2..ae3e39b6 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -1284,6 +1284,25 @@ TEST_CASE("single CBOR roundtrip") // compare parsed JSON values CHECK(j1 == j2); + SECTION("roundtrips") + { + SECTION("std::ostringstream") + { + std::ostringstream ss; + json::to_cbor(j1, ss); + json j3 = json::from_cbor(ss.str()); + CHECK(j1 == j3); + } + + SECTION("std::string") + { + std::string s; + json::to_cbor(j1, s); + json j3 = json::from_cbor(s); + CHECK(j1 == j3); + } + } + // check with different start index packed.insert(packed.begin(), 5, 0xff); CHECK(j1 == json::from_cbor(packed, 5)); @@ -1519,16 +1538,60 @@ TEST_CASE("CBOR roundtrips", "[hide]") std::ifstream f_json(filename); json j1 = json::parse(f_json); - // parse CBOR file - std::ifstream f_cbor(filename + ".cbor", std::ios::binary); - std::vector<uint8_t> packed( - (std::istreambuf_iterator<char>(f_cbor)), - std::istreambuf_iterator<char>()); - json j2; - CHECK_NOTHROW(j2 = json::from_cbor(packed)); + SECTION("std::vector<uint8_t>") + { + // parse CBOR file + std::ifstream f_cbor(filename + ".cbor", std::ios::binary); + std::vector<uint8_t> packed( + (std::istreambuf_iterator<char>(f_cbor)), + std::istreambuf_iterator<char>()); + json j2; + CHECK_NOTHROW(j2 = json::from_cbor(packed)); - // compare parsed JSON values - CHECK(j1 == j2); + // compare parsed JSON values + CHECK(j1 == j2); + } + + SECTION("std::ifstream") + { + // parse CBOR file + std::ifstream f_cbor(filename + ".cbor", std::ios::binary); + json j2; + CHECK_NOTHROW(j2 = json::from_cbor(f_cbor)); + + // compare parsed JSON values + CHECK(j1 == j2); + } + + SECTION("uint8_t* and size") + { + // parse CBOR file + std::ifstream f_cbor(filename + ".cbor", std::ios::binary); + std::vector<uint8_t> packed( + (std::istreambuf_iterator<char>(f_cbor)), + std::istreambuf_iterator<char>()); + json j2; + CHECK_NOTHROW(j2 = json::from_cbor({packed.data(), packed.size()})); + + // compare parsed JSON values + CHECK(j1 == j2); + } + + SECTION("output to output adapters") + { + // parse CBOR file + std::ifstream f_cbor(filename + ".cbor", std::ios::binary); + std::vector<uint8_t> packed( + (std::istreambuf_iterator<char>(f_cbor)), + std::istreambuf_iterator<char>()); + + SECTION("std::vector<uint8_t>") + { + std::vector<uint8_t> vec; + json::to_cbor(j1, vec); + CHECK(vec == packed); + } + } } } } diff --git a/test/src/unit-class_const_iterator.cpp b/test/src/unit-class_const_iterator.cpp index 78179882..573e773b 100644 --- a/test/src/unit-class_const_iterator.cpp +++ b/test/src/unit-class_const_iterator.cpp @@ -182,32 +182,32 @@ TEST_CASE("const_iterator class") { json j(json::value_t::null); json::const_iterator it = j.cbegin(); - CHECK_THROWS_AS(it->type_name(), json::invalid_iterator&); - CHECK_THROWS_WITH(it->type_name(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_AS(std::string(it->type_name()), json::invalid_iterator&); + CHECK_THROWS_WITH(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value"); } SECTION("number") { json j(17); json::const_iterator it = j.cbegin(); - CHECK(it->type_name() == "number"); + CHECK(std::string(it->type_name()) == "number"); it = j.cend(); - CHECK_THROWS_AS(it->type_name(), json::invalid_iterator&); - CHECK_THROWS_WITH(it->type_name(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_AS(std::string(it->type_name()), json::invalid_iterator&); + CHECK_THROWS_WITH(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value"); } SECTION("object") { json j({{"foo", "bar"}}); json::const_iterator it = j.cbegin(); - CHECK(it->type_name() == "string"); + CHECK(std::string(it->type_name()) == "string"); } SECTION("array") { json j({1, 2, 3, 4}); json::const_iterator it = j.cbegin(); - CHECK(it->type_name() == "number"); + CHECK(std::string(it->type_name()) == "number"); } } } diff --git a/test/src/unit-class_iterator.cpp b/test/src/unit-class_iterator.cpp index 7057d967..1ef4a538 100644 --- a/test/src/unit-class_iterator.cpp +++ b/test/src/unit-class_iterator.cpp @@ -166,32 +166,32 @@ TEST_CASE("iterator class") { json j(json::value_t::null); json::iterator it = j.begin(); - CHECK_THROWS_AS(it->type_name(), json::invalid_iterator&); - CHECK_THROWS_WITH(it->type_name(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_AS(std::string(it->type_name()), json::invalid_iterator&); + CHECK_THROWS_WITH(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value"); } SECTION("number") { json j(17); json::iterator it = j.begin(); - CHECK(it->type_name() == "number"); + CHECK(std::string(it->type_name()) == "number"); it = j.end(); - CHECK_THROWS_AS(it->type_name(), json::invalid_iterator&); - CHECK_THROWS_WITH(it->type_name(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_AS(std::string(it->type_name()), json::invalid_iterator&); + CHECK_THROWS_WITH(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value"); } SECTION("object") { json j({{"foo", "bar"}}); json::iterator it = j.begin(); - CHECK(it->type_name() == "string"); + CHECK(std::string(it->type_name()) == "string"); } SECTION("array") { json j({1, 2, 3, 4}); json::iterator it = j.begin(); - CHECK(it->type_name() == "number"); + CHECK(std::string(it->type_name()) == "number"); } } } diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index f23d2c18..f5415552 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -41,12 +41,31 @@ json parser_helper(const std::string& s) { json j; json::parser(nlohmann::detail::input_adapter(s)).parse(true, j); + + // if this line was reached, no exception ocurred + // -> check if result is the same without exceptions + json j_nothrow; + CHECK_NOTHROW(json::parser(nlohmann::detail::input_adapter(s), nullptr, false).parse(true, j_nothrow)); + CHECK(j_nothrow == j); + return j; } bool accept_helper(const std::string& s) { - return json::parser(nlohmann::detail::input_adapter(s)).accept(true); + // 1. parse s without exceptions + json j; + CHECK_NOTHROW(json::parser(nlohmann::detail::input_adapter(s), nullptr, false).parse(true, j)); + const bool ok_noexcept = not j.is_discarded(); + + // 2. accept s + const bool ok_accept = json::parser(nlohmann::detail::input_adapter(s)).accept(true); + + // 3. check if both approaches come to the same result + CHECK(ok_noexcept == ok_accept); + + // 4. return result + return ok_accept; } TEST_CASE("parser class") @@ -590,8 +609,8 @@ TEST_CASE("parser class") SECTION("overflow") { - // overflows during parsing yield an exception, but is accepted anyway - CHECK(accept_helper("1.18973e+4932")); + // overflows during parsing + CHECK(not accept_helper("1.18973e+4932")); } SECTION("invalid numbers") diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp index cd4f9e3f..9a416a84 100644 --- a/test/src/unit-convenience.cpp +++ b/test/src/unit-convenience.cpp @@ -36,7 +36,7 @@ void check_escaped(const char* original, const char* escaped, const bool ensure_ void check_escaped(const char* original, const char* escaped, const bool ensure_ascii) { std::stringstream ss; - json::serializer s(nlohmann::detail::output_adapter_factory<char>::create(ss), ' '); + json::serializer s(nlohmann::detail::output_adapter<char>(ss), ' '); s.dump_escaped(original, ensure_ascii); CHECK(ss.str() == escaped); } @@ -45,15 +45,15 @@ TEST_CASE("convenience functions") { SECTION("type name as string") { - CHECK(json(json::value_t::null).type_name() == "null"); - CHECK(json(json::value_t::object).type_name() == "object"); - CHECK(json(json::value_t::array).type_name() == "array"); - CHECK(json(json::value_t::number_integer).type_name() == "number"); - CHECK(json(json::value_t::number_unsigned).type_name() == "number"); - CHECK(json(json::value_t::number_float).type_name() == "number"); - CHECK(json(json::value_t::boolean).type_name() == "boolean"); - CHECK(json(json::value_t::string).type_name() == "string"); - CHECK(json(json::value_t::discarded).type_name() == "discarded"); + CHECK(std::string(json(json::value_t::null).type_name()) == "null"); + CHECK(std::string(json(json::value_t::object).type_name()) == "object"); + CHECK(std::string(json(json::value_t::array).type_name()) == "array"); + CHECK(std::string(json(json::value_t::number_integer).type_name()) == "number"); + CHECK(std::string(json(json::value_t::number_unsigned).type_name()) == "number"); + CHECK(std::string(json(json::value_t::number_float).type_name()) == "number"); + CHECK(std::string(json(json::value_t::boolean).type_name()) == "boolean"); + CHECK(std::string(json(json::value_t::string).type_name()) == "string"); + CHECK(std::string(json(json::value_t::discarded).type_name()) == "discarded"); } SECTION("string escape") diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 3de8f156..da406391 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -91,14 +91,19 @@ TEST_CASE("deserialization") { SECTION("stream") { - std::stringstream ss1, ss2, ss3; + std::stringstream ss1, ss2, ss3, ss4; ss1 << "[\"foo\",1,2,3,false,{\"one\":1}"; ss2 << "[\"foo\",1,2,3,false,{\"one\":1}"; ss3 << "[\"foo\",1,2,3,false,{\"one\":1}"; + ss4 << "[\"foo\",1,2,3,false,{\"one\":1}"; CHECK_THROWS_AS(json::parse(ss1), json::parse_error&); CHECK_THROWS_WITH(json::parse(ss2), "[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'"); CHECK(not json::accept(ss3)); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(ss1, nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("string") @@ -108,6 +113,10 @@ TEST_CASE("deserialization") CHECK_THROWS_WITH(json::parse(s), "[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'"); CHECK(not json::accept(s)); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(s, nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("operator<<") @@ -260,6 +269,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u'}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("case 2") @@ -267,6 +280,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1'}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("case 3") @@ -274,6 +291,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1', '1', '1', '1', '1', '1', '1', '1'}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("case 4") @@ -281,6 +302,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', 'u', '1', '1', '1', '1', '1', '1', '1', '1', '\\'}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("case 5") @@ -288,6 +313,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'\"', 0x7F, 0xC1}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("case 6") @@ -295,6 +324,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'\"', 0x7F, 0xDF, 0x7F}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("case 7") @@ -302,6 +335,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'\"', 0x7F, 0xDF, 0xC0}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("case 8") @@ -309,6 +346,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'\"', 0x7F, 0xE0, 0x9F}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("case 9") @@ -316,6 +357,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'\"', 0x7F, 0xEF, 0xC0}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("case 10") @@ -323,6 +368,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'\"', 0x7F, 0xED, 0x7F}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("case 11") @@ -330,6 +379,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'\"', 0x7F, 0xF0, 0x8F}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("case 12") @@ -337,6 +390,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'\"', 0x7F, 0xF0, 0xC0}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("case 13") @@ -344,6 +401,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'\"', 0x7F, 0xF3, 0x7F}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("case 14") @@ -351,6 +412,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'\"', 0x7F, 0xF3, 0xC0}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("case 15") @@ -358,6 +423,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'\"', 0x7F, 0xF4, 0x7F}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } SECTION("case 16") @@ -365,6 +434,10 @@ TEST_CASE("deserialization") uint8_t v[] = {'{', '\"', '\"', ':', '1', '1'}; CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(not json::accept(std::begin(v), std::end(v))); + + json j_error; + CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false)); + CHECK(j_error.is_discarded()); } } } diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp index baef3e35..730fc7dd 100644 --- a/test/src/unit-msgpack.cpp +++ b/test/src/unit-msgpack.cpp @@ -1126,6 +1126,25 @@ TEST_CASE("single MessagePack roundtrip") // compare parsed JSON values CHECK(j1 == j2); + SECTION("roundtrips") + { + SECTION("std::ostringstream") + { + std::ostringstream ss; + json::to_msgpack(j1, ss); + json j3 = json::from_msgpack(ss.str()); + CHECK(j1 == j3); + } + + SECTION("std::string") + { + std::string s; + json::to_msgpack(j1, s); + json j3 = json::from_msgpack(s); + CHECK(j1 == j3); + } + } + // check with different start index packed.insert(packed.begin(), 5, 0xff); CHECK(j1 == json::from_msgpack(packed, 5)); @@ -1292,15 +1311,60 @@ TEST_CASE("MessagePack roundtrips", "[hide]") std::ifstream f_json(filename); json j1 = json::parse(f_json); - // parse MessagePack file - std::ifstream f_msgpack(filename + ".msgpack", std::ios::binary); - std::vector<uint8_t> packed((std::istreambuf_iterator<char>(f_msgpack)), - std::istreambuf_iterator<char>()); - json j2; - CHECK_NOTHROW(j2 = json::from_msgpack(packed)); + SECTION("std::vector<uint8_t>") + { + // parse MessagePack file + std::ifstream f_msgpack(filename + ".msgpack", std::ios::binary); + std::vector<uint8_t> packed( + (std::istreambuf_iterator<char>(f_msgpack)), + std::istreambuf_iterator<char>()); + json j2; + CHECK_NOTHROW(j2 = json::from_msgpack(packed)); - // compare parsed JSON values - CHECK(j1 == j2); + // compare parsed JSON values + CHECK(j1 == j2); + } + + SECTION("std::ifstream") + { + // parse MessagePack file + std::ifstream f_msgpack(filename + ".msgpack", std::ios::binary); + json j2; + CHECK_NOTHROW(j2 = json::from_msgpack(f_msgpack)); + + // compare parsed JSON values + CHECK(j1 == j2); + } + + SECTION("uint8_t* and size") + { + // parse MessagePack file + std::ifstream f_msgpack(filename + ".msgpack", std::ios::binary); + std::vector<uint8_t> packed( + (std::istreambuf_iterator<char>(f_msgpack)), + std::istreambuf_iterator<char>()); + json j2; + CHECK_NOTHROW(j2 = json::from_msgpack({packed.data(), packed.size()})); + + // compare parsed JSON values + CHECK(j1 == j2); + } + + SECTION("output to output adapters") + { + // parse MessagePack file + std::ifstream f_msgpack(filename + ".msgpack", std::ios::binary); + std::vector<uint8_t> packed( + (std::istreambuf_iterator<char>(f_msgpack)), + std::istreambuf_iterator<char>()); + + SECTION("std::vector<uint8_t>") + { + std::vector<uint8_t> vec; + json::to_msgpack(j1, vec); + CHECK(vec == packed); + } + } } } }