diff options
Diffstat (limited to 'ext/json/json.hpp')
-rw-r--r-- | ext/json/json.hpp | 3278 |
1 files changed, 2559 insertions, 719 deletions
diff --git a/ext/json/json.hpp b/ext/json/json.hpp index 878fb899..9d48e7a6 100644 --- a/ext/json/json.hpp +++ b/ext/json/json.hpp @@ -1,11 +1,11 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License <http://opensource.org/licenses/MIT>. -Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>. +Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -29,30 +29,32 @@ SOFTWARE. #ifndef NLOHMANN_JSON_HPP #define NLOHMANN_JSON_HPP -#include <algorithm> -#include <array> -#include <cassert> -#include <ciso646> -#include <cmath> -#include <cstddef> -#include <cstdint> -#include <cstdlib> -#include <functional> -#include <initializer_list> -#include <iomanip> -#include <iostream> -#include <iterator> -#include <limits> -#include <locale> -#include <map> -#include <memory> -#include <numeric> -#include <sstream> -#include <stdexcept> -#include <string> -#include <type_traits> -#include <utility> -#include <vector> +#include <algorithm> // all_of, for_each, transform +#include <array> // array +#include <cassert> // assert +#include <cctype> // isdigit +#include <ciso646> // and, not, or +#include <cmath> // isfinite, ldexp, signbit +#include <cstddef> // nullptr_t, ptrdiff_t, size_t +#include <cstdint> // int64_t, uint64_t +#include <cstdlib> // strtod, strtof, strtold, strtoul +#include <cstring> // strlen +#include <functional> // function, hash, less +#include <initializer_list> // initializer_list +#include <iomanip> // setw +#include <iostream> // istream, ostream +#include <iterator> // advance, begin, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator +#include <limits> // numeric_limits +#include <locale> // locale +#include <map> // map +#include <memory> // addressof, allocator, allocator_traits, unique_ptr +#include <numeric> // accumulate +#include <sstream> // stringstream +#include <stdexcept> // domain_error, invalid_argument, out_of_range +#include <string> // getline, stoi, string, to_string +#include <type_traits> // add_pointer, enable_if, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_floating_point, is_integral, is_nothrow_move_assignable, std::is_nothrow_move_constructible, std::is_pointer, std::is_reference, std::is_same, remove_const, remove_pointer, remove_reference +#include <utility> // declval, forward, make_pair, move, pair, swap +#include <vector> // vector // exclude unsupported compilers #if defined(__clang__) @@ -73,6 +75,21 @@ SOFTWARE. #pragma GCC diagnostic ignored "-Wfloat-equal" #endif +// disable documentation warnings on clang +#if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow for portable deprecation warnings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define JSON_DEPRECATED __declspec(deprecated) +#else + #define JSON_DEPRECATED +#endif + /*! @brief namespace for Niels Lohmann @see https://github.com/nlohmann @@ -96,36 +113,19 @@ such as sequence containers. For instance, `std::map` passes the test as it contains a `mapped_type`, whereas `std::vector` fails the test. @sa http://stackoverflow.com/a/7728728/266378 -@since version 1.0.0 +@since version 1.0.0, overworked in version 2.0.6 */ template<typename T> struct has_mapped_type { private: - template<typename C> static char test(typename C::mapped_type*); - template<typename C> static char (&test(...))[2]; - public: - static constexpr bool value = sizeof(test<T>(0)) == 1; -}; + template <typename U, typename = typename U::mapped_type> + static int detect(U&&); -/*! -@brief helper class to create locales with decimal point - -This struct is used a default locale during the JSON serialization. JSON -requires the decimal point to be `.`, so this function overloads the -`do_decimal_point()` function to return `.`. This function is called by -float-to-string conversions to retrieve the decimal separator between integer -and fractional parts. - -@sa https://github.com/nlohmann/json/issues/51#issuecomment-86869315 -@since version 2.0.0 -*/ -struct DecimalSeparator : std::numpunct<char> -{ - char do_decimal_point() const - { - return '.'; - } + static void detect(...); + public: + static constexpr bool value = + std::is_integral<decltype(detect(std::declval<T>()))>::value; }; } @@ -228,6 +228,7 @@ class basic_json public: // forward declarations + template<typename U> class iter_impl; template<typename Base> class json_reverse_iterator; class json_pointer; @@ -262,9 +263,9 @@ class basic_json using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer; /// an iterator for a basic_json container - class iterator; + using iterator = iter_impl<basic_json>; /// a const iterator for a basic_json container - class const_iterator; + using const_iterator = iter_impl<const basic_json>; /// a reverse iterator for a basic_json container using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>; /// a const reverse iterator for a basic_json container @@ -872,8 +873,17 @@ class basic_json break; } + case value_t::null: + { + break; + } + default: { + if (t == value_t::null) + { + throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.0.10"); // LCOV_EXCL_LINE + } break; } } @@ -950,7 +960,7 @@ class basic_json With a parser callback function, the result of parsing a JSON text can be influenced. When passed to @ref parse(std::istream&, const - parser_callback_t) or @ref parse(const string_t&, const parser_callback_t), + parser_callback_t) or @ref parse(const CharT, const parser_callback_t), it is called on certain events (passed as @ref parse_event_t via parameter @a event) with a set recursion depth @a depth and context JSON value @a parsed. The return value of the callback function is a boolean @@ -993,7 +1003,7 @@ class basic_json skipped completely or replaced by an empty discarded object. @sa @ref parse(std::istream&, parser_callback_t) or - @ref parse(const string_t&, parser_callback_t) for examples + @ref parse(const CharT, const parser_callback_t) for examples @since version 1.0.0 */ @@ -1057,40 +1067,10 @@ class basic_json } /*! - @brief create a null object (implicitly) - - Create a `null` JSON value. This is the implicit version of the `null` - value constructor as it takes no parameters. - - @note The class invariant is satisfied, because it poses no requirements - for null values. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this constructor never throws - exceptions. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - As postcondition, it holds: `basic_json().empty() == true`. - - @liveexample{The following code shows the constructor for a `null` JSON - value.,basic_json} - - @sa @ref basic_json(std::nullptr_t) -- create a `null` value - - @since version 1.0.0 - */ - basic_json() = default; - - /*! - @brief create a null object (explicitly) + @brief create a null object - Create a `null` JSON value. This is the explicitly version of the `null` - value constructor as it takes a null pointer as parameter. It allows to - create `null` values by explicitly assigning a `nullptr` to a JSON value. + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). The passed null pointer itself is not read -- it is only used to choose the right constructor. @@ -1099,15 +1079,12 @@ class basic_json @exceptionsafety No-throw guarantee: this constructor never throws exceptions. - @liveexample{The following code shows the constructor with null pointer - parameter.,basic_json__nullptr_t} - - @sa @ref basic_json() -- default constructor (implicitly creating a `null` - value) + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} @since version 1.0.0 */ - basic_json(std::nullptr_t) noexcept + basic_json(std::nullptr_t = nullptr) noexcept : basic_json(value_t::null) { assert_invariant(); @@ -1164,11 +1141,9 @@ class basic_json @since version 1.0.0 */ - template <class CompatibleObjectType, typename - std::enable_if< - std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and - std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type - = 0> + template<class CompatibleObjectType, typename std::enable_if< + std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and + std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type = 0> basic_json(const CompatibleObjectType& val) : m_type(value_t::object) { @@ -1229,16 +1204,14 @@ class basic_json @since version 1.0.0 */ - template <class CompatibleArrayType, typename - std::enable_if< - not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and - not std::is_same<CompatibleArrayType, typename basic_json_t::const_iterator>::value and - not std::is_same<CompatibleArrayType, typename basic_json_t::reverse_iterator>::value and - not std::is_same<CompatibleArrayType, typename basic_json_t::const_reverse_iterator>::value and - not std::is_same<CompatibleArrayType, typename array_t::iterator>::value and - not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and - std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type - = 0> + template<class CompatibleArrayType, typename std::enable_if< + not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and + not std::is_same<CompatibleArrayType, typename basic_json_t::const_iterator>::value and + not std::is_same<CompatibleArrayType, typename basic_json_t::reverse_iterator>::value and + not std::is_same<CompatibleArrayType, typename basic_json_t::const_reverse_iterator>::value and + not std::is_same<CompatibleArrayType, typename array_t::iterator>::value and + not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and + std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type = 0> basic_json(const CompatibleArrayType& val) : m_type(value_t::array) { @@ -1324,10 +1297,8 @@ class basic_json @since version 1.0.0 */ - template <class CompatibleStringType, typename - std::enable_if< - std::is_constructible<string_t, CompatibleStringType>::value, int>::type - = 0> + template<class CompatibleStringType, typename std::enable_if< + std::is_constructible<string_t, CompatibleStringType>::value, int>::type = 0> basic_json(const CompatibleStringType& val) : basic_json(string_t(val)) { @@ -1377,12 +1348,9 @@ class basic_json @since version 1.0.0 */ - template<typename T, - typename std::enable_if< - not (std::is_same<T, int>::value) - and std::is_same<T, number_integer_t>::value - , int>::type - = 0> + template<typename T, typename std::enable_if< + not (std::is_same<T, int>::value) and + std::is_same<T, number_integer_t>::value, int>::type = 0> basic_json(const number_integer_t val) noexcept : m_type(value_t::number_integer), m_value(val) { @@ -1446,13 +1414,11 @@ class basic_json @since version 1.0.0 */ - template<typename CompatibleNumberIntegerType, typename - std::enable_if< + template<typename CompatibleNumberIntegerType, typename std::enable_if< std::is_constructible<number_integer_t, CompatibleNumberIntegerType>::value and std::numeric_limits<CompatibleNumberIntegerType>::is_integer and std::numeric_limits<CompatibleNumberIntegerType>::is_signed, - CompatibleNumberIntegerType>::type - = 0> + CompatibleNumberIntegerType>::type = 0> basic_json(const CompatibleNumberIntegerType val) noexcept : m_type(value_t::number_integer), m_value(static_cast<number_integer_t>(val)) @@ -1477,12 +1443,9 @@ class basic_json @since version 2.0.0 */ - template<typename T, - typename std::enable_if< - not (std::is_same<T, int>::value) - and std::is_same<T, number_unsigned_t>::value - , int>::type - = 0> + template<typename T, typename std::enable_if< + not (std::is_same<T, int>::value) and + std::is_same<T, number_unsigned_t>::value, int>::type = 0> basic_json(const number_unsigned_t val) noexcept : m_type(value_t::number_unsigned), m_value(val) { @@ -1509,13 +1472,11 @@ class basic_json @since version 2.0.0 */ - template <typename CompatibleNumberUnsignedType, typename - std::enable_if < - std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and - std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and - not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed, - CompatibleNumberUnsignedType>::type - = 0> + template<typename CompatibleNumberUnsignedType, typename std::enable_if < + std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and + std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and + not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed, + CompatibleNumberUnsignedType>::type = 0> basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), m_value(static_cast<number_unsigned_t>(val)) @@ -1591,11 +1552,9 @@ class basic_json @since version 1.0.0 */ - template<typename CompatibleNumberFloatType, typename = typename - std::enable_if< + template<typename CompatibleNumberFloatType, typename = typename std::enable_if< std::is_constructible<number_float_t, CompatibleNumberFloatType>::value and - std::is_floating_point<CompatibleNumberFloatType>::value>::type - > + std::is_floating_point<CompatibleNumberFloatType>::value>::type> basic_json(const CompatibleNumberFloatType val) noexcept : basic_json(number_float_t(val)) { @@ -1843,7 +1802,8 @@ class basic_json @param[in] first begin of the range to copy from (included) @param[in] last end of the range to copy from (excluded) - @pre Iterators @a first and @a last must be initialized. + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion.** @throw std::domain_error if iterators are not compatible; that is, do not belong to the same JSON value; example: `"iterators are not compatible"` @@ -1861,12 +1821,9 @@ class basic_json @since version 1.0.0 */ - template <class InputIT, typename - std::enable_if< - std::is_same<InputIT, typename basic_json_t::iterator>::value or - std::is_same<InputIT, typename basic_json_t::const_iterator>::value - , int>::type - = 0> + template<class InputIT, typename std::enable_if< + std::is_same<InputIT, typename basic_json_t::iterator>::value or + std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int>::type = 0> basic_json(InputIT first, InputIT last) { assert(first.m_object != nullptr); @@ -1970,12 +1927,21 @@ class basic_json @note A UTF-8 byte order mark is silently ignored. + @deprecated This constructor is deprecated and will be removed in version + 3.0.0 to unify the interface of the library. Deserialization will be + done by stream operators or by calling one of the `parse` functions, + e.g. @ref parse(std::istream&, const parser_callback_t). That is, calls + like `json j(i);` for an input stream @a i need to be replaced by + `json j = json::parse(i);`. See the example below. + @liveexample{The example below demonstrates constructing a JSON value from a `std::stringstream` with and without callback function.,basic_json__istream} - @since version 2.0.0 + @since version 2.0.0, deprecated in version 2.0.3, to be removed in + version 3.0.0 */ + JSON_DEPRECATED explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr) { *this = parser(i, cb).parse(); @@ -2231,7 +2197,7 @@ class basic_json { std::stringstream ss; // fix locale problems - ss.imbue(std::locale(std::locale(), new DecimalSeparator)); + ss.imbue(std::locale::classic()); // 6, 15 or 16 digits of precision allows round-trip IEEE 754 // string->float->string, string->double->string or string->long @@ -2614,11 +2580,9 @@ class basic_json ////////////////// /// get an object (explicit) - template <class T, typename - std::enable_if< - std::is_convertible<typename object_t::key_type, typename T::key_type>::value and - std::is_convertible<basic_json_t, typename T::mapped_type>::value - , int>::type = 0> + template<class T, typename std::enable_if< + std::is_convertible<typename object_t::key_type, typename T::key_type>::value and + std::is_convertible<basic_json_t, typename T::mapped_type>::value, int>::type = 0> T get_impl(T*) const { if (is_object()) @@ -2645,14 +2609,12 @@ class basic_json } /// get an array (explicit) - template <class T, typename - std::enable_if< - std::is_convertible<basic_json_t, typename T::value_type>::value and - not std::is_same<basic_json_t, typename T::value_type>::value and - not std::is_arithmetic<T>::value and - not std::is_convertible<std::string, T>::value and - not has_mapped_type<T>::value - , int>::type = 0> + template<class T, typename std::enable_if< + std::is_convertible<basic_json_t, typename T::value_type>::value and + not std::is_same<basic_json_t, typename T::value_type>::value and + not std::is_arithmetic<T>::value and + not std::is_convertible<std::string, T>::value and + not has_mapped_type<T>::value, int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2672,11 +2634,9 @@ class basic_json } /// get an array (explicit) - template <class T, typename - std::enable_if< - std::is_convertible<basic_json_t, T>::value and - not std::is_same<basic_json_t, T>::value - , int>::type = 0> + template<class T, typename std::enable_if< + std::is_convertible<basic_json_t, T>::value and + not std::is_same<basic_json_t, T>::value, int>::type = 0> std::vector<T> get_impl(std::vector<T>*) const { if (is_array()) @@ -2697,11 +2657,9 @@ class basic_json } /// get an array (explicit) - template <class T, typename - std::enable_if< - std::is_same<basic_json, typename T::value_type>::value and - not has_mapped_type<T>::value - , int>::type = 0> + template<class T, typename std::enable_if< + std::is_same<basic_json, typename T::value_type>::value and + not has_mapped_type<T>::value, int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2728,10 +2686,8 @@ class basic_json } /// get a string (explicit) - template <typename T, typename - std::enable_if< - std::is_convertible<string_t, T>::value - , int>::type = 0> + template<typename T, typename std::enable_if< + std::is_convertible<string_t, T>::value, int>::type = 0> T get_impl(T*) const { if (is_string()) @@ -2745,10 +2701,8 @@ class basic_json } /// get a number (explicit) - template<typename T, typename - std::enable_if< - std::is_arithmetic<T>::value - , int>::type = 0> + template<typename T, typename std::enable_if< + std::is_arithmetic<T>::value, int>::type = 0> T get_impl(T*) const { switch (m_type) @@ -2937,10 +2891,8 @@ class basic_json @since version 1.0.0 */ - template<typename ValueType, typename - std::enable_if< - not std::is_pointer<ValueType>::value - , int>::type = 0> + template<typename ValueType, typename std::enable_if< + not std::is_pointer<ValueType>::value, int>::type = 0> ValueType get() const { return get_impl(static_cast<ValueType*>(nullptr)); @@ -2973,10 +2925,8 @@ class basic_json @since version 1.0.0 */ - template<typename PointerType, typename - std::enable_if< - std::is_pointer<PointerType>::value - , int>::type = 0> + template<typename PointerType, typename std::enable_if< + std::is_pointer<PointerType>::value, int>::type = 0> PointerType get() noexcept { // delegate the call to get_ptr @@ -2987,10 +2937,8 @@ class basic_json @brief get a pointer value (explicit) @copydoc get() */ - template<typename PointerType, typename - std::enable_if< - std::is_pointer<PointerType>::value - , int>::type = 0> + template<typename PointerType, typename std::enable_if< + std::is_pointer<PointerType>::value, int>::type = 0> constexpr const PointerType get() const noexcept { // delegate the call to get_ptr @@ -3023,10 +2971,8 @@ class basic_json @since version 1.0.0 */ - template<typename PointerType, typename - std::enable_if< - std::is_pointer<PointerType>::value - , int>::type = 0> + template<typename PointerType, typename std::enable_if< + std::is_pointer<PointerType>::value, int>::type = 0> PointerType get_ptr() noexcept { // get the type of the PointerType (remove pointer and const) @@ -3052,11 +2998,9 @@ class basic_json @brief get a pointer value (implicit) @copydoc get_ptr() */ - template<typename PointerType, typename - std::enable_if< - std::is_pointer<PointerType>::value - and std::is_const<typename std::remove_pointer<PointerType>::type>::value - , int>::type = 0> + template<typename PointerType, typename std::enable_if< + std::is_pointer<PointerType>::value and + std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0> constexpr const PointerType get_ptr() const noexcept { // get the type of the PointerType (remove pointer and const) @@ -3104,10 +3048,8 @@ class basic_json @since version 1.1.0 */ - template<typename ReferenceType, typename - std::enable_if< - std::is_reference<ReferenceType>::value - , int>::type = 0> + template<typename ReferenceType, typename std::enable_if< + std::is_reference<ReferenceType>::value, int>::type = 0> ReferenceType get_ref() { // delegate call to get_ref_impl @@ -3118,11 +3060,9 @@ class basic_json @brief get a reference value (implicit) @copydoc get_ref() */ - template<typename ReferenceType, typename - std::enable_if< - std::is_reference<ReferenceType>::value - and std::is_const<typename std::remove_reference<ReferenceType>::type>::value - , int>::type = 0> + template<typename ReferenceType, typename std::enable_if< + std::is_reference<ReferenceType>::value and + std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int>::type = 0> ReferenceType get_ref() const { // delegate call to get_ref_impl @@ -3157,10 +3097,9 @@ class basic_json @since version 1.0.0 */ - template < typename ValueType, typename - std::enable_if < - not std::is_pointer<ValueType>::value - and not std::is_same<ValueType, typename string_t::value_type>::value + template < typename ValueType, typename std::enable_if < + not std::is_pointer<ValueType>::value and + not std::is_same<ValueType, typename string_t::value_type>::value #ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value #endif @@ -3509,6 +3448,9 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @@ -3667,6 +3609,9 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @@ -3744,10 +3689,8 @@ class basic_json @since version 1.0.0 */ - template <class ValueType, typename - std::enable_if< - std::is_convertible<basic_json_t, ValueType>::value - , int>::type = 0> + template<class ValueType, typename std::enable_if< + std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0> ValueType value(const typename object_t::key_type& key, ValueType default_value) const { // at only works for objects @@ -3820,10 +3763,8 @@ class basic_json @since version 2.0.2 */ - template <class ValueType, typename - std::enable_if< - std::is_convertible<basic_json_t, ValueType>::value - , int>::type = 0> + template<class ValueType, typename std::enable_if< + std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0> ValueType value(const json_pointer& ptr, ValueType default_value) const { // at only works for objects @@ -3861,13 +3802,14 @@ class basic_json container `c`, the expression `c.front()` is equivalent to `*c.begin()`. @return In case of a structured type (array or object), a reference to the - first element is returned. In cast of number, string, or boolean values, a + first element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). + or an empty array or object (undefined behavior, **guarded by + assertions**). @post The JSON value remains unchanged. @throw std::out_of_range when called on `null` value @@ -3903,13 +3845,14 @@ class basic_json @endcode @return In case of a structured type (array or object), a reference to the - last element is returned. In cast of number, string, or boolean values, a + last element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). + or an empty array or object (undefined behavior, **guarded by + assertions**). @post The JSON value remains unchanged. @throw std::out_of_range when called on `null` value. @@ -3951,7 +3894,7 @@ class basic_json @return Iterator following the last removed element. If the iterator @a pos refers to the last element, the `end()` iterator is returned. - @tparam InteratorType an @ref iterator or @ref const_iterator + @tparam IteratorType an @ref iterator or @ref const_iterator @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. @@ -3973,7 +3916,7 @@ class basic_json @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType} - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @@ -3982,13 +3925,11 @@ class basic_json @since version 1.0.0 */ - template <class InteratorType, typename - std::enable_if< - std::is_same<InteratorType, typename basic_json_t::iterator>::value or - std::is_same<InteratorType, typename basic_json_t::const_iterator>::value - , int>::type - = 0> - InteratorType erase(InteratorType pos) + template<class IteratorType, typename std::enable_if< + std::is_same<IteratorType, typename basic_json_t::iterator>::value or + std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type + = 0> + IteratorType erase(IteratorType pos) { // make sure iterator fits the current value if (this != pos.m_object) @@ -3996,7 +3937,7 @@ class basic_json throw std::domain_error("iterator does not fit current value"); } - InteratorType result = end(); + IteratorType result = end(); switch (m_type) { @@ -4060,7 +4001,7 @@ class basic_json @return Iterator following the last removed element. If the iterator @a second refers to the last element, the `end()` iterator is returned. - @tparam InteratorType an @ref iterator or @ref const_iterator + @tparam IteratorType an @ref iterator or @ref const_iterator @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. @@ -4083,7 +4024,7 @@ class basic_json @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType_IteratorType} - @sa @ref erase(InteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType) -- removes the element at a given position @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @sa @ref erase(const size_type) -- removes the element from an array at @@ -4091,13 +4032,11 @@ class basic_json @since version 1.0.0 */ - template <class InteratorType, typename - std::enable_if< - std::is_same<InteratorType, typename basic_json_t::iterator>::value or - std::is_same<InteratorType, typename basic_json_t::const_iterator>::value - , int>::type - = 0> - InteratorType erase(InteratorType first, InteratorType last) + template<class IteratorType, typename std::enable_if< + std::is_same<IteratorType, typename basic_json_t::iterator>::value or + std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type + = 0> + IteratorType erase(IteratorType first, IteratorType last) { // make sure iterator fits the current value if (this != first.m_object or this != last.m_object) @@ -4105,7 +4044,7 @@ class basic_json throw std::domain_error("iterators do not fit current value"); } - InteratorType result = end(); + IteratorType result = end(); switch (m_type) { @@ -4177,8 +4116,8 @@ class basic_json @liveexample{The example shows the effect of `erase()`.,erase__key_type} - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const size_type) -- removes the element from an array at the given index @@ -4214,8 +4153,8 @@ class basic_json @liveexample{The example shows the effect of `erase()`.,erase__size_type} - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @@ -4257,10 +4196,14 @@ class basic_json element is not found or the JSON value is not an object, end() is returned. + @note This method always returns @ref end() when executed on a JSON type + that is not an object. + @param[in] key key value of the element to search for @return Iterator to an element with key equivalent to @a key. If no such - element is found, past-the-end (see end()) iterator is returned. + element is found or the JSON value is not an object, past-the-end (see + @ref end()) iterator is returned. @complexity Logarithmic in the size of the JSON object. @@ -4303,6 +4246,9 @@ class basic_json default `std::map` type, the return value will always be `0` (@a key was not found) or `1` (@a key was found). + @note This method always returns `0` when executed on a JSON type that is + not an object. + @param[in] key key value of the element to count @return Number of elements with key @a key. If the JSON value is not an @@ -4862,9 +4808,6 @@ class basic_json object | `{}` array | `[]` - @note Floating-point numbers are set to `0.0` which will be serialized to - `0`. The vale type remains @ref number_float_t. - @complexity Linear in the size of the JSON value. @liveexample{The example below shows the effect of `clear()` to different @@ -5110,6 +5053,102 @@ class basic_json } /*! + @brief add an object to an array + + Creates a JSON value from the passed parameters @a args to the end of the + JSON value. If the function is called on a JSON null value, an empty array + is created before appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @throw std::domain_error when called on a type other than JSON array or + null; example: `"cannot use emplace_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` can be used to add + elements to a JSON array. Note how the `null` value was silently converted + to a JSON array.,emplace_back} + + @since version 2.0.8 + */ + template<class... Args> + void emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (not(is_null() or is_array())) + { + throw std::domain_error("cannot use emplace_back() with " + type_name()); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (perfect forwarding) + m_value.array->emplace_back(std::forward<Args>(args)...); + } + + /*! + @brief add an object to an object if key does not exist + + Inserts a new element into a JSON object constructed in-place with the given + @a args if there is no element with the key in the container. If the + function is called on a JSON null value, an empty object is created before + appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @return a pair consisting of an iterator to the inserted element, or the + already-existing element if no insertion happened, and a bool + denoting whether the insertion took place. + + @throw std::domain_error when called on a type other than JSON object or + null; example: `"cannot use emplace() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `emplace()` can be used to add elements + to a JSON object. Note how the `null` value was silently converted to a + JSON object. Further note how no value is added if there was already one + value stored with the same key.,emplace} + + @since version 2.0.8 + */ + template<class... Args> + std::pair<iterator, bool> emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (not(is_null() or is_object())) + { + throw std::domain_error("cannot use emplace() with " + type_name()); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array (perfect forwarding) + auto res = m_value.object->emplace(std::forward<Args>(args)...); + // create result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; + + // return pair of iterator and boolean + return {it, res.second}; + } + + /*! @brief inserts element Inserts element @a val before iterator @a pos. @@ -5885,7 +5924,7 @@ class basic_json o.width(0); // fix locale problems - const auto old_locale = o.imbue(std::locale(std::locale(), new DecimalSeparator)); + const auto old_locale = o.imbue(std::locale::classic()); // set precision // 6, 15 or 16 digits of precision allows round-trip IEEE 754 @@ -5923,9 +5962,45 @@ class basic_json /// @{ /*! - @brief deserialize from string + @brief deserialize from an array + + This function reads from an array of 1-byte values. - @param[in] s string to read a serialized JSON value from + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @param[in] array array to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} + + @since version 2.0.3 + */ + template<class T, std::size_t N> + static basic_json parse(T (&array)[N], + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); + } + + /*! + @brief deserialize from string literal + + @tparam CharT character/literal type with size of 1 byte + @param[in] s string literal to read a serialized JSON value from @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values (optional) @@ -5937,6 +6012,8 @@ class basic_json @a cb has a super-linear complexity. @note A UTF-8 byte order mark is silently ignored. + @note String containers like `std::string` or @ref string_t can be parsed + with @ref parse(const ContiguousContainer&, const parser_callback_t) @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__string__parser_callback_t} @@ -5944,12 +6021,16 @@ class basic_json @sa @ref parse(std::istream&, const parser_callback_t) for a version that reads from an input stream - @since version 1.0.0 + @since version 1.0.0 (originally for @ref string_t) */ - static basic_json parse(const string_t& s, + 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> + static basic_json parse(const CharT s, const parser_callback_t cb = nullptr) { - return parser(s, cb).parse(); + return parser(reinterpret_cast<const char*>(s), cb).parse(); } /*! @@ -5971,7 +6052,7 @@ class basic_json @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__istream__parser_callback_t} - @sa @ref parse(const string_t&, const parser_callback_t) for a version + @sa @ref parse(const CharT, const parser_callback_t) for a version that reads from a string @since version 1.0.0 @@ -5992,6 +6073,130 @@ class basic_json } /*! + @brief deserialize from an iterator range with contiguous storage + + This function reads from an iterator range of a container with contiguous + storage of 1-byte values. Compatible container types include + `std::vector`, `std::string`, `std::array`, `std::valarray`, and + `std::initializer_list`. Furthermore, C-style arrays can be used with + `std::begin()`/`std::end()`. User-defined containers can be used as long + as they implement random-access iterators and a contiguous storage. + + @pre The iterator range is contiguous. Violating this precondition yields + undefined behavior. **This precondition is enforced with an assertion.** + @pre Each element in the range has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with noncompliant iterators and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @tparam IteratorType iterator of container with contiguous storage + @param[in] first begin of the range to parse (included) + @param[in] last end of the range to parse (excluded) + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an iterator range.,parse__iteratortype__parser_callback_t} + + @since version 2.0.3 + */ + template<class IteratorType, typename std::enable_if< + std::is_base_of< + 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) + { + // assertion to check that the iterator range is indeed contiguous, + // see http://stackoverflow.com/a/35008842/266378 for more discussion + assert(std::accumulate(first, last, std::make_pair<bool, int>(true, 0), + [&first](std::pair<bool, int> res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first); + + // assertion to check that each element is 1 byte long + static_assert(sizeof(typename std::iterator_traits<IteratorType>::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); + + // if iterator range is empty, create a parser with an empty string + // to generate "unexpected EOF" error message + if (std::distance(first, last) <= 0) + { + return parser("").parse(); + } + + return parser(first, last, cb).parse(); + } + + /*! + @brief deserialize from a container with contiguous storage + + This function reads from a container with contiguous storage of 1-byte + values. Compatible container types include `std::vector`, `std::string`, + `std::array`, and `std::initializer_list`. User-defined containers can be + used as long as they implement random-access iterators and a contiguous + storage. + + @pre The container storage is contiguous. Violating this precondition + yields undefined behavior. **This precondition is enforced with an + assertion.** + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with a noncompliant container and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @tparam ContiguousContainer container type with contiguous storage + @param[in] c container to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + + @since version 2.0.3 + */ + template<class ContiguousContainer, 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 + , int>::type = 0> + static basic_json parse(const ContiguousContainer& c, + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(c), std::end(c), cb); + } + + /*! @brief deserialize from stream Deserializes an input stream to a JSON value. @@ -6032,6 +6237,1496 @@ class basic_json /// @} + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// + + /// @name binary serialization/deserialization support + /// @{ + + private: + template<typename T> + static void add_to_vector(std::vector<uint8_t>& vec, size_t bytes, const T number) + { + assert(bytes == 1 or bytes == 2 or bytes == 4 or bytes == 8); + + switch (bytes) + { + case 8: + { + vec.push_back(static_cast<uint8_t>((number >> 070) & 0xff)); + vec.push_back(static_cast<uint8_t>((number >> 060) & 0xff)); + vec.push_back(static_cast<uint8_t>((number >> 050) & 0xff)); + vec.push_back(static_cast<uint8_t>((number >> 040) & 0xff)); + // intentional fall-through + } + + case 4: + { + vec.push_back(static_cast<uint8_t>((number >> 030) & 0xff)); + vec.push_back(static_cast<uint8_t>((number >> 020) & 0xff)); + // intentional fall-through + } + + case 2: + { + vec.push_back(static_cast<uint8_t>((number >> 010) & 0xff)); + // intentional fall-through + } + + case 1: + { + vec.push_back(static_cast<uint8_t>(number & 0xff)); + break; + } + } + } + + /*! + @brief take sufficient bytes from a vector to fill an integer variable + + In the context of binary serialization formats, we need to read several + bytes from a byte vector and combine them to multi-byte integral data + types. + + @param[in] vec byte vector to read from + @param[in] current_index the position in the vector after which to read + + @return the next sizeof(T) bytes from @a vec, in reverse order as T + + @tparam T the integral return type + + @throw std::out_of_range if there are less than sizeof(T)+1 bytes in the + vector @a vec to read + + In the for loop, the bytes from the vector are copied in reverse order into + the return value. In the figures below, let sizeof(T)=4 and `i` be the loop + variable. + + Precondition: + + vec: | | | a | b | c | d | T: | | | | | + ^ ^ ^ ^ + current_index i ptr sizeof(T) + + Postcondition: + + vec: | | | a | b | c | d | T: | d | c | b | a | + ^ ^ ^ + | i ptr + current_index + + @sa Code adapted from <http://stackoverflow.com/a/41031865/266378>. + */ + template<typename T> + static T get_from_vector(const std::vector<uint8_t>& vec, const size_t current_index) + { + if (current_index + sizeof(T) + 1 > vec.size()) + { + throw std::out_of_range("cannot read " + std::to_string(sizeof(T)) + " bytes from vector"); + } + + T result; + uint8_t* ptr = reinterpret_cast<uint8_t*>(&result); + for (size_t i = 0; i < sizeof(T); ++i) + { + *ptr++ = vec[current_index + sizeof(T) - i]; + } + return result; + } + + /*! + @brief create a MessagePack serialization of a given JSON value + + This is a straightforward implementation of the MessagePack specification. + + @param[in] j JSON value to serialize + @param[in,out] v byte vector to write the serialization to + + @sa https://github.com/msgpack/msgpack/blob/master/spec.md + */ + static void to_msgpack_internal(const basic_json& j, std::vector<uint8_t>& v) + { + switch (j.type()) + { + case value_t::null: + { + // nil + v.push_back(0xc0); + break; + } + + case value_t::boolean: + { + // true and false + v.push_back(j.m_value.boolean ? 0xc3 : 0xc2); + break; + } + + case value_t::number_integer: + { + 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. + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT8_MAX) + { + // uint 8 + v.push_back(0xcc); + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT16_MAX) + { + // uint 16 + v.push_back(0xcd); + add_to_vector(v, 2, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT32_MAX) + { + // uint 32 + v.push_back(0xce); + add_to_vector(v, 4, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT64_MAX) + { + // uint 64 + v.push_back(0xcf); + add_to_vector(v, 8, j.m_value.number_unsigned); + } + } + else + { + if (j.m_value.number_integer >= -32) + { + // negative fixnum + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT8_MIN and j.m_value.number_integer <= INT8_MAX) + { + // int 8 + v.push_back(0xd0); + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT16_MIN and j.m_value.number_integer <= INT16_MAX) + { + // int 16 + v.push_back(0xd1); + add_to_vector(v, 2, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT32_MIN and j.m_value.number_integer <= INT32_MAX) + { + // int 32 + v.push_back(0xd2); + add_to_vector(v, 4, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT64_MIN and j.m_value.number_integer <= INT64_MAX) + { + // int 64 + v.push_back(0xd3); + add_to_vector(v, 8, j.m_value.number_integer); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT8_MAX) + { + // uint 8 + v.push_back(0xcc); + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT16_MAX) + { + // uint 16 + v.push_back(0xcd); + add_to_vector(v, 2, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT32_MAX) + { + // uint 32 + v.push_back(0xce); + add_to_vector(v, 4, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT64_MAX) + { + // uint 64 + v.push_back(0xcf); + add_to_vector(v, 8, j.m_value.number_unsigned); + } + break; + } + + case value_t::number_float: + { + // float 64 + v.push_back(0xcb); + const uint8_t* helper = reinterpret_cast<const uint8_t*>(&(j.m_value.number_float)); + for (size_t i = 0; i < 8; ++i) + { + v.push_back(helper[7 - i]); + } + break; + } + + case value_t::string: + { + const auto N = j.m_value.string->size(); + if (N <= 31) + { + // fixstr + v.push_back(static_cast<uint8_t>(0xa0 | N)); + } + else if (N <= 255) + { + // str 8 + v.push_back(0xd9); + add_to_vector(v, 1, N); + } + else if (N <= 65535) + { + // str 16 + v.push_back(0xda); + add_to_vector(v, 2, N); + } + else if (N <= 4294967295) + { + // str 32 + v.push_back(0xdb); + add_to_vector(v, 4, N); + } + + // append string + std::copy(j.m_value.string->begin(), j.m_value.string->end(), + std::back_inserter(v)); + break; + } + + case value_t::array: + { + const auto N = j.m_value.array->size(); + if (N <= 15) + { + // fixarray + v.push_back(static_cast<uint8_t>(0x90 | N)); + } + else if (N <= 0xffff) + { + // array 16 + v.push_back(0xdc); + add_to_vector(v, 2, N); + } + else if (N <= 0xffffffff) + { + // array 32 + v.push_back(0xdd); + add_to_vector(v, 4, N); + } + + // append each element + for (const auto& el : *j.m_value.array) + { + to_msgpack_internal(el, v); + } + break; + } + + case value_t::object: + { + const auto N = j.m_value.object->size(); + if (N <= 15) + { + // fixmap + v.push_back(static_cast<uint8_t>(0x80 | (N & 0xf))); + } + else if (N <= 65535) + { + // map 16 + v.push_back(0xde); + add_to_vector(v, 2, N); + } + else if (N <= 4294967295) + { + // map 32 + v.push_back(0xdf); + add_to_vector(v, 4, N); + } + + // append each element + for (const auto& el : *j.m_value.object) + { + to_msgpack_internal(el.first, v); + to_msgpack_internal(el.second, v); + } + break; + } + + default: + { + break; + } + } + } + + /*! + @brief create a CBOR serialization of a given JSON value + + This is a straightforward implementation of the CBOR specification. + + @param[in] j JSON value to serialize + @param[in,out] v byte vector to write the serialization to + + @sa https://tools.ietf.org/html/rfc7049 + */ + static void to_cbor_internal(const basic_json& j, std::vector<uint8_t>& v) + { + switch (j.type()) + { + case value_t::null: + { + v.push_back(0xf6); + break; + } + + case value_t::boolean: + { + v.push_back(j.m_value.boolean ? 0xf5 : 0xf4); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // CBOR does not differentiate between positive signed + // integers and unsigned integers. Therefore, we used the + // code from the value_t::number_unsigned case here. + if (j.m_value.number_integer <= 0x17) + { + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer <= UINT8_MAX) + { + v.push_back(0x18); + // one-byte uint8_t + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer <= UINT16_MAX) + { + v.push_back(0x19); + // two-byte uint16_t + add_to_vector(v, 2, j.m_value.number_integer); + } + else if (j.m_value.number_integer <= UINT32_MAX) + { + v.push_back(0x1a); + // four-byte uint32_t + add_to_vector(v, 4, j.m_value.number_integer); + } + else + { + v.push_back(0x1b); + // eight-byte uint64_t + add_to_vector(v, 8, j.m_value.number_integer); + } + } + else + { + // The conversions below encode the sign in the first byte, + // and the value is converted to a positive number. + const auto positive_number = -1 - j.m_value.number_integer; + if (j.m_value.number_integer >= -24) + { + v.push_back(static_cast<uint8_t>(0x20 + positive_number)); + } + else if (positive_number <= UINT8_MAX) + { + // int 8 + v.push_back(0x38); + add_to_vector(v, 1, positive_number); + } + else if (positive_number <= UINT16_MAX) + { + // int 16 + v.push_back(0x39); + add_to_vector(v, 2, positive_number); + } + else if (positive_number <= UINT32_MAX) + { + // int 32 + v.push_back(0x3a); + add_to_vector(v, 4, positive_number); + } + else + { + // int 64 + v.push_back(0x3b); + add_to_vector(v, 8, positive_number); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= 0x17) + { + v.push_back(static_cast<uint8_t>(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= 0xff) + { + v.push_back(0x18); + // one-byte uint8_t + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= 0xffff) + { + v.push_back(0x19); + // two-byte uint16_t + add_to_vector(v, 2, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= 0xffffffff) + { + v.push_back(0x1a); + // four-byte uint32_t + add_to_vector(v, 4, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= 0xffffffffffffffff) + { + v.push_back(0x1b); + // eight-byte uint64_t + add_to_vector(v, 8, j.m_value.number_unsigned); + } + break; + } + + case value_t::number_float: + { + // Double-Precision Float + v.push_back(0xfb); + const uint8_t* helper = reinterpret_cast<const uint8_t*>(&(j.m_value.number_float)); + for (size_t i = 0; i < 8; ++i) + { + v.push_back(helper[7 - i]); + } + break; + } + + case value_t::string: + { + const auto N = j.m_value.string->size(); + if (N <= 0x17) + { + v.push_back(0x60 + N); // 1 byte for string + size + } + else if (N <= 0xff) + { + v.push_back(0x78); // one-byte uint8_t for N + add_to_vector(v, 1, N); + } + else if (N <= 0xffff) + { + v.push_back(0x79); // two-byte uint16_t for N + add_to_vector(v, 2, N); + } + else if (N <= 0xffffffff) + { + v.push_back(0x7a); // four-byte uint32_t for N + add_to_vector(v, 4, N); + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + v.push_back(0x7b); // eight-byte uint64_t for N + add_to_vector(v, 8, N); + } + // LCOV_EXCL_STOP + + // append string + std::copy(j.m_value.string->begin(), j.m_value.string->end(), + std::back_inserter(v)); + break; + } + + case value_t::array: + { + const auto N = j.m_value.array->size(); + if (N <= 0x17) + { + v.push_back(0x80 + N); // 1 byte for array + size + } + else if (N <= 0xff) + { + v.push_back(0x98); // one-byte uint8_t for N + add_to_vector(v, 1, N); + } + else if (N <= 0xffff) + { + v.push_back(0x99); // two-byte uint16_t for N + add_to_vector(v, 2, N); + } + else if (N <= 0xffffffff) + { + v.push_back(0x9a); // four-byte uint32_t for N + add_to_vector(v, 4, N); + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + v.push_back(0x9b); // eight-byte uint64_t for N + add_to_vector(v, 8, N); + } + // LCOV_EXCL_STOP + + // append each element + for (const auto& el : *j.m_value.array) + { + to_cbor_internal(el, v); + } + break; + } + + case value_t::object: + { + const auto N = j.m_value.object->size(); + if (N <= 0x17) + { + v.push_back(0xa0 + N); // 1 byte for object + size + } + else if (N <= 0xff) + { + v.push_back(0xb8); + add_to_vector(v, 1, N); // one-byte uint8_t for N + } + else if (N <= 0xffff) + { + v.push_back(0xb9); + add_to_vector(v, 2, N); // two-byte uint16_t for N + } + else if (N <= 0xffffffff) + { + v.push_back(0xba); + add_to_vector(v, 4, N); // four-byte uint32_t for N + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + v.push_back(0xbb); + add_to_vector(v, 8, N); // eight-byte uint64_t for N + } + // LCOV_EXCL_STOP + + // append each element + for (const auto& el : *j.m_value.object) + { + to_cbor_internal(el.first, v); + to_cbor_internal(el.second, v); + } + break; + } + + default: + { + break; + } + } + } + + + /* + @brief checks if given lengths do not exceed the size of a given vector + + To secure the access to the byte vector during CBOR/MessagePack + deserialization, bytes are copied from the vector into buffers. This + function checks if the number of bytes to copy (@a len) does not exceed the + size @s size of the vector. Additionally, an @a offset is given from where + to start reading the bytes. + + This function checks whether reading the bytes is safe; that is, offset is a + valid index in the vector, offset+len + + @param[in] size size of the byte vector + @param[in] len number of bytes to read + @param[in] offset offset where to start reading + + vec: x x x x x X X X X X + ^ ^ ^ + 0 offset len + + @throws out_of_range if `len > v.size()` + */ + static void check_length(const size_t size, const size_t len, const size_t offset) + { + // simple case: requested length is greater than the vector's length + if (len > size or offset > size) + { + throw std::out_of_range("len out of range"); + } + + // second case: adding offset would result in overflow + if ((size > (std::numeric_limits<size_t>::max() - offset))) + { + throw std::out_of_range("len+offset out of range"); + } + + // last case: reading past the end of the vector + if (len + offset > size) + { + throw std::out_of_range("len+offset out of range"); + } + } + + /*! + @brief create a JSON value from a given MessagePack vector + + @param[in] v MessagePack serialization + @param[in] idx byte index to start reading from @a v + + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from MessagePack were + used in the given vector @a v or if the input is not valid MessagePack + @throw std::out_of_range if the given vector ends prematurely + + @sa https://github.com/msgpack/msgpack/blob/master/spec.md + */ + static basic_json from_msgpack_internal(const std::vector<uint8_t>& v, size_t& idx) + { + // make sure reading 1 byte is safe + check_length(v.size(), 1, idx); + + // store and increment index + const size_t current_idx = idx++; + + if (v[current_idx] <= 0xbf) + { + if (v[current_idx] <= 0x7f) // positive fixint + { + return v[current_idx]; + } + else if (v[current_idx] <= 0x8f) // fixmap + { + basic_json result = value_t::object; + const size_t len = v[current_idx] & 0x0f; + for (size_t i = 0; i < len; ++i) + { + std::string key = from_msgpack_internal(v, idx); + result[key] = from_msgpack_internal(v, idx); + } + return result; + } + else if (v[current_idx] <= 0x9f) // fixarray + { + basic_json result = value_t::array; + const size_t len = v[current_idx] & 0x0f; + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_msgpack_internal(v, idx)); + } + return result; + } + else // fixstr + { + const size_t len = v[current_idx] & 0x1f; + const size_t offset = current_idx + 1; + idx += len; // skip content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast<const char*>(v.data()) + offset, len); + } + } + else if (v[current_idx] >= 0xe0) // negative fixint + { + return static_cast<int8_t>(v[current_idx]); + } + else + { + switch (v[current_idx]) + { + case 0xc0: // nil + { + return value_t::null; + } + + case 0xc2: // false + { + return false; + } + + case 0xc3: // true + { + return true; + } + + case 0xca: // float 32 + { + // copy bytes in reverse order into the double variable + check_length(v.size(), sizeof(float), 1); + float res; + for (size_t byte = 0; byte < sizeof(float); ++byte) + { + reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte]; + } + idx += sizeof(float); // skip content bytes + return res; + } + + case 0xcb: // float 64 + { + // copy bytes in reverse order into the double variable + check_length(v.size(), sizeof(double), 1); + double res; + for (size_t byte = 0; byte < sizeof(double); ++byte) + { + reinterpret_cast<uint8_t*>(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte]; + } + idx += sizeof(double); // skip content bytes + return res; + } + + case 0xcc: // uint 8 + { + idx += 1; // skip content byte + return get_from_vector<uint8_t>(v, current_idx); + } + + case 0xcd: // uint 16 + { + idx += 2; // skip 2 content bytes + return get_from_vector<uint16_t>(v, current_idx); + } + + case 0xce: // uint 32 + { + idx += 4; // skip 4 content bytes + return get_from_vector<uint32_t>(v, current_idx); + } + + case 0xcf: // uint 64 + { + idx += 8; // skip 8 content bytes + return get_from_vector<uint64_t>(v, current_idx); + } + + case 0xd0: // int 8 + { + idx += 1; // skip content byte + return get_from_vector<int8_t>(v, current_idx); + } + + case 0xd1: // int 16 + { + idx += 2; // skip 2 content bytes + return get_from_vector<int16_t>(v, current_idx); + } + + case 0xd2: // int 32 + { + idx += 4; // skip 4 content bytes + return get_from_vector<int32_t>(v, current_idx); + } + + case 0xd3: // int 64 + { + idx += 8; // skip 8 content bytes + return get_from_vector<int64_t>(v, current_idx); + } + + case 0xd9: // str 8 + { + const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx)); + const size_t offset = current_idx + 2; + idx += len + 1; // skip size byte + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast<const char*>(v.data()) + offset, len); + } + + case 0xda: // str 16 + { + const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx)); + const size_t offset = current_idx + 3; + idx += len + 2; // skip 2 size bytes + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast<const char*>(v.data()) + offset, len); + } + + case 0xdb: // str 32 + { + const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx)); + const size_t offset = current_idx + 5; + idx += len + 4; // skip 4 size bytes + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast<const char*>(v.data()) + offset, len); + } + + case 0xdc: // array 16 + { + basic_json result = value_t::array; + const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx)); + idx += 2; // skip 2 size bytes + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_msgpack_internal(v, idx)); + } + return result; + } + + case 0xdd: // array 32 + { + basic_json result = value_t::array; + const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx)); + idx += 4; // skip 4 size bytes + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_msgpack_internal(v, idx)); + } + return result; + } + + case 0xde: // map 16 + { + basic_json result = value_t::object; + const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx)); + idx += 2; // skip 2 size bytes + for (size_t i = 0; i < len; ++i) + { + std::string key = from_msgpack_internal(v, idx); + result[key] = from_msgpack_internal(v, idx); + } + return result; + } + + case 0xdf: // map 32 + { + basic_json result = value_t::object; + const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx)); + idx += 4; // skip 4 size bytes + for (size_t i = 0; i < len; ++i) + { + std::string key = from_msgpack_internal(v, idx); + result[key] = from_msgpack_internal(v, idx); + } + return result; + } + + default: + { + throw std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast<int>(v[current_idx]))); + } + } + } + } + + /*! + @brief create a JSON value from a given CBOR vector + + @param[in] v CBOR serialization + @param[in] idx byte index to start reading from @a v + + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from CBOR were used in + the given vector @a v or if the input is not valid CBOR + @throw std::out_of_range if the given vector ends prematurely + + @sa https://tools.ietf.org/html/rfc7049 + */ + static basic_json from_cbor_internal(const std::vector<uint8_t>& v, size_t& idx) + { + // store and increment index + const size_t current_idx = idx++; + + switch (v.at(current_idx)) + { + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + { + return v[current_idx]; + } + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + idx += 1; // skip content byte + return get_from_vector<uint8_t>(v, current_idx); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + idx += 2; // skip 2 content bytes + return get_from_vector<uint16_t>(v, current_idx); + } + + case 0x1a: // Unsigned integer (four-byte uint32_t follows) + { + idx += 4; // skip 4 content bytes + return get_from_vector<uint32_t>(v, current_idx); + } + + case 0x1b: // Unsigned integer (eight-byte uint64_t follows) + { + idx += 8; // skip 8 content bytes + return get_from_vector<uint64_t>(v, current_idx); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2a: + case 0x2b: + case 0x2c: + case 0x2d: + case 0x2e: + case 0x2f: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + { + return static_cast<int8_t>(0x20 - 1 - v[current_idx]); + } + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + idx += 1; // skip content byte + // must be uint8_t ! + return static_cast<number_integer_t>(-1) - get_from_vector<uint8_t>(v, current_idx); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + idx += 2; // skip 2 content bytes + return static_cast<number_integer_t>(-1) - get_from_vector<uint16_t>(v, current_idx); + } + + case 0x3a: // Negative integer -1-n (four-byte uint32_t follows) + { + idx += 4; // skip 4 content bytes + return static_cast<number_integer_t>(-1) - get_from_vector<uint32_t>(v, current_idx); + } + + case 0x3b: // Negative integer -1-n (eight-byte uint64_t follows) + { + idx += 8; // skip 8 content bytes + return static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(get_from_vector<uint64_t>(v, current_idx)); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6a: + case 0x6b: + case 0x6c: + case 0x6d: + case 0x6e: + case 0x6f: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + const auto len = static_cast<size_t>(v[current_idx] - 0x60); + const size_t offset = current_idx + 1; + idx += len; // skip content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast<const char*>(v.data()) + offset, len); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx)); + const size_t offset = current_idx + 2; + idx += len + 1; // skip size byte + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast<const char*>(v.data()) + offset, len); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx)); + const size_t offset = current_idx + 3; + idx += len + 2; // skip 2 size bytes + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast<const char*>(v.data()) + offset, len); + } + + case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) + { + const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx)); + const size_t offset = current_idx + 5; + idx += len + 4; // skip 4 size bytes + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast<const char*>(v.data()) + offset, len); + } + + case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) + { + const auto len = static_cast<size_t>(get_from_vector<uint64_t>(v, current_idx)); + const size_t offset = current_idx + 9; + idx += len + 8; // skip 8 size bytes + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast<const char*>(v.data()) + offset, len); + } + + case 0x7f: // UTF-8 string (indefinite length) + { + std::string result; + while (v.at(idx) != 0xff) + { + string_t s = from_cbor_internal(v, idx); + result += s; + } + // skip break byte (0xFF) + idx += 1; + return result; + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8a: + case 0x8b: + case 0x8c: + case 0x8d: + case 0x8e: + case 0x8f: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + { + basic_json result = value_t::array; + const auto len = static_cast<size_t>(v[current_idx] - 0x80); + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_cbor_internal(v, idx)); + } + return result; + } + + case 0x98: // array (one-byte uint8_t for n follows) + { + basic_json result = value_t::array; + const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx)); + idx += 1; // skip 1 size byte + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_cbor_internal(v, idx)); + } + return result; + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + basic_json result = value_t::array; + const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx)); + idx += 2; // skip 4 size bytes + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_cbor_internal(v, idx)); + } + return result; + } + + case 0x9a: // array (four-byte uint32_t for n follow) + { + basic_json result = value_t::array; + const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx)); + idx += 4; // skip 4 size bytes + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_cbor_internal(v, idx)); + } + return result; + } + + case 0x9b: // array (eight-byte uint64_t for n follow) + { + basic_json result = value_t::array; + const auto len = static_cast<size_t>(get_from_vector<uint64_t>(v, current_idx)); + idx += 8; // skip 8 size bytes + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_cbor_internal(v, idx)); + } + return result; + } + + case 0x9f: // array (indefinite length) + { + basic_json result = value_t::array; + while (v.at(idx) != 0xff) + { + result.push_back(from_cbor_internal(v, idx)); + } + // skip break byte (0xFF) + idx += 1; + return result; + } + + // map (0x00..0x17 pairs of data items follow) + case 0xa0: + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + case 0xa5: + case 0xa6: + case 0xa7: + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + case 0xac: + case 0xad: + case 0xae: + case 0xaf: + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + { + basic_json result = value_t::object; + const auto len = static_cast<size_t>(v[current_idx] - 0xa0); + for (size_t i = 0; i < len; ++i) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + return result; + } + + case 0xb8: // map (one-byte uint8_t for n follows) + { + basic_json result = value_t::object; + const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx)); + idx += 1; // skip 1 size byte + for (size_t i = 0; i < len; ++i) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + return result; + } + + case 0xb9: // map (two-byte uint16_t for n follow) + { + basic_json result = value_t::object; + const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx)); + idx += 2; // skip 2 size bytes + for (size_t i = 0; i < len; ++i) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + return result; + } + + case 0xba: // map (four-byte uint32_t for n follow) + { + basic_json result = value_t::object; + const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx)); + idx += 4; // skip 4 size bytes + for (size_t i = 0; i < len; ++i) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + return result; + } + + case 0xbb: // map (eight-byte uint64_t for n follow) + { + basic_json result = value_t::object; + const auto len = static_cast<size_t>(get_from_vector<uint64_t>(v, current_idx)); + idx += 8; // skip 8 size bytes + for (size_t i = 0; i < len; ++i) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + return result; + } + + case 0xbf: // map (indefinite length) + { + basic_json result = value_t::object; + while (v.at(idx) != 0xff) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + // skip break byte (0xFF) + idx += 1; + return result; + } + + case 0xf4: // false + { + return false; + } + + case 0xf5: // true + { + return true; + } + + case 0xf6: // null + { + return value_t::null; + } + + case 0xf9: // Half-Precision Float (two-byte IEEE 754) + { + check_length(v.size(), 2, 1); + idx += 2; // skip two content bytes + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added to + // IEEE 754 in 2008, today's programming platforms often still + // only have limited support for them. It is very easy to + // include at least decoding support for them even without such + // support. An example of a small decoder for half-precision + // floating-point numbers in the C language is shown in Fig. 3. + const int half = (v[current_idx + 1] << 8) + v[current_idx + 2]; + const int exp = (half >> 10) & 0x1f; + const int mant = half & 0x3ff; + double val; + if (exp == 0) + { + val = std::ldexp(mant, -24); + } + else if (exp != 31) + { + val = std::ldexp(mant + 1024, exp - 25); + } + else + { + val = mant == 0 ? INFINITY : NAN; + } + return half & 0x8000 ? -val : val; + } + + case 0xfa: // Single-Precision Float (four-byte IEEE 754) + { + // copy bytes in reverse order into the float variable + check_length(v.size(), sizeof(float), 1); + float res; + for (size_t byte = 0; byte < sizeof(float); ++byte) + { + reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte]; + } + idx += sizeof(float); // skip content bytes + return res; + } + + case 0xfb: // Double-Precision Float (eight-byte IEEE 754) + { + check_length(v.size(), sizeof(double), 1); + // copy bytes in reverse order into the double variable + double res; + for (size_t byte = 0; byte < sizeof(double); ++byte) + { + reinterpret_cast<uint8_t*>(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte]; + } + idx += sizeof(double); // skip content bytes + return res; + } + + default: // anything else (0xFF is handled inside the other types) + { + throw std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast<int>(v[current_idx]))); + } + } + } + + public: + /*! + @brief create a MessagePack serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the MessagePack + serialization format. MessagePack is a binary serialization format which + aims to be more compact than JSON itself, yet more efficient to parse. + + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in MessagePack format.,to_msgpack} + + @sa http://msgpack.org + @sa @ref from_msgpack(const std::vector<uint8_t>&) for the analogous + deserialization + @sa @ref to_cbor(const basic_json& for the related CBOR format + */ + static std::vector<uint8_t> to_msgpack(const basic_json& j) + { + std::vector<uint8_t> result; + to_msgpack_internal(j, result); + return result; + } + + /*! + @brief create a JSON value from a byte vector in MessagePack format + + Deserializes a given byte vector @a v to a JSON value using the MessagePack + serialization format. + + @param[in] v a byte vector in MessagePack format + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from MessagePack were + used in the given vector @a v or if the input is not valid MessagePack + @throw std::out_of_range if the given vector ends prematurely + + @complexity Linear in the size of the byte vector @a v. + + @liveexample{The example shows the deserialization of a byte vector in + MessagePack format to a JSON value.,from_msgpack} + + @sa http://msgpack.org + @sa @ref to_msgpack(const basic_json&) for the analogous serialization + @sa @ref from_cbor(const std::vector<uint8_t>&) for the related CBOR format + */ + static basic_json from_msgpack(const std::vector<uint8_t>& v) + { + size_t i = 0; + return from_msgpack_internal(v, i); + } + + /*! + @brief create a MessagePack serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the CBOR (Concise + Binary Object Representation) serialization format. CBOR is a binary + serialization format which aims to be more compact than JSON itself, yet + more efficient to parse. + + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in CBOR format.,to_cbor} + + @sa http://cbor.io + @sa @ref from_cbor(const std::vector<uint8_t>&) for the analogous + deserialization + @sa @ref to_msgpack(const basic_json& for the related MessagePack format + */ + static std::vector<uint8_t> to_cbor(const basic_json& j) + { + std::vector<uint8_t> result; + to_cbor_internal(j, result); + return result; + } + + /*! + @brief create a JSON value from a byte vector in CBOR format + + Deserializes a given byte vector @a v to a JSON value using the CBOR + (Concise Binary Object Representation) serialization format. + + @param[in] v a byte vector in CBOR format + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from CBOR were used in + the given vector @a v or if the input is not valid MessagePack + @throw std::out_of_range if the given vector ends prematurely + + @complexity Linear in the size of the byte vector @a v. + + @liveexample{The example shows the deserialization of a byte vector in CBOR + format to a JSON value.,from_cbor} + + @sa http://cbor.io + @sa @ref to_cbor(const basic_json&) for the analogous serialization + @sa @ref from_msgpack(const std::vector<uint8_t>&) for the related + MessagePack format + */ + static basic_json from_cbor(const std::vector<uint8_t>& v) + { + size_t i = 0; + return from_cbor_internal(v, i); + } + + /// @} private: /////////////////////////// @@ -6044,7 +7739,7 @@ class basic_json Returns the type name as string to be used in error messages - usually to indicate that a function was called on a wrong JSON type. - @return basically a string representation of a the @ref m_type member + @return basically a string representation of a the @a m_type member @complexity Constant. @@ -6584,43 +8279,53 @@ class basic_json public: /*! - @brief a const random access iterator for the @ref basic_json class + @brief a template for a random access iterator for the @ref basic_json class - This class implements a const iterator for the @ref basic_json class. From - this class, the @ref iterator class is derived. + This class implements a both iterators (iterator and const_iterator) for the + @ref basic_json class. @note An iterator is called *initialized* when a pointer to a JSON value has been set (e.g., by a constructor or a copy assignment). If the iterator is default-constructed, it is *uninitialized* and most - methods are undefined. The library uses assertions to detect calls - on uninitialized iterators. + methods are undefined. **The library uses assertions to detect calls + on uninitialized iterators.** @requirement The class satisfies the following concept requirements: - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): The iterator that can be moved to point (forward and backward) to any element in constant time. - @since version 1.0.0 + @since version 1.0.0, simplified in version 2.0.9 */ - class const_iterator : public std::iterator<std::random_access_iterator_tag, const basic_json> + template<typename U> + class iter_impl : public std::iterator<std::random_access_iterator_tag, U> { /// allow basic_json to access private members friend class basic_json; + // make sure U is basic_json or const basic_json + static_assert(std::is_same<U, basic_json>::value + or std::is_same<U, const basic_json>::value, + "iter_impl only accepts (const) basic_json"); + public: /// the type of the values when the iterator is dereferenced using value_type = typename basic_json::value_type; /// a type to represent differences between iterators using difference_type = typename basic_json::difference_type; /// defines a pointer to the type iterated over (value_type) - using pointer = typename basic_json::const_pointer; + using pointer = typename std::conditional<std::is_const<U>::value, + typename basic_json::const_pointer, + typename basic_json::pointer>::type; /// defines a reference to the type iterated over (value_type) - using reference = typename basic_json::const_reference; + using reference = typename std::conditional<std::is_const<U>::value, + typename basic_json::const_reference, + typename basic_json::reference>::type; /// the category of the iterator using iterator_category = std::bidirectional_iterator_tag; /// default constructor - const_iterator() = default; + iter_impl() = default; /*! @brief constructor for a given JSON instance @@ -6628,7 +8333,7 @@ class basic_json @pre object != nullptr @post The iterator is initialized; i.e. `m_object != nullptr`. */ - explicit const_iterator(pointer object) noexcept + explicit iter_impl(pointer object) noexcept : m_object(object) { assert(m_object != nullptr); @@ -6655,37 +8360,25 @@ class basic_json } } - /*! - @brief copy constructor given a non-const iterator - @param[in] other iterator to copy from - @note It is not checked whether @a other is initialized. + /* + Use operator `const_iterator` instead of `const_iterator(const iterator& + other) noexcept` to avoid two class definitions for @ref iterator and + @ref const_iterator. + + This function is only called if this class is an @ref iterator. If this + class is a @ref const_iterator this function is not called. */ - explicit const_iterator(const iterator& other) noexcept - : m_object(other.m_object) + operator const_iterator() const { - if (m_object != nullptr) - { - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - m_it.object_iterator = other.m_it.object_iterator; - break; - } + const_iterator ret; - case basic_json::value_t::array: - { - m_it.array_iterator = other.m_it.array_iterator; - break; - } - - default: - { - m_it.primitive_iterator = other.m_it.primitive_iterator; - break; - } - } + if (m_object) + { + ret.m_object = m_object; + ret.m_it = m_it; } + + return ret; } /*! @@ -6693,7 +8386,7 @@ class basic_json @param[in] other iterator to copy from @note It is not checked whether @a other is initialized. */ - const_iterator(const const_iterator& other) noexcept + iter_impl(const iter_impl& other) noexcept : m_object(other.m_object), m_it(other.m_it) {} @@ -6702,7 +8395,7 @@ class basic_json @param[in,out] other iterator to copy from @note It is not checked whether @a other is initialized. */ - const_iterator& operator=(const_iterator other) noexcept( + iter_impl& operator=(iter_impl other) noexcept( std::is_nothrow_move_constructible<pointer>::value and std::is_nothrow_move_assignable<pointer>::value and std::is_nothrow_move_constructible<internal_iterator>::value and @@ -6864,7 +8557,7 @@ class basic_json @brief post-increment (it++) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator++(int) + iter_impl operator++(int) { auto result = *this; ++(*this); @@ -6875,7 +8568,7 @@ class basic_json @brief pre-increment (++it) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator++() + iter_impl& operator++() { assert(m_object != nullptr); @@ -6907,7 +8600,7 @@ class basic_json @brief post-decrement (it--) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator--(int) + iter_impl operator--(int) { auto result = *this; --(*this); @@ -6918,7 +8611,7 @@ class basic_json @brief pre-decrement (--it) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator--() + iter_impl& operator--() { assert(m_object != nullptr); @@ -6950,7 +8643,7 @@ class basic_json @brief comparison: equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator==(const const_iterator& other) const + bool operator==(const iter_impl& other) const { // if objects are not the same, the comparison is undefined if (m_object != other.m_object) @@ -6983,7 +8676,7 @@ class basic_json @brief comparison: not equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator!=(const const_iterator& other) const + bool operator!=(const iter_impl& other) const { return not operator==(other); } @@ -6992,7 +8685,7 @@ class basic_json @brief comparison: smaller @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator<(const const_iterator& other) const + bool operator<(const iter_impl& other) const { // if objects are not the same, the comparison is undefined if (m_object != other.m_object) @@ -7025,7 +8718,7 @@ class basic_json @brief comparison: less than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator<=(const const_iterator& other) const + bool operator<=(const iter_impl& other) const { return not other.operator < (*this); } @@ -7034,7 +8727,7 @@ class basic_json @brief comparison: greater than @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator>(const const_iterator& other) const + bool operator>(const iter_impl& other) const { return not operator<=(other); } @@ -7043,7 +8736,7 @@ class basic_json @brief comparison: greater than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator>=(const const_iterator& other) const + bool operator>=(const iter_impl& other) const { return not operator<(other); } @@ -7052,7 +8745,7 @@ class basic_json @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator+=(difference_type i) + iter_impl& operator+=(difference_type i) { assert(m_object != nullptr); @@ -7083,7 +8776,7 @@ class basic_json @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator-=(difference_type i) + iter_impl& operator-=(difference_type i) { return operator+=(-i); } @@ -7092,7 +8785,7 @@ class basic_json @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator+(difference_type i) + iter_impl operator+(difference_type i) { auto result = *this; result += i; @@ -7103,7 +8796,7 @@ class basic_json @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator-(difference_type i) + iter_impl operator-(difference_type i) { auto result = *this; result -= i; @@ -7114,7 +8807,7 @@ class basic_json @brief return difference @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - difference_type operator-(const const_iterator& other) const + difference_type operator-(const iter_impl& other) const { assert(m_object != nullptr); @@ -7211,141 +8904,6 @@ class basic_json }; /*! - @brief a mutable random access iterator for the @ref basic_json class - - @requirement The class satisfies the following concept requirements: - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): - The iterator that can be moved to point (forward and backward) to any - element in constant time. - - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): - It is possible to write to the pointed-to element. - - @since version 1.0.0 - */ - class iterator : public const_iterator - { - public: - using base_iterator = const_iterator; - using pointer = typename basic_json::pointer; - using reference = typename basic_json::reference; - - /// default constructor - iterator() = default; - - /// constructor for a given JSON instance - explicit iterator(pointer object) noexcept - : base_iterator(object) - {} - - /// copy constructor - iterator(const iterator& other) noexcept - : base_iterator(other) - {} - - /// copy assignment - iterator& operator=(iterator other) noexcept( - std::is_nothrow_move_constructible<pointer>::value and - std::is_nothrow_move_assignable<pointer>::value and - std::is_nothrow_move_constructible<internal_iterator>::value and - std::is_nothrow_move_assignable<internal_iterator>::value - ) - { - base_iterator::operator=(other); - return *this; - } - - /// return a reference to the value pointed to by the iterator - reference operator*() const - { - return const_cast<reference>(base_iterator::operator*()); - } - - /// dereference the iterator - pointer operator->() const - { - return const_cast<pointer>(base_iterator::operator->()); - } - - /// post-increment (it++) - iterator operator++(int) - { - iterator result = *this; - base_iterator::operator++(); - return result; - } - - /// pre-increment (++it) - iterator& operator++() - { - base_iterator::operator++(); - return *this; - } - - /// post-decrement (it--) - iterator operator--(int) - { - iterator result = *this; - base_iterator::operator--(); - return result; - } - - /// pre-decrement (--it) - iterator& operator--() - { - base_iterator::operator--(); - return *this; - } - - /// add to iterator - iterator& operator+=(difference_type i) - { - base_iterator::operator+=(i); - return *this; - } - - /// subtract from iterator - iterator& operator-=(difference_type i) - { - base_iterator::operator-=(i); - return *this; - } - - /// add to iterator - iterator operator+(difference_type i) - { - auto result = *this; - result += i; - return result; - } - - /// subtract from iterator - iterator operator-(difference_type i) - { - auto result = *this; - result -= i; - return result; - } - - /// return difference - difference_type operator-(const iterator& other) const - { - return base_iterator::operator-(other); - } - - /// access to successor - reference operator[](difference_type n) const - { - return const_cast<reference>(base_iterator::operator[](n)); - } - - /// return the value of an iterator - reference value() const - { - return const_cast<reference>(base_iterator::value()); - } - }; - - /*! @brief a template for a reverse iterator class @tparam Base the base iterator type to reverse. Valid types are @ref @@ -7495,32 +9053,39 @@ class basic_json /// the char type to use in the lexer using lexer_char_t = unsigned char; - /// constructor with a given buffer - explicit lexer(const string_t& s) noexcept - : m_stream(nullptr), m_buffer(s) + /// a lexer from a buffer with given length + lexer(const lexer_char_t* buff, const size_t len) noexcept + : m_content(buff) { - m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str()); assert(m_content != nullptr); m_start = m_cursor = m_content; - m_limit = m_content + s.size(); + m_limit = m_content + len; } - /// constructor with a given stream - explicit lexer(std::istream* s) noexcept - : m_stream(s), m_buffer() + /// a lexer from an input stream + explicit lexer(std::istream& s) + : m_stream(&s), m_line_buffer() { - assert(m_stream != nullptr); - std::getline(*m_stream, m_buffer); - m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str()); - assert(m_content != nullptr); - m_start = m_cursor = m_content; - m_limit = m_content + m_buffer.size(); - } + // immediately abort if stream is erroneous + if (s.fail()) + { + throw std::invalid_argument("stream error"); + } - /// default constructor - lexer() = default; + // fill buffer + fill_line_buffer(); + + // skip UTF-8 byte-order mark + if (m_line_buffer.size() >= 3 and m_line_buffer.substr(0, 3) == "\xEF\xBB\xBF") + { + m_line_buffer[0] = ' '; + m_line_buffer[1] = ' '; + m_line_buffer[2] = ' '; + } + } - // switch off unwanted functions + // switch off unwanted functions (due to pointer members) + lexer() = delete; lexer(const lexer&) = delete; lexer operator=(const lexer&) = delete; @@ -7673,7 +9238,7 @@ class basic_json infinite sequence of whitespace or byte-order-marks. This contradicts the assumption of finite input, q.e.d. */ - token_type scan() noexcept + token_type scan() { while (true) { @@ -7706,33 +9271,33 @@ class basic_json 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, }; if ((m_limit - m_cursor) < 5) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(5); // LCOV_EXCL_LINE } yych = *m_cursor; if (yybm[0 + yych] & 32) { goto basic_json_parser_6; } - if (yych <= '\\') + if (yych <= '[') { if (yych <= '-') { @@ -7781,62 +9346,58 @@ class basic_json { goto basic_json_parser_17; } - if (yych == '[') + if (yych <= 'Z') { - goto basic_json_parser_19; + goto basic_json_parser_4; } - goto basic_json_parser_4; + goto basic_json_parser_19; } } } else { - if (yych <= 't') + if (yych <= 'n') { - if (yych <= 'f') + if (yych <= 'e') { - if (yych <= ']') + if (yych == ']') { goto basic_json_parser_21; } - if (yych <= 'e') - { - goto basic_json_parser_4; - } - goto basic_json_parser_23; + goto basic_json_parser_4; } else { - if (yych == 'n') + if (yych <= 'f') { - goto basic_json_parser_24; + goto basic_json_parser_23; } - if (yych <= 's') + if (yych <= 'm') { goto basic_json_parser_4; } - goto basic_json_parser_25; + goto basic_json_parser_24; } } else { - if (yych <= '|') + if (yych <= 'z') { - if (yych == '{') + if (yych == 't') { - goto basic_json_parser_26; + goto basic_json_parser_25; } goto basic_json_parser_4; } else { - if (yych <= '}') + if (yych <= '{') { - goto basic_json_parser_28; + goto basic_json_parser_26; } - if (yych == 0xEF) + if (yych == '}') { - goto basic_json_parser_30; + goto basic_json_parser_28; } goto basic_json_parser_4; } @@ -7859,7 +9420,7 @@ basic_json_parser_6: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yybm[0 + yych] & 32) @@ -7876,7 +9437,19 @@ basic_json_parser_9: { goto basic_json_parser_5; } - goto basic_json_parser_32; + if (yych <= 0x7F) + { + goto basic_json_parser_31; + } + if (yych <= 0xC1) + { + goto basic_json_parser_5; + } + if (yych <= 0xF4) + { + goto basic_json_parser_31; + } + goto basic_json_parser_5; basic_json_parser_10: ++m_cursor; { @@ -7905,18 +9478,18 @@ basic_json_parser_13: { if (yych == '.') { - goto basic_json_parser_37; + goto basic_json_parser_43; } } else { if (yych <= 'E') { - goto basic_json_parser_38; + goto basic_json_parser_44; } if (yych == 'e') { - goto basic_json_parser_38; + goto basic_json_parser_44; } } basic_json_parser_14: @@ -7929,7 +9502,7 @@ basic_json_parser_15: m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(3); // LCOV_EXCL_LINE } yych = *m_cursor; if (yybm[0 + yych] & 64) @@ -7940,7 +9513,7 @@ basic_json_parser_15: { if (yych == '.') { - goto basic_json_parser_37; + goto basic_json_parser_43; } goto basic_json_parser_14; } @@ -7948,11 +9521,11 @@ basic_json_parser_15: { if (yych <= 'E') { - goto basic_json_parser_38; + goto basic_json_parser_44; } if (yych == 'e') { - goto basic_json_parser_38; + goto basic_json_parser_44; } goto basic_json_parser_14; } @@ -7979,7 +9552,7 @@ basic_json_parser_23: yych = *(m_marker = ++m_cursor); if (yych == 'a') { - goto basic_json_parser_39; + goto basic_json_parser_45; } goto basic_json_parser_5; basic_json_parser_24: @@ -7987,7 +9560,7 @@ basic_json_parser_24: yych = *(m_marker = ++m_cursor); if (yych == 'u') { - goto basic_json_parser_40; + goto basic_json_parser_46; } goto basic_json_parser_5; basic_json_parser_25: @@ -7995,7 +9568,7 @@ basic_json_parser_25: yych = *(m_marker = ++m_cursor); if (yych == 'r') { - goto basic_json_parser_41; + goto basic_json_parser_47; } goto basic_json_parser_5; basic_json_parser_26: @@ -8011,35 +9584,71 @@ basic_json_parser_28: break; } basic_json_parser_30: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 0xBB) - { - goto basic_json_parser_42; - } - goto basic_json_parser_5; -basic_json_parser_31: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; -basic_json_parser_32: +basic_json_parser_31: if (yybm[0 + yych] & 128) { - goto basic_json_parser_31; + goto basic_json_parser_30; } - if (yych <= 0x1F) + if (yych <= 0xE0) { - goto basic_json_parser_33; + if (yych <= '\\') + { + if (yych <= 0x1F) + { + goto basic_json_parser_32; + } + if (yych <= '"') + { + goto basic_json_parser_33; + } + goto basic_json_parser_35; + } + else + { + if (yych <= 0xC1) + { + goto basic_json_parser_32; + } + if (yych <= 0xDF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_37; + } } - if (yych <= '"') + else { - goto basic_json_parser_34; + if (yych <= 0xEF) + { + if (yych == 0xED) + { + goto basic_json_parser_39; + } + goto basic_json_parser_38; + } + else + { + if (yych <= 0xF0) + { + goto basic_json_parser_40; + } + if (yych <= 0xF3) + { + goto basic_json_parser_41; + } + if (yych <= 0xF4) + { + goto basic_json_parser_42; + } + } } - goto basic_json_parser_36; -basic_json_parser_33: +basic_json_parser_32: m_cursor = m_marker; if (yyaccept == 0) { @@ -8049,17 +9658,17 @@ basic_json_parser_33: { goto basic_json_parser_14; } -basic_json_parser_34: +basic_json_parser_33: ++m_cursor; { last_token_type = token_type::value_string; break; } -basic_json_parser_36: +basic_json_parser_35: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= 'e') @@ -8068,13 +9677,13 @@ basic_json_parser_36: { if (yych == '"') { - goto basic_json_parser_31; + goto basic_json_parser_30; } if (yych <= '.') { - goto basic_json_parser_33; + goto basic_json_parser_32; } - goto basic_json_parser_31; + goto basic_json_parser_30; } else { @@ -8082,17 +9691,17 @@ basic_json_parser_36: { if (yych <= '[') { - goto basic_json_parser_33; + goto basic_json_parser_32; } - goto basic_json_parser_31; + goto basic_json_parser_30; } else { if (yych == 'b') { - goto basic_json_parser_31; + goto basic_json_parser_30; } - goto basic_json_parser_33; + goto basic_json_parser_32; } } } @@ -8102,13 +9711,13 @@ basic_json_parser_36: { if (yych <= 'f') { - goto basic_json_parser_31; + goto basic_json_parser_30; } if (yych == 'n') { - goto basic_json_parser_31; + goto basic_json_parser_30; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { @@ -8116,130 +9725,235 @@ basic_json_parser_36: { if (yych <= 'r') { - goto basic_json_parser_31; + goto basic_json_parser_30; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= 't') { - goto basic_json_parser_31; + goto basic_json_parser_30; } if (yych <= 'u') { - goto basic_json_parser_43; + goto basic_json_parser_48; } - goto basic_json_parser_33; + goto basic_json_parser_32; } } } +basic_json_parser_36: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; basic_json_parser_37: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x9F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_38: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_39: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0x9F) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_40: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x8F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_41: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_42: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0x8F) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_43: yych = *++m_cursor; if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_44; + goto basic_json_parser_49; } - goto basic_json_parser_33; -basic_json_parser_38: + goto basic_json_parser_32; +basic_json_parser_44: yych = *++m_cursor; if (yych <= ',') { if (yych == '+') { - goto basic_json_parser_46; + goto basic_json_parser_51; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= '-') { - goto basic_json_parser_46; + goto basic_json_parser_51; } if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_47; + goto basic_json_parser_52; } - goto basic_json_parser_33; + goto basic_json_parser_32; } -basic_json_parser_39: +basic_json_parser_45: yych = *++m_cursor; if (yych == 'l') { - goto basic_json_parser_49; + goto basic_json_parser_54; } - goto basic_json_parser_33; -basic_json_parser_40: + goto basic_json_parser_32; +basic_json_parser_46: yych = *++m_cursor; if (yych == 'l') { - goto basic_json_parser_50; + goto basic_json_parser_55; } - goto basic_json_parser_33; -basic_json_parser_41: + goto basic_json_parser_32; +basic_json_parser_47: yych = *++m_cursor; if (yych == 'u') { - goto basic_json_parser_51; - } - goto basic_json_parser_33; -basic_json_parser_42: - yych = *++m_cursor; - if (yych == 0xBF) - { - goto basic_json_parser_52; + goto basic_json_parser_56; } - goto basic_json_parser_33; -basic_json_parser_43: + goto basic_json_parser_32; +basic_json_parser_48: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '@') { if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_54; + goto basic_json_parser_57; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= 'F') { - goto basic_json_parser_54; + goto basic_json_parser_57; } if (yych <= '`') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= 'f') { - goto basic_json_parser_54; + goto basic_json_parser_57; } - goto basic_json_parser_33; + goto basic_json_parser_32; } -basic_json_parser_44: +basic_json_parser_49: yyaccept = 1; m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(3); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= 'D') @@ -8250,7 +9964,7 @@ basic_json_parser_44: } if (yych <= '9') { - goto basic_json_parser_44; + goto basic_json_parser_49; } goto basic_json_parser_14; } @@ -8258,29 +9972,29 @@ basic_json_parser_44: { if (yych <= 'E') { - goto basic_json_parser_38; + goto basic_json_parser_44; } if (yych == 'e') { - goto basic_json_parser_38; + goto basic_json_parser_44; } goto basic_json_parser_14; } -basic_json_parser_46: +basic_json_parser_51: yych = *++m_cursor; if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych >= ':') { - goto basic_json_parser_33; + goto basic_json_parser_32; } -basic_json_parser_47: +basic_json_parser_52: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '/') @@ -8289,164 +10003,159 @@ basic_json_parser_47: } if (yych <= '9') { - goto basic_json_parser_47; + goto basic_json_parser_52; } goto basic_json_parser_14; -basic_json_parser_49: +basic_json_parser_54: yych = *++m_cursor; if (yych == 's') { - goto basic_json_parser_55; + goto basic_json_parser_58; } - goto basic_json_parser_33; -basic_json_parser_50: + goto basic_json_parser_32; +basic_json_parser_55: yych = *++m_cursor; if (yych == 'l') { - goto basic_json_parser_56; + goto basic_json_parser_59; } - goto basic_json_parser_33; -basic_json_parser_51: + goto basic_json_parser_32; +basic_json_parser_56: yych = *++m_cursor; if (yych == 'e') { - goto basic_json_parser_58; - } - goto basic_json_parser_33; -basic_json_parser_52: - ++m_cursor; - { - continue; + goto basic_json_parser_61; } -basic_json_parser_54: + goto basic_json_parser_32; +basic_json_parser_57: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '@') { if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_60; + goto basic_json_parser_63; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= 'F') { - goto basic_json_parser_60; + goto basic_json_parser_63; } if (yych <= '`') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= 'f') { - goto basic_json_parser_60; + goto basic_json_parser_63; } - goto basic_json_parser_33; + goto basic_json_parser_32; } -basic_json_parser_55: +basic_json_parser_58: yych = *++m_cursor; if (yych == 'e') { - goto basic_json_parser_61; + goto basic_json_parser_64; } - goto basic_json_parser_33; -basic_json_parser_56: + goto basic_json_parser_32; +basic_json_parser_59: ++m_cursor; { last_token_type = token_type::literal_null; break; } -basic_json_parser_58: +basic_json_parser_61: ++m_cursor; { last_token_type = token_type::literal_true; break; } -basic_json_parser_60: +basic_json_parser_63: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '@') { if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_63; + goto basic_json_parser_66; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= 'F') { - goto basic_json_parser_63; + goto basic_json_parser_66; } if (yych <= '`') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= 'f') { - goto basic_json_parser_63; + goto basic_json_parser_66; } - goto basic_json_parser_33; + goto basic_json_parser_32; } -basic_json_parser_61: +basic_json_parser_64: ++m_cursor; { last_token_type = token_type::literal_false; break; } -basic_json_parser_63: +basic_json_parser_66: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '@') { if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_31; + goto basic_json_parser_30; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= 'F') { - goto basic_json_parser_31; + goto basic_json_parser_30; } if (yych <= '`') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= 'f') { - goto basic_json_parser_31; + goto basic_json_parser_30; } - goto basic_json_parser_33; + goto basic_json_parser_32; } } @@ -8455,30 +10164,93 @@ basic_json_parser_63: return last_token_type; } - /// append data from the stream to the internal buffer - void yyfill() noexcept - { - if (m_stream == nullptr or not * m_stream) + /*! + @brief append data from the stream to the line buffer + + This function is called by the scan() function when the end of the + buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be + incremented without leaving the limits of the line buffer. Note re2c + decides when to call this function. + + If the lexer reads from contiguous storage, there is no trailing null + byte. Therefore, this function must make sure to add these padding + null bytes. + + If the lexer reads from an input stream, this function reads the next + line of the input. + + @pre + p p p p p p u u u u u x . . . . . . + ^ ^ ^ ^ + m_content m_start | m_limit + m_cursor + + @post + u u u u u x x x x x x x . . . . . . + ^ ^ ^ + | m_cursor m_limit + m_start + m_content + */ + void fill_line_buffer(size_t n = 0) + { + // if line buffer is used, m_content points to its data + assert(m_line_buffer.empty() + or m_content == reinterpret_cast<const lexer_char_t*>(m_line_buffer.data())); + + // if line buffer is used, m_limit is set past the end of its data + assert(m_line_buffer.empty() + or m_limit == m_content + m_line_buffer.size()); + + // pointer relationships + assert(m_content <= m_start); + assert(m_start <= m_cursor); + assert(m_cursor <= m_limit); + assert(m_marker == nullptr or m_marker <= m_limit); + + // number of processed characters (p) + const size_t num_processed_chars = static_cast<size_t>(m_start - m_content); + // offset for m_marker wrt. to m_start + const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start; + // number of unprocessed characters (u) + const auto offset_cursor = m_cursor - m_start; + + // no stream is used or end of file is reached + if (m_stream == nullptr or m_stream->eof()) { - return; - } + // m_start may or may not be pointing into m_line_buffer at + // this point. We trust the standand library to do the right + // thing. See http://stackoverflow.com/q/28142011/266378 + m_line_buffer.assign(m_start, m_limit); - const auto offset_start = m_start - m_content; - const auto offset_marker = m_marker - m_start; - const auto offset_cursor = m_cursor - m_start; + // append n characters to make sure that there is sufficient + // space between m_cursor and m_limit + m_line_buffer.append(1, '\x00'); + if (n > 0) + { + m_line_buffer.append(n - 1, '\x01'); + } + } + else + { + // delete processed characters from line buffer + m_line_buffer.erase(0, num_processed_chars); + // read next line from input stream + m_line_buffer_tmp.clear(); + std::getline(*m_stream, m_line_buffer_tmp, '\n'); - m_buffer.erase(0, static_cast<size_t>(offset_start)); - std::string line; - assert(m_stream != nullptr); - std::getline(*m_stream, line); - m_buffer += "\n" + line; // add line with newline symbol + // add line with newline symbol to the line buffer + m_line_buffer += m_line_buffer_tmp; + m_line_buffer.push_back('\n'); + } - m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str()); + // set pointers + m_content = reinterpret_cast<const lexer_char_t*>(m_line_buffer.data()); assert(m_content != nullptr); m_start = m_content; m_marker = m_start + offset_marker; m_cursor = m_start + offset_cursor; - m_limit = m_start + m_buffer.size() - 1; + m_limit = m_start + m_line_buffer.size(); } /// return string representation of last read token @@ -8556,9 +10328,20 @@ basic_json_parser_63: // iterate the result between the quotes for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++i) { - // process escaped characters - if (*i == '\\') + // find next escape character + auto e = std::find(i, m_cursor - 1, '\\'); + if (e != i) + { + // see https://github.com/nlohmann/json/issues/365#issuecomment-262874705 + for (auto k = i; k < e; k++) + { + result.push_back(static_cast<typename string_t::value_type>(*k)); + } + i = e - 1; // -1 because of ++i + } + else { + // processing escaped character // read next character ++i; @@ -8629,6 +10412,11 @@ basic_json_parser_63: // skip the next 10 characters (xxxx\uyyyy) i += 10; } + else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF) + { + // we found a lone low surrogate + throw std::invalid_argument("missing high surrogate"); + } else { // add unicode character(s) @@ -8640,12 +10428,6 @@ basic_json_parser_63: } } } - else - { - // all other characters are just copied to the end of the - // string - result.append(1, static_cast<typename string_t::value_type>(*i)); - } } return result; @@ -8659,8 +10441,6 @@ basic_json_parser_63: supplied via the first parameter. Set this to @a static_cast<number_float_t*>(nullptr). - @param[in] type the @ref number_float_t in use - @param[in,out] endptr recieves a pointer to the first character after the number @@ -8679,8 +10459,6 @@ basic_json_parser_63: supplied via the first parameter. Set this to @a static_cast<number_float_t*>(nullptr). - @param[in] type the @ref number_float_t in use - @param[in,out] endptr recieves a pointer to the first character after the number @@ -8699,8 +10477,6 @@ basic_json_parser_63: supplied via the first parameter. Set this to @a static_cast<number_float_t*>(nullptr). - @param[in] type the @ref number_float_t in use - @param[in,out] endptr recieves a pointer to the first character after the number @@ -8781,19 +10557,19 @@ basic_json_parser_63: // skip if definitely not an integer if (type != value_t::number_float) { - // multiply last value by ten and add the new digit - auto temp = value * 10 + *curptr - '0'; + auto digit = static_cast<number_unsigned_t>(*curptr - '0'); - // test for overflow - if (temp < value || temp > max) + // overflow if value * 10 + digit > max, move terms around + // to avoid overflow in intermediate values + if (value > (max - digit) / 10) { // overflow type = value_t::number_float; } else { - // no overflow - save it - value = temp; + // no overflow + value = value * 10 + digit; } } } @@ -8805,12 +10581,34 @@ basic_json_parser_63: } else if (type == value_t::number_integer) { - result.m_value.number_integer = -static_cast<number_integer_t>(value); + // invariant: if we parsed a '-', the absolute value is between + // 0 (we allow -0) and max == -INT64_MIN + assert(value >= 0); + assert(value <= max); + + if (value == max) + { + // we cannot simply negate value (== max == -INT64_MIN), + // see https://github.com/nlohmann/json/issues/389 + result.m_value.number_integer = static_cast<number_integer_t>(INT64_MIN); + } + else + { + // all other values can be negated safely + result.m_value.number_integer = -static_cast<number_integer_t>(value); + } } else { // parse with strtod result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), NULL); + + // replace infinity and NAN by null + if (not std::isfinite(result.m_value.number_float)) + { + type = value_t::null; + result.m_value = basic_json::json_value(); + } } // save the type @@ -8820,8 +10618,10 @@ basic_json_parser_63: private: /// optional input stream std::istream* m_stream = nullptr; - /// the buffer - string_t m_buffer; + /// line buffer buffer for m_stream + string_t m_line_buffer {}; + /// used for filling m_line_buffer + string_t m_line_buffer_tmp {}; /// the buffer pointer const lexer_char_t* m_content = nullptr; /// pointer to the beginning of the current symbol @@ -8844,25 +10644,34 @@ basic_json_parser_63: class parser { public: - /// constructor for strings - parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept - : callback(cb), m_lexer(s) - { - // read first token - get_token(); - } + /// a parser reading from a string literal + parser(const char* buff, const parser_callback_t cb = nullptr) + : callback(cb), + m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(buff), std::strlen(buff)) + {} /// a parser reading from an input stream - parser(std::istream& _is, const parser_callback_t cb = nullptr) noexcept - : callback(cb), m_lexer(&_is) - { - // read first token - get_token(); - } + parser(std::istream& is, const parser_callback_t cb = nullptr) + : callback(cb), m_lexer(is) + {} + + /// a parser reading from an 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> + parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) + : callback(cb), + m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(&(*first)), + static_cast<size_t>(std::distance(first, last))) + {} /// public parser interface basic_json parse() { + // read first token + get_token(); + basic_json result = parse_internal(true); result.assert_invariant(); @@ -8883,7 +10692,8 @@ basic_json_parser_63: { case lexer::token_type::begin_object: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::object_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) { // explicitly set result to object to cope with {} result.m_type = value_t::object; @@ -8961,7 +10771,8 @@ basic_json_parser_63: case lexer::token_type::begin_array: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::array_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::array_start, result)) != 0))) { // explicitly set result to object to cope with [] result.m_type = value_t::array; @@ -9067,7 +10878,7 @@ basic_json_parser_63: } /// get next token from lexer - typename lexer::token_type get_token() noexcept + typename lexer::token_type get_token() { last_token = m_lexer.scan(); return last_token; @@ -9280,6 +11091,12 @@ basic_json_parser_63: /*! @brief return a reference to the pointed to value + @note This version does not throw if a value is not present, but tries + to create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + @param[in] ptr a JSON value @return reference to the JSON value pointed to by the JSON pointer @@ -9294,6 +11111,29 @@ basic_json_parser_63: { for (const auto& reference_token : reference_tokens) { + // convert null values to arrays or objects before continuing + if (ptr->m_type == value_t::null) + { + // check if reference token is a number + const bool nums = std::all_of(reference_token.begin(), + reference_token.end(), + [](const char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object + // otherwise + if (nums or reference_token == "-") + { + *ptr = value_t::array; + } + else + { + *ptr = value_t::object; + } + } + switch (ptr->m_type) { case value_t::object: @@ -9475,7 +11315,7 @@ basic_json_parser_63: } /// split the string input to reference tokens - static std::vector<std::string> split(std::string reference_string) + static std::vector<std::string> split(const std::string& reference_string) { std::vector<std::string> result; @@ -9539,13 +11379,11 @@ basic_json_parser_63: /*! @brief replace all occurrences of a substring by another string - @param[in,out] s the string to manipulate + @param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t @param[in] f the substring to replace with @a t @param[in] t the string to replace @a f - @return The string @a s where all occurrences of @a f are replaced - with @a t. - @pre The search string @a f must not be empty. @since version 2.0.0 @@ -9960,7 +11798,7 @@ basic_json_parser_63: json_pointer top_pointer = ptr.top(); if (top_pointer != ptr) { - basic_json& x = result.at(top_pointer); + result.at(top_pointer); } // get reference to parent of JSON pointer ptr @@ -10203,7 +12041,7 @@ basic_json_parser_63: */ static basic_json diff(const basic_json& source, const basic_json& target, - std::string path = "") + const std::string& path = "") { // the patch basic_json result(value_t::array); @@ -10365,7 +12203,7 @@ namespace std @since version 1.0.0 */ -template <> +template<> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( is_nothrow_move_constructible<nlohmann::json>::value and @@ -10376,7 +12214,7 @@ inline void swap(nlohmann::json& j1, } /// hash value for JSON objects -template <> +template<> struct hash<nlohmann::json> { /*! @@ -10401,30 +12239,32 @@ can be used by adding `"_json"` to a string literal and returns a JSON object if no parse error occurred. @param[in] s a string representation of a JSON object +@param[in] n the length of string @a s @return a JSON object @since version 1.0.0 */ -inline nlohmann::json operator "" _json(const char* s, std::size_t) +inline nlohmann::json operator "" _json(const char* s, std::size_t n) { - return nlohmann::json::parse(reinterpret_cast<const nlohmann::json::string_t::value_type*>(s)); + return nlohmann::json::parse(s, s + n); } /*! @brief user-defined string literal for JSON pointer This operator implements a user-defined string literal for JSON Pointers. It -can be used by adding `"_json"` to a string literal and returns a JSON pointer +can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer object if no parse error occurred. @param[in] s a string representation of a JSON Pointer +@param[in] n the length of string @a s @return a JSON pointer object @since version 2.0.0 */ -inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t) +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) { - return nlohmann::json::json_pointer(s); + return nlohmann::json::json_pointer(std::string(s, n)); } // restore GCC/clang diagnostic settings |