diff --git a/src/json.hpp b/src/json.hpp index f883d9ed..7325e7c8 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -126,12 +126,12 @@ using conditional_t = typename std::conditional<If, Then, Else>::type; template <typename T> using is_scoped_enum = std::integral_constant<bool, not std::is_convertible<T, int>::value and - std::is_enum<T>::value>; + std::is_enum<T>::value>; template <typename T> using is_unscoped_enum = std::integral_constant<bool, std::is_convertible<T, int>::value and - std::is_enum<T>::value>; + std::is_enum<T>::value>; // TODO update this doc /*! @@ -142,7 +142,10 @@ using is_unscoped_enum = namespace detail { // Very useful construct against boilerplate (more boilerplate needed than in C++17: http://en.cppreference.com/w/cpp/types/void_t) -template <typename...> struct make_void { using type = void; }; +template <typename...> struct make_void +{ + using type = void; +}; template <typename... Ts> using void_t = typename make_void<Ts...>::type; // Implementation of 3 C++17 constructs: conjunction, disjunction, negation. @@ -160,14 +163,14 @@ template <class...> struct conjunction : std::true_type {}; template <class B1> struct conjunction<B1> : B1 {}; template <class B1, class... Bn> struct conjunction<B1, Bn...> - : conditional_t<bool(B1::value), conjunction<Bn...>, B1> {}; +: conditional_t<bool(B1::value), conjunction<Bn...>, B1> {}; -template <class B> struct negation : std::integral_constant<bool, !B::value> {}; +template <class B> struct negation : std::integral_constant < bool, !B::value > {}; template <class...> struct disjunction : std::false_type {}; template <class B1> struct disjunction<B1> : B1 {}; template <class B1, class... Bn> struct disjunction<B1, Bn...> - : conditional_t<bool(B1::value), B1, disjunction<Bn...>> {}; +: conditional_t<bool(B1::value), B1, disjunction<Bn...>> {}; /*! @brief Helper to determine whether there's a key_type for T. @@ -178,17 +181,17 @@ contains a `mapped_type`, whereas `std::vector` fails the test. @since version 1.0.0, overworked in version 2.0.6 */ #define NLOHMANN_JSON_HAS_HELPER(type) \ - template <typename T> struct has_##type { \ - private: \ - template <typename U, typename = typename U::type> \ - static int detect(U &&); \ - \ - static void detect(...); \ - \ - public: \ - static constexpr bool value = \ - std::is_integral<decltype(detect(std::declval<T>()))>::value; \ - }; + template <typename T> struct has_##type { \ + private: \ + template <typename U, typename = typename U::type> \ + static int detect(U &&); \ + \ + static void detect(...); \ + \ + public: \ + static constexpr bool value = \ + std::is_integral<decltype(detect(std::declval<T>()))>::value; \ + }; NLOHMANN_JSON_HAS_HELPER(mapped_type) NLOHMANN_JSON_HAS_HELPER(key_type) @@ -198,122 +201,122 @@ NLOHMANN_JSON_HAS_HELPER(iterator) #undef NLOHMANN_JSON_HAS_HELPER template <bool B, class RealType, class CompatibleObjectType> -struct is_compatible_object_type_impl : std::false_type{}; +struct is_compatible_object_type_impl : std::false_type {}; template <class RealType, class CompatibleObjectType> struct is_compatible_object_type_impl<true, RealType, CompatibleObjectType> { - static constexpr auto value = - std::is_constructible<typename RealType::key_type, - typename CompatibleObjectType::key_type>::value and - std::is_constructible<typename RealType::mapped_type, - typename CompatibleObjectType::mapped_type>::value; + static constexpr auto value = + std::is_constructible<typename RealType::key_type, + typename CompatibleObjectType::key_type>::value and + std::is_constructible<typename RealType::mapped_type, + typename CompatibleObjectType::mapped_type>::value; }; template<class RealType, class CompatibleObjectType> struct is_compatible_object_type { - // As noted ahead, we need to stop evaluating traits if CompatibleObjectType = void - // hence the conjunction - static auto constexpr value = is_compatible_object_type_impl< - conjunction<negation<std::is_same<void, CompatibleObjectType>>, - has_mapped_type<CompatibleObjectType>, - has_key_type<CompatibleObjectType>>::value, - RealType, CompatibleObjectType>::value; + // As noted ahead, we need to stop evaluating traits if CompatibleObjectType = void + // hence the conjunction + static auto constexpr value = is_compatible_object_type_impl < + conjunction<negation<std::is_same<void, CompatibleObjectType>>, + has_mapped_type<CompatibleObjectType>, + has_key_type<CompatibleObjectType>>::value, + RealType, CompatibleObjectType >::value; }; template <bool B, class BasicJson, class CompatibleArrayType> -struct is_compatible_array_type_impl : std::false_type{}; +struct is_compatible_array_type_impl : std::false_type {}; template <class BasicJson, class CompatibleArrayType> struct is_compatible_array_type_impl<true, BasicJson, CompatibleArrayType> { - static constexpr auto value = - not std::is_same<CompatibleArrayType, - typename BasicJson::iterator>::value and - not std::is_same<CompatibleArrayType, - typename BasicJson::const_iterator>::value and - not std::is_same<CompatibleArrayType, - typename BasicJson::reverse_iterator>::value and - not std::is_same<CompatibleArrayType, - typename BasicJson::const_reverse_iterator>::value and - not std::is_same<CompatibleArrayType, - typename BasicJson::array_t::iterator>::value and - not std::is_same<CompatibleArrayType, - typename BasicJson::array_t::const_iterator>::value; + static constexpr auto value = + not std::is_same<CompatibleArrayType, + typename BasicJson::iterator>::value and + not std::is_same<CompatibleArrayType, + typename BasicJson::const_iterator>::value and + not std::is_same<CompatibleArrayType, + typename BasicJson::reverse_iterator>::value and + not std::is_same<CompatibleArrayType, + typename BasicJson::const_reverse_iterator>::value and + not std::is_same<CompatibleArrayType, + typename BasicJson::array_t::iterator>::value and + not std::is_same<CompatibleArrayType, + typename BasicJson::array_t::const_iterator>::value; }; template <class BasicJson, class CompatibleArrayType> struct is_compatible_array_type { - // the check for CompatibleArrayType = void is done in is_compatible_object_type - // but we need the conjunction here as well - static auto constexpr value = is_compatible_array_type_impl< - conjunction<negation<is_compatible_object_type< - typename BasicJson::object_t, CompatibleArrayType>>, - has_value_type<CompatibleArrayType>, - has_iterator<CompatibleArrayType>>::value, - BasicJson, CompatibleArrayType>::value; + // the check for CompatibleArrayType = void is done in is_compatible_object_type + // but we need the conjunction here as well + static auto constexpr value = is_compatible_array_type_impl < + conjunction<negation<is_compatible_object_type< + typename BasicJson::object_t, CompatibleArrayType>>, + has_value_type<CompatibleArrayType>, + has_iterator<CompatibleArrayType>>::value, + BasicJson, CompatibleArrayType >::value; }; template <bool, typename, typename> -struct is_compatible_integer_type_impl : std::false_type{}; +struct is_compatible_integer_type_impl : std::false_type {}; template <typename RealIntegerType, typename CompatibleNumberIntegerType> struct is_compatible_integer_type_impl<true, RealIntegerType, CompatibleNumberIntegerType> { - using RealLimits = std::numeric_limits<RealIntegerType>; - using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>; + using RealLimits = std::numeric_limits<RealIntegerType>; + using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>; - static constexpr auto value = - std::is_constructible<RealIntegerType, - CompatibleNumberIntegerType>::value and - CompatibleLimits::is_integer and - RealLimits::is_signed == CompatibleLimits::is_signed; + static constexpr auto value = + std::is_constructible<RealIntegerType, + CompatibleNumberIntegerType>::value and + CompatibleLimits::is_integer and + RealLimits::is_signed == CompatibleLimits::is_signed; }; template <typename RealIntegerType, typename CompatibleNumberIntegerType> struct is_compatible_integer_type { - static constexpr auto value = is_compatible_integer_type_impl< - std::is_arithmetic<CompatibleNumberIntegerType>::value, RealIntegerType, - CompatibleNumberIntegerType>::value; + static constexpr auto value = is_compatible_integer_type_impl < + std::is_arithmetic<CompatibleNumberIntegerType>::value, RealIntegerType, + CompatibleNumberIntegerType >::value; }; template <typename RealFloat, typename CompatibleFloat> struct is_compatible_float_type { - static constexpr auto value = - std::is_constructible<RealFloat, CompatibleFloat>::value and - std::is_floating_point<CompatibleFloat>::value; + static constexpr auto value = + std::is_constructible<RealFloat, CompatibleFloat>::value and + std::is_floating_point<CompatibleFloat>::value; }; template <typename T, typename BasicJson> struct is_compatible_basic_json_type { - static auto constexpr value = - is_unscoped_enum<T>::value or - std::is_same<T, BasicJson>::value or - std::is_constructible<typename BasicJson::string_t, T>::value or - std::is_same<typename BasicJson::boolean_t, T>::value or - is_compatible_array_type<BasicJson, T>::value or - is_compatible_object_type<typename BasicJson::object_t, T>::value or - is_compatible_float_type<typename BasicJson::number_float_t, T>::value or - is_compatible_integer_type<typename BasicJson::number_integer_t, - T>::value or - is_compatible_integer_type<typename BasicJson::number_unsigned_t, - T>::value; + static auto constexpr value = + is_unscoped_enum<T>::value or + std::is_same<T, BasicJson>::value or + std::is_constructible<typename BasicJson::string_t, T>::value or + std::is_same<typename BasicJson::boolean_t, T>::value or + is_compatible_array_type<BasicJson, T>::value or + is_compatible_object_type<typename BasicJson::object_t, T>::value or + is_compatible_float_type<typename BasicJson::number_float_t, T>::value or + is_compatible_integer_type<typename BasicJson::number_integer_t, + T>::value or + is_compatible_integer_type<typename BasicJson::number_unsigned_t, + T>::value; }; template <typename T, typename BasicJson, typename PrimitiveIterator> struct is_basic_json_nested_class { - static auto constexpr value = std::is_same<T, typename BasicJson::iterator>::value or - std::is_same<T, typename BasicJson::const_iterator>::value or - std::is_same<T, typename BasicJson::reverse_iterator>::value or - std::is_same<T, typename BasicJson::const_reverse_iterator>::value or - std::is_same<T, PrimitiveIterator>::value or - std::is_same<T, typename BasicJson::json_pointer>::value; + static auto constexpr value = std::is_same<T, typename BasicJson::iterator>::value or + std::is_same<T, typename BasicJson::const_iterator>::value or + std::is_same<T, typename BasicJson::reverse_iterator>::value or + std::is_same<T, typename BasicJson::const_reverse_iterator>::value or + std::is_same<T, PrimitiveIterator>::value or + std::is_same<T, typename BasicJson::json_pointer>::value; }; // This trait checks if JSONSerializer<T>::from_json(json const&, udt&) exists @@ -321,17 +324,17 @@ template <template <typename, typename> class JSONSerializer, typename Json, typename T> struct has_from_json { -private: - // also check the return type of from_json - template <typename U, typename = enable_if_t<std::is_same<void, decltype(uncvref_t<U>::from_json( - std::declval<Json>(), std::declval<T &>()))>::value>> - static int detect(U &&); + private: + // also check the return type of from_json + template <typename U, typename = enable_if_t<std::is_same<void, decltype(uncvref_t<U>::from_json( + std::declval<Json>(), std::declval<T&>()))>::value>> + static int detect(U&&); - static void detect(...); + static void detect(...); -public: - static constexpr bool value = std::is_integral<decltype( - detect(std::declval<JSONSerializer<T, void>>()))>::value; + public: + static constexpr bool value = std::is_integral<decltype( + detect(std::declval<JSONSerializer<T, void>>()))>::value; }; // This trait checks if JSONSerializer<T>::from_json(json const&) exists @@ -340,15 +343,15 @@ template <template <typename, typename> class JSONSerializer, typename Json, typename T> struct has_non_default_from_json { -private: - template <typename U, typename = enable_if_t<std::is_same<T, decltype(uncvref_t<U>::from_json(std::declval<Json>()))>::value>> - static int detect(U &&); + private: + template <typename U, typename = enable_if_t<std::is_same<T, decltype(uncvref_t<U>::from_json(std::declval<Json>()))>::value>> + static int detect(U&&); - static void detect(...); + static void detect(...); -public: - static constexpr bool value = std::is_integral<decltype( - detect(std::declval<JSONSerializer<T, void>>()))>::value; + public: + static constexpr bool value = std::is_integral<decltype( + detect(std::declval<JSONSerializer<T, void>>()))>::value; }; // This trait checks if JSONSerializer<T>::to_json exists @@ -356,16 +359,16 @@ template <template <typename, typename> class JSONSerializer, typename Json, typename T> struct has_to_json { -private: - template <typename U, typename = decltype(uncvref_t<U>::to_json( - std::declval<Json &>(), std::declval<T>()))> - static int detect(U &&); + private: + template <typename U, typename = decltype(uncvref_t<U>::to_json( + std::declval<Json&>(), std::declval<T>()))> + static int detect(U&&); - static void detect(...); + static void detect(...); -public: - static constexpr bool value = std::is_integral<decltype( - detect(std::declval<JSONSerializer<T, void>>()))>::value; + public: + static constexpr bool value = std::is_integral<decltype( + detect(std::declval<JSONSerializer<T, void>>()))>::value; }; // those declarations are needed to workaround a MSVC bug related to ADL @@ -375,26 +378,26 @@ void from_json(); struct to_json_fn { - // is it really useful to mark those as constexpr? - template <typename Json, typename T> - constexpr auto operator()(Json &&j, T &&val) const - noexcept(noexcept(to_json(std::forward<Json>(j), std::forward<T>(val)))) - -> decltype(to_json(std::forward<Json>(j), std::forward<T>(val)), - void()) - { - return to_json(std::forward<Json>(j), std::forward<T>(val)); - } + // is it really useful to mark those as constexpr? + template <typename Json, typename T> + constexpr auto operator()(Json&& j, T&& val) const + noexcept(noexcept(to_json(std::forward<Json>(j), std::forward<T>(val)))) + -> decltype(to_json(std::forward<Json>(j), std::forward<T>(val)), + void()) + { + return to_json(std::forward<Json>(j), std::forward<T>(val)); + } }; struct from_json_fn { - template <typename Json, typename T> - constexpr auto operator()(Json &&j, T &val) const - noexcept(noexcept(from_json(std::forward<Json>(j), val))) - -> decltype(from_json(std::forward<Json>(j), val), void()) - { - return from_json(std::forward<Json>(j), val); - } + template <typename Json, typename T> + constexpr auto operator()(Json&& j, T& val) const + noexcept(noexcept(from_json(std::forward<Json>(j), val))) + -> decltype(from_json(std::forward<Json>(j), val), void()) + { + return from_json(std::forward<Json>(j), val); + } }; /*! @@ -423,7 +426,7 @@ struct DecimalSeparator : std::numpunct<char> template <typename T> struct static_const { - static constexpr T value{}; + static constexpr T value{}; }; template <typename T> @@ -431,8 +434,8 @@ constexpr T static_const<T>::value; inline namespace { - constexpr auto const& to_json = static_const<detail::to_json_fn>::value; - constexpr auto const& from_json = static_const<detail::from_json_fn>::value; +constexpr auto const& to_json = static_const<detail::to_json_fn>::value; +constexpr auto const& from_json = static_const<detail::from_json_fn>::value; } // default JSONSerializer template argument, doesn't care about template argument @@ -440,17 +443,17 @@ inline namespace template <typename = void, typename = void> struct adl_serializer { - template <typename Json, typename T> - static void from_json(Json&& j, T& val) - { - ::nlohmann::from_json(std::forward<Json>(j), val); - } + template <typename Json, typename T> + static void from_json(Json&& j, T& val) + { + ::nlohmann::from_json(std::forward<Json>(j), val); + } - template <typename Json, typename T> - static void to_json(Json& j, T&& val) - { - ::nlohmann::to_json(j, std::forward<T>(val)); - } + template <typename Json, typename T> + static void to_json(Json& j, T&& val) + { + ::nlohmann::to_json(j, std::forward<T>(val)); + } }; /*! @@ -1535,12 +1538,12 @@ class basic_json enable_if_t<detail::is_compatible_object_type< object_t, CompatibleObjectType>::value, int> = 0> - basic_json(const CompatibleObjectType &val) : m_type(value_t::object) + basic_json(const CompatibleObjectType& val) : m_type(value_t::object) { - using std::begin; - using std::end; - m_value.object = create<object_t>(begin(val), end(val)); - assert_invariant(); + using std::begin; + using std::end; + m_value.object = create<object_t>(begin(val), end(val)); + assert_invariant(); } /*! @@ -1598,12 +1601,12 @@ class basic_json enable_if_t<detail::is_compatible_array_type< basic_json_t, CompatibleArrayType>::value, int> = 0> - basic_json(const CompatibleArrayType &val) : m_type(value_t::array) + basic_json(const CompatibleArrayType& val) : m_type(value_t::array) { - using std::begin; - using std::end; - m_value.array = create<array_t>(begin(val), end(val)); - assert_invariant(); + using std::begin; + using std::end; + m_value.array = create<array_t>(begin(val), end(val)); + assert_invariant(); } @@ -1614,18 +1617,18 @@ class basic_json template < typename T, enable_if_t<not std::is_base_of<std::istream, uncvref_t<T>>::value and - not detail::is_basic_json_nested_class<uncvref_t<T>, basic_json_t, primitive_iterator_t>::value and - not std::is_same<uncvref_t<T>, typename basic_json_t::array_t::iterator>::value and - not std::is_same<uncvref_t<T>, typename basic_json_t::object_t::iterator>::value and + not detail::is_basic_json_nested_class<uncvref_t<T>, basic_json_t, primitive_iterator_t>::value and + not std::is_same<uncvref_t<T>, typename basic_json_t::array_t::iterator>::value and + not std::is_same<uncvref_t<T>, typename basic_json_t::object_t::iterator>::value and - detail::conjunction<detail::negation<detail::is_compatible_basic_json_type< - uncvref_t<T>, basic_json_t>>, - detail::has_to_json<JSONSerializer, basic_json, - uncvref_t<T>>>::value, - int> = 0> - basic_json(T &&val) + detail::conjunction<detail::negation<detail::is_compatible_basic_json_type< + uncvref_t<T>, basic_json_t>>, + detail::has_to_json<JSONSerializer, basic_json, + uncvref_t<T>>>::value, + int> = 0 > + basic_json(T && val) { - JSONSerializer<uncvref_t<T>>::to_json(*this, std::forward<T>(val)); + JSONSerializer<uncvref_t<T>>::to_json(*this, std::forward<T>(val)); } /*! @@ -1828,11 +1831,12 @@ class basic_json typename CompatibleNumberIntegerType, enable_if_t<detail::is_compatible_integer_type< number_integer_t, CompatibleNumberIntegerType>::value, - int> = 0> + int> = 0 > basic_json(const CompatibleNumberIntegerType val) noexcept : m_type(value_t::number_integer), - m_value(static_cast<number_integer_t>(val)) { - assert_invariant(); + m_value(static_cast<number_integer_t>(val)) + { + assert_invariant(); } /*! @@ -1885,11 +1889,12 @@ class basic_json typename CompatibleNumberUnsignedType, enable_if_t<detail::is_compatible_integer_type< number_unsigned_t, CompatibleNumberUnsignedType>::value, - int> = 0> + int> = 0 > basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), - m_value(static_cast<number_unsigned_t>(val)) { - assert_invariant(); + m_value(static_cast<number_unsigned_t>(val)) + { + assert_invariant(); } /*! @@ -2123,7 +2128,7 @@ class basic_json @since version 1.0.0 */ static basic_json array(std::initializer_list<basic_json> init = - std::initializer_list<basic_json>()) + std::initializer_list<basic_json>()) { return basic_json(init, false, value_t::array); } @@ -2163,7 +2168,7 @@ class basic_json @since version 1.0.0 */ static basic_json object(std::initializer_list<basic_json> init = - std::initializer_list<basic_json>()) + std::initializer_list<basic_json>()) { return basic_json(init, false, value_t::object); } @@ -2504,7 +2509,7 @@ class basic_json std::is_nothrow_move_assignable<value_t>::value and std::is_nothrow_move_constructible<json_value>::value and std::is_nothrow_move_assignable<json_value>::value - ) + ) { // check that passed value is valid other.assert_invariant(); @@ -2521,59 +2526,65 @@ class basic_json template <typename T, enable_if_t<not detail::is_compatible_basic_json_type< uncvref_t<T>, basic_json_t>::value and detail::has_to_json<JSONSerializer, basic_json_t, uncvref_t<T>>::value>> - reference &operator=(T &&val) noexcept(std::is_nothrow_constructible<basic_json_t, uncvref_t<T>>::value and + reference& operator=(T&& val) noexcept(std::is_nothrow_constructible<basic_json_t, uncvref_t<T>>::value and std::is_nothrow_move_assignable<uncvref_t<T>>::value) { - static_assert(sizeof(T) == 0 , ""); - // I'm not sure this a is good practice... - return *this = basic_json_t{std::forward<T>(val)}; + static_assert(sizeof(T) == 0 , ""); + // I'm not sure this a is good practice... + return *this = basic_json_t{std::forward<T>(val)}; } - /*! - @brief destructor + /*! + @brief destructor - Destroys the JSON value and frees all allocated memory. + Destroys the JSON value and frees all allocated memory. - @complexity Linear. + @complexity Linear. - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is linear. - - All stored elements are destroyed and all memory is freed. + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. + - All stored elements are destroyed and all memory is freed. - @since version 1.0.0 - */ - ~basic_json() { - assert_invariant(); + @since version 1.0.0 + */ + ~basic_json() + { + assert_invariant(); - switch (m_type) { - case value_t::object: { - AllocatorType<object_t> alloc; - alloc.destroy(m_value.object); - alloc.deallocate(m_value.object, 1); - break; - } + switch (m_type) + { + case value_t::object: + { + AllocatorType<object_t> alloc; + alloc.destroy(m_value.object); + alloc.deallocate(m_value.object, 1); + break; + } - case value_t::array: { - AllocatorType<array_t> alloc; - alloc.destroy(m_value.array); - alloc.deallocate(m_value.array, 1); - break; - } + case value_t::array: + { + AllocatorType<array_t> alloc; + alloc.destroy(m_value.array); + alloc.deallocate(m_value.array, 1); + break; + } - case value_t::string: { - AllocatorType<string_t> alloc; - alloc.destroy(m_value.string); - alloc.deallocate(m_value.string, 1); - break; - } + case value_t::string: + { + AllocatorType<string_t> alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); + break; + } - default: { - // all other types need no specific destructor - break; - } - } + default: + { + // all other types need no specific destructor + break; + } + } } /// @} @@ -3025,15 +3036,15 @@ class basic_json /// get an array (explicit) template < class T, - typename std::enable_if< + 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 detail::has_mapped_type<T>::value, - int>::type = 0> - T get_impl(T *) const + 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 detail::has_mapped_type<T>::value, + int >::type = 0 > + T get_impl(T*) const { if (is_array()) { @@ -3305,41 +3316,41 @@ class basic_json template <typename ValueType, enable_if_t<not std::is_pointer<ValueType>::value, int> = 0> auto get() const - -> decltype(this->get_impl(static_cast<ValueType *>(nullptr))) + -> decltype(this->get_impl(static_cast<ValueType*>(nullptr))) { - return get_impl(static_cast<ValueType *>(nullptr)); + return get_impl(static_cast<ValueType*>(nullptr)); } template < typename T, enable_if_t<detail::conjunction<detail::negation<detail::is_compatible_basic_json_type< - uncvref_t<T>, basic_json_t>>, - detail::has_from_json<JSONSerializer, basic_json_t, - uncvref_t<T>>>::value, - int> = 0> - auto get() const -> uncvref_t<T> + uncvref_t<T>, basic_json_t>>, + detail::has_from_json<JSONSerializer, basic_json_t, + uncvref_t<T>>>::value, + int> = 0 > + auto get() const -> uncvref_t<T> { - using type = uncvref_t<T>; - static_assert(std::is_default_constructible<type>::value && - std::is_copy_constructible<type>::value, - "user-defined types must be DefaultConstructible and " - "CopyConstructible when used with get"); - type ret; - JSONSerializer<type>::from_json(*this, ret); - return ret; + using type = uncvref_t<T>; + static_assert(std::is_default_constructible<type>::value&& + std::is_copy_constructible<type>::value, + "user-defined types must be DefaultConstructible and " + "CopyConstructible when used with get"); + type ret; + JSONSerializer<type>::from_json(*this, ret); + return ret; } // This overload is chosen for non-default constructible user-defined-types template < typename T, enable_if_t<detail::conjunction<detail::negation<detail::is_compatible_basic_json_type< - uncvref_t<T>, basic_json_t>>, - detail::has_non_default_from_json<JSONSerializer, basic_json_t, - uncvref_t<T>>>::value, - short> = 0> - T get() const + uncvref_t<T>, basic_json_t>>, + detail::has_non_default_from_json<JSONSerializer, basic_json_t, + uncvref_t<T>>>::value, + short> = 0 > + T get() const { - return JSONSerializer<T>::from_json(*this); + return JSONSerializer<T>::from_json(*this); } /*! @@ -3421,8 +3432,8 @@ class basic_json { // get the type of the PointerType (remove pointer and const) using pointee_t = typename std::remove_const<typename - std::remove_pointer<typename - std::remove_const<PointerType>::type>::type>::type; + std::remove_pointer<typename + std::remove_const<PointerType>::type>::type>::type; // make sure the type matches the allowed types static_assert( std::is_same<object_t, pointee_t>::value @@ -3449,8 +3460,8 @@ class basic_json { // get the type of the PointerType (remove pointer and const) using pointee_t = typename std::remove_const<typename - std::remove_pointer<typename - std::remove_const<PointerType>::type>::type>::type; + std::remove_pointer<typename + std::remove_const<PointerType>::type>::type>::type; // make sure the type matches the allowed types static_assert( std::is_same<object_t, pointee_t>::value @@ -4503,14 +4514,14 @@ class basic_json case value_t::object: { result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, - last.m_it.object_iterator); + last.m_it.object_iterator); break; } case value_t::array: { result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, - last.m_it.array_iterator); + last.m_it.array_iterator); break; } @@ -5727,9 +5738,9 @@ class basic_json // insert to array and return iterator iterator result(this); result.m_it.array_iterator = m_value.array->insert( - pos.m_it.array_iterator, - first.m_it.array_iterator, - last.m_it.array_iterator); + pos.m_it.array_iterator, + first.m_it.array_iterator, + last.m_it.array_iterator); return result; } @@ -5799,7 +5810,7 @@ class basic_json std::is_nothrow_move_assignable<value_t>::value and std::is_nothrow_move_constructible<json_value>::value and std::is_nothrow_move_assignable<json_value>::value - ) + ) { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); @@ -8399,13 +8410,13 @@ class basic_json o << (pretty_print ? ",\n" : ","); } o << string_t(new_indent, ' ') << "\"" - << escape_string(i->first) << "\":" - << (pretty_print ? " " : ""); - i->second.dump(o, pretty_print, indent_step, new_indent); + << escape_string(i->first) << "\":" + << (pretty_print ? " " : ""); + i->second.dump(o, pretty_print, indent_step, new_indent); } - // decrease indentation - if (pretty_print) + // decrease indentation + if (pretty_print) { new_indent -= indent_step; o << "\n"; @@ -8450,16 +8461,16 @@ class basic_json } o << string_t(new_indent, ' ') << "]"; - return; + return; } - case value_t::string: + case value_t::string: { o << string_t("\"") << escape_string(*m_value.string) << "\""; - return; + return; } - case value_t::boolean: + case value_t::boolean: { o << (m_value.boolean ? "true" : "false"); return; @@ -8533,126 +8544,126 @@ class basic_json */ class primitive_iterator_t { - public: + public: difference_type get_value() const noexcept - { - return m_it; - } - /// set iterator to a defined beginning - void set_begin() noexcept - { - m_it = begin_value; - } + { + return m_it; + } + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } - /// set iterator to a defined past the end - void set_end() noexcept - { - m_it = end_value; - } + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } - /// return whether the iterator can be dereferenced - constexpr bool is_begin() const noexcept - { - return (m_it == begin_value); - } + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return (m_it == begin_value); + } - /// return whether the iterator is at end - constexpr bool is_end() const noexcept - { - return (m_it == end_value); - } + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return (m_it == end_value); + } - friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it == rhs.m_it; - } + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } - friend constexpr bool operator!=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return !(lhs == rhs); - } + friend constexpr bool operator!=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return !(lhs == rhs); + } - friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it < rhs.m_it; - } + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } - friend constexpr bool operator<=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it <= rhs.m_it; - } + friend constexpr bool operator<=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it <= rhs.m_it; + } - friend constexpr bool operator>(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it > rhs.m_it; - } + friend constexpr bool operator>(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it > rhs.m_it; + } - friend constexpr bool operator>=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it >= rhs.m_it; - } + friend constexpr bool operator>=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it >= rhs.m_it; + } - primitive_iterator_t operator+(difference_type i) - { - auto result = *this; - result += i; - return result; - } + primitive_iterator_t operator+(difference_type i) + { + auto result = *this; + result += i; + return result; + } - friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it - rhs.m_it; - } + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } - friend std::ostream& operator<<(std::ostream& os, primitive_iterator_t it) - { - return os << it.m_it; - } + friend std::ostream& operator<<(std::ostream& os, primitive_iterator_t it) + { + return os << it.m_it; + } - primitive_iterator_t& operator++() - { - ++m_it; - return *this; - } + primitive_iterator_t& operator++() + { + ++m_it; + return *this; + } - primitive_iterator_t& operator++(int) - { - m_it++; - return *this; - } + primitive_iterator_t& operator++(int) + { + m_it++; + return *this; + } - primitive_iterator_t& operator--() - { - --m_it; - return *this; - } + primitive_iterator_t& operator--() + { + --m_it; + return *this; + } - primitive_iterator_t& operator--(int) - { - m_it--; - return *this; - } + primitive_iterator_t& operator--(int) + { + m_it--; + return *this; + } - primitive_iterator_t& operator+=(difference_type n) - { - m_it += n; - return *this; - } + primitive_iterator_t& operator+=(difference_type n) + { + m_it += n; + return *this; + } - primitive_iterator_t& operator-=(difference_type n) - { - m_it -= n; - return *this; - } + primitive_iterator_t& operator-=(difference_type n) + { + m_it -= n; + return *this; + } - private: - static constexpr difference_type begin_value = 0; - static constexpr difference_type end_value = begin_value + 1; + private: + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; - /// iterator as signed integer type - difference_type m_it = std::numeric_limits<std::ptrdiff_t>::denorm_min(); - }; + /// iterator as signed integer type + difference_type m_it = std::numeric_limits<std::ptrdiff_t>::denorm_min(); + }; /*! @brief an iterator value @@ -8672,104 +8683,104 @@ class basic_json /// create an uninitialized internal_iterator internal_iterator() noexcept - : object_iterator(), array_iterator(), primitive_iterator() - {} + : object_iterator(), array_iterator(), primitive_iterator() + {} }; /// proxy class for the iterator_wrapper functions template<typename IteratorType> class iteration_proxy { - private: + private: /// helper class for iteration class iteration_proxy_internal { - private: + private: /// the iterator IteratorType anchor; /// an index for arrays (used to create key names) size_t array_index = 0; - public: + public: explicit iteration_proxy_internal(IteratorType it) noexcept - : anchor(it) - {} + : anchor(it) + {} - /// dereference operator (needed for range-based for) - iteration_proxy_internal& operator*() + /// dereference operator (needed for range-based for) + iteration_proxy_internal& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_internal& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// inequality operator (needed for range-based for) + bool operator!= (const iteration_proxy_internal& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + typename basic_json::string_t key() const + { + assert(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: { - return *this; + return std::to_string(array_index); } - /// increment operator (needed for range-based for) - iteration_proxy_internal& operator++() + // use key from the object + case value_t::object: { - ++anchor; - ++array_index; - - return *this; + return anchor.key(); } - /// inequality operator (needed for range-based for) - bool operator!= (const iteration_proxy_internal& o) const + // use an empty key for all primitive types + default: { - return anchor != o.anchor; + return ""; } + } + } - /// return key of the iterator - typename basic_json::string_t key() const - { - assert(anchor.m_object != nullptr); - - switch (anchor.m_object->type()) - { - // use integer array index as key - case value_t::array: - { - return std::to_string(array_index); - } - - // use key from the object - case value_t::object: - { - return anchor.key(); - } - - // use an empty key for all primitive types - default: - { - return ""; - } - } - } - - /// return value of the iterator - typename IteratorType::reference value() const - { - return anchor.value(); - } + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } }; - /// the container to iterate - typename IteratorType::reference container; + /// the container to iterate + typename IteratorType::reference container; - public: - /// construct iteration proxy from a container - explicit iteration_proxy(typename IteratorType::reference cont) - : container(cont) - {} + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) + : container(cont) + {} - /// return iterator begin (needed for range-based for) - iteration_proxy_internal begin() noexcept - { - return iteration_proxy_internal(container.begin()); - } + /// return iterator begin (needed for range-based for) + iteration_proxy_internal begin() noexcept + { + return iteration_proxy_internal(container.begin()); + } - /// return iterator end (needed for range-based for) - iteration_proxy_internal end() noexcept - { - return iteration_proxy_internal(container.end()); - } + /// return iterator end (needed for range-based for) + iteration_proxy_internal end() noexcept + { + return iteration_proxy_internal(container.end()); + } }; public: @@ -8793,7 +8804,7 @@ class basic_json @since version 1.0.0, simplified in version 2.0.9 */ template<typename U> - class iter_impl : public std::iterator<std::random_access_iterator_tag, U> + class iter_impl : public std::iterator<std::random_access_iterator_tag, U> { /// allow basic_json to access private members friend class basic_json; @@ -8803,19 +8814,19 @@ class basic_json or std::is_same<U, const basic_json>::value, "iter_impl only accepts (const) basic_json"); - public: + 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 std::conditional<std::is_const<U>::value, - typename basic_json::const_pointer, - typename basic_json::pointer>::type; + typename basic_json::const_pointer, + typename basic_json::pointer>::type; /// defines a reference to the type iterated over (value_type) using reference = typename std::conditional<std::is_const<U>::value, - typename basic_json::const_reference, - typename basic_json::reference>::type; + typename basic_json::const_reference, + typename basic_json::reference>::type; /// the category of the iterator using iterator_category = std::bidirectional_iterator_tag; @@ -8829,566 +8840,566 @@ class basic_json @post The iterator is initialized; i.e. `m_object != nullptr`. */ explicit iter_impl(pointer object) noexcept - : m_object(object) + : m_object(object) + { + assert(m_object != nullptr); + + switch (m_object->m_type) { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::object: { - case basic_json::value_t::object: - { - m_it.object_iterator = typename object_t::iterator(); - break; - } - - case basic_json::value_t::array: - { - m_it.array_iterator = typename array_t::iterator(); - break; - } - - default: - { - m_it.primitive_iterator = primitive_iterator_t(); - break; - } - } - } - - /* - 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. - */ - operator const_iterator() const - { - const_iterator ret; - - if (m_object) - { - ret.m_object = m_object; - ret.m_it = m_it; + m_it.object_iterator = typename object_t::iterator(); + break; } - return ret; - } - - /*! - @brief copy constructor - @param[in] other iterator to copy from - @note It is not checked whether @a other is initialized. - */ - iter_impl(const iter_impl& other) noexcept - : m_object(other.m_object), m_it(other.m_it) - {} - - /*! - @brief copy assignment - @param[in,out] other iterator to copy from - @note It is not checked whether @a other is initialized. - */ - 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 - std::is_nothrow_move_assignable<internal_iterator>::value - ) - { - std::swap(m_object, other.m_object); - std::swap(m_it, other.m_it); - return *this; - } - - private: - /*! - @brief set the iterator to the first value - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - void set_begin() noexcept - { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::array: { - case basic_json::value_t::object: - { - m_it.object_iterator = m_object->m_value.object->begin(); - break; - } + m_it.array_iterator = typename array_t::iterator(); + break; + } - case basic_json::value_t::array: - { - m_it.array_iterator = m_object->m_value.array->begin(); - break; - } - - case basic_json::value_t::null: - { - // set to end so begin()==end() is true: null is empty - m_it.primitive_iterator.set_end(); - break; - } - - default: - { - m_it.primitive_iterator.set_begin(); - break; - } + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; } } + } - /*! - @brief set the iterator past the last value - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - void set_end() noexcept + /* + 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. + */ + operator const_iterator() const + { + const_iterator ret; + + if (m_object) { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - m_it.object_iterator = m_object->m_value.object->end(); - break; - } - - case basic_json::value_t::array: - { - m_it.array_iterator = m_object->m_value.array->end(); - break; - } - - default: - { - m_it.primitive_iterator.set_end(); - break; - } - } + ret.m_object = m_object; + ret.m_it = m_it; } - public: - /*! - @brief return a reference to the value pointed to by the iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - reference operator*() const + return ret; + } + + /*! + @brief copy constructor + @param[in] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief copy assignment + @param[in,out] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ + 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 + std::is_nothrow_move_assignable<internal_iterator>::value + ) + { + std::swap(m_object, other.m_object); + std::swap(m_it, other.m_it); + return *this; + } + + private: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::object: { - case basic_json::value_t::object: - { - assert(m_it.object_iterator != m_object->m_value.object->end()); - return m_it.object_iterator->second; - } + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } - case basic_json::value_t::array: - { - assert(m_it.array_iterator != m_object->m_value.array->end()); - return *m_it.array_iterator; - } + case basic_json::value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } - case basic_json::value_t::null: - { + case basic_json::value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case basic_json::value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case basic_json::value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case basic_json::value_t::null: + { JSON_THROW(std::out_of_range("cannot get value")); - } + } - default: + default: + { + if (m_it.primitive_iterator.is_begin()) { - if (m_it.primitive_iterator.is_begin()) - { - return *m_object; - } + return *m_object; + } JSON_THROW(std::out_of_range("cannot get value")); - } } } + } - /*! - @brief dereference the iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - pointer operator->() const + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::object: { - case basic_json::value_t::object: - { - assert(m_it.object_iterator != m_object->m_value.object->end()); - return &(m_it.object_iterator->second); - } + assert(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } - case basic_json::value_t::array: - { - assert(m_it.array_iterator != m_object->m_value.array->end()); - return &*m_it.array_iterator; - } + case basic_json::value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } - default: + default: + { + if (m_it.primitive_iterator.is_begin()) { - if (m_it.primitive_iterator.is_begin()) - { - return m_object; - } + return m_object; + } JSON_THROW(std::out_of_range("cannot get value")); - } + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator++(int) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case basic_json::value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + default: + { + ++m_it.primitive_iterator; + break; } } - /*! - @brief post-increment (it++) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator++(int) - { - auto result = *this; - ++(*this); - return result; - } + return *this; + } - /*! - @brief pre-increment (++it) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator++() - { - assert(m_object != nullptr); + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator--(int) + { + auto result = *this; + --(*this); + return result; + } - switch (m_object->m_type) + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: { - case basic_json::value_t::object: - { - std::advance(m_it.object_iterator, 1); - break; - } - - case basic_json::value_t::array: - { - std::advance(m_it.array_iterator, 1); - break; - } - - default: - { - ++m_it.primitive_iterator; - break; - } + std::advance(m_it.object_iterator, -1); + break; } - return *this; - } - - /*! - @brief post-decrement (it--) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator--(int) - { - auto result = *this; - --(*this); - return result; - } - - /*! - @brief pre-decrement (--it) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator--() - { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::array: { - case basic_json::value_t::object: - { - std::advance(m_it.object_iterator, -1); - break; - } - - case basic_json::value_t::array: - { - std::advance(m_it.array_iterator, -1); - break; - } - - default: - { - --m_it.primitive_iterator; - break; - } + std::advance(m_it.array_iterator, -1); + break; } - return *this; + default: + { + --m_it.primitive_iterator; + break; + } } - /*! - @brief comparison: equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator==(const iter_impl& other) const + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator==(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) { - // if objects are not the same, the comparison is undefined - if (m_object != other.m_object) - { JSON_THROW(std::domain_error("cannot compare iterators of different containers")); - } - - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - return (m_it.object_iterator == other.m_it.object_iterator); - } - - case basic_json::value_t::array: - { - return (m_it.array_iterator == other.m_it.array_iterator); - } - - default: - { - return (m_it.primitive_iterator == other.m_it.primitive_iterator); - } - } } - /*! - @brief comparison: not equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator!=(const iter_impl& other) const - { - return not operator==(other); - } + assert(m_object != nullptr); - /*! - @brief comparison: smaller - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator<(const iter_impl& other) const + switch (m_object->m_type) { - // if objects are not the same, the comparison is undefined - if (m_object != other.m_object) + case basic_json::value_t::object: { + return (m_it.object_iterator == other.m_it.object_iterator); + } + + case basic_json::value_t::array: + { + return (m_it.array_iterator == other.m_it.array_iterator); + } + + default: + { + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator!=(const iter_impl& other) const + { + return not operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) + { JSON_THROW(std::domain_error("cannot compare iterators of different containers")); - } + } - assert(m_object != nullptr); + assert(m_object != nullptr); - switch (m_object->m_type) + switch (m_object->m_type) + { + case basic_json::value_t::object: { - case basic_json::value_t::object: - { JSON_THROW(std::domain_error("cannot compare order of object iterators")); - } + } - case basic_json::value_t::array: - { - return (m_it.array_iterator < other.m_it.array_iterator); - } + case basic_json::value_t::array: + { + return (m_it.array_iterator < other.m_it.array_iterator); + } - default: - { - return (m_it.primitive_iterator < other.m_it.primitive_iterator); - } + default: + { + return (m_it.primitive_iterator < other.m_it.primitive_iterator); } } + } - /*! - @brief comparison: less than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator<=(const iter_impl& other) const + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return not other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return not operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return not operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + assert(m_object != nullptr); + + switch (m_object->m_type) { - return not other.operator < (*this); - } - - /*! - @brief comparison: greater than - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator>(const iter_impl& other) const - { - return not operator<=(other); - } - - /*! - @brief comparison: greater than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator>=(const iter_impl& other) const - { - return not operator<(other); - } - - /*! - @brief add to iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator+=(difference_type i) - { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::object: { - case basic_json::value_t::object: - { JSON_THROW(std::domain_error("cannot use offsets with object iterators")); - } - - case basic_json::value_t::array: - { - std::advance(m_it.array_iterator, i); - break; - } - - default: - { - m_it.primitive_iterator += i; - break; - } } - return *this; - } - - /*! - @brief subtract from iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator-=(difference_type i) - { - return operator+=(-i); - } - - /*! - @brief add to iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator+(difference_type i) - { - auto result = *this; - result += i; - return result; - } - - /*! - @brief subtract from iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator-(difference_type i) - { - auto result = *this; - result -= i; - return result; - } - - /*! - @brief return difference - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - difference_type operator-(const iter_impl& other) const - { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: { - case basic_json::value_t::object: - { JSON_THROW(std::domain_error("cannot use offsets with object iterators")); - } + } - case basic_json::value_t::array: - { - return m_it.array_iterator - other.m_it.array_iterator; - } + case basic_json::value_t::array: + { + return m_it.array_iterator - other.m_it.array_iterator; + } - default: - { - return m_it.primitive_iterator - other.m_it.primitive_iterator; - } + default: + { + return m_it.primitive_iterator - other.m_it.primitive_iterator; } } + } - /*! - @brief access to successor - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - reference operator[](difference_type n) const + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::object: { - case basic_json::value_t::object: - { JSON_THROW(std::domain_error("cannot use operator[] for object iterators")); - } + } - case basic_json::value_t::array: - { - return *std::next(m_it.array_iterator, n); - } + case basic_json::value_t::array: + { + return *std::next(m_it.array_iterator, n); + } - case basic_json::value_t::null: - { + case basic_json::value_t::null: + { JSON_THROW(std::out_of_range("cannot get value")); - } + } - default: + default: + { + if (m_it.primitive_iterator.get_value() == -n) { - if (m_it.primitive_iterator.get_value() == -n) - { - return *m_object; - } + return *m_object; + } JSON_THROW(std::out_of_range("cannot get value")); - } } } + } - /*! - @brief return the key of an object iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - typename object_t::key_type key() const + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + typename object_t::key_type key() const + { + assert(m_object != nullptr); + + if (m_object->is_object()) { - assert(m_object != nullptr); - - if (m_object->is_object()) - { - return m_it.object_iterator->first; - } + return m_it.object_iterator->first; + } JSON_THROW(std::domain_error("cannot use key() for non-object iterators")); - } + } - /*! - @brief return the value of an iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - reference value() const - { - return operator*(); - } + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } - private: - /// associated JSON instance - pointer m_object = nullptr; - /// the actual iterator of the associated instance - internal_iterator m_it = internal_iterator(); - }; + private: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator m_it = internal_iterator(); + }; /*! @brief a template for a reverse iterator class @@ -9408,9 +9419,9 @@ class basic_json @since version 1.0.0 */ template<typename Base> - class json_reverse_iterator : public std::reverse_iterator<Base> + class json_reverse_iterator : public std::reverse_iterator<Base> { - public: + public: /// shortcut to the reverse iterator adaptor using base_iterator = std::reverse_iterator<Base>; /// the reference type for the pointed-to element @@ -9418,89 +9429,89 @@ class basic_json /// create reverse iterator from iterator json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept - : base_iterator(it) - {} + : base_iterator(it) + {} - /// create reverse iterator from base class - json_reverse_iterator(const base_iterator& it) noexcept - : base_iterator(it) - {} + /// create reverse iterator from base class + json_reverse_iterator(const base_iterator& it) noexcept + : base_iterator(it) + {} - /// post-increment (it++) - json_reverse_iterator operator++(int) - { - return base_iterator::operator++(1); - } + /// post-increment (it++) + json_reverse_iterator operator++(int) + { + return base_iterator::operator++(1); + } - /// pre-increment (++it) - json_reverse_iterator& operator++() - { - base_iterator::operator++(); - return *this; - } + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + base_iterator::operator++(); + return *this; + } - /// post-decrement (it--) - json_reverse_iterator operator--(int) - { - return base_iterator::operator--(1); - } + /// post-decrement (it--) + json_reverse_iterator operator--(int) + { + return base_iterator::operator--(1); + } - /// pre-decrement (--it) - json_reverse_iterator& operator--() - { - base_iterator::operator--(); - return *this; - } + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + base_iterator::operator--(); + return *this; + } - /// add to iterator - json_reverse_iterator& operator+=(difference_type i) - { - base_iterator::operator+=(i); - return *this; - } + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + base_iterator::operator+=(i); + return *this; + } - /// add to iterator - json_reverse_iterator operator+(difference_type i) const - { - auto result = *this; - result += i; - return result; - } + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } - /// subtract from iterator - json_reverse_iterator operator-(difference_type i) const - { - auto result = *this; - result -= i; - return result; - } + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } - /// return difference - difference_type operator-(const json_reverse_iterator& other) const - { - return this->base() - other.base(); - } + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return this->base() - other.base(); + } - /// access to successor - reference operator[](difference_type n) const - { - return *(this->operator+(n)); - } + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } - /// return the key of an object iterator - typename object_t::key_type key() const - { - auto it = --this->base(); - return it.key(); - } + /// return the key of an object iterator + typename object_t::key_type key() const + { + auto it = --this->base(); + return it.key(); + } - /// return the value of an iterator - reference value() const - { - auto it = --this->base(); - return it.operator * (); - } - }; + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } + }; private: @@ -9517,656 +9528,666 @@ class basic_json */ class lexer { - public: + public: /// token types for the parser enum class token_type - { - uninitialized, ///< indicating the scanner is uninitialized - literal_true, ///< the `true` literal - literal_false, ///< the `false` literal - literal_null, ///< the `null` literal - value_string, ///< a string -- use get_string() for actual value - value_number, ///< a number -- use get_number() for actual value - begin_array, ///< the character for array begin `[` - begin_object, ///< the character for object begin `{` - end_array, ///< the character for array end `]` - end_object, ///< the character for object end `}` - name_separator, ///< the name separator `:` - value_separator, ///< the value separator `,` - parse_error, ///< indicating a parse error - end_of_input ///< indicating the end of the input buffer - }; + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_number, ///< a number -- use get_number() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input ///< indicating the end of the input buffer + }; - /// the char type to use in the lexer - using lexer_char_t = unsigned char; + /// the char type to use in the lexer + using lexer_char_t = unsigned char; - /// a lexer from a buffer with given length - lexer(const lexer_char_t* buff, const size_t len) noexcept - : m_content(buff) - { - assert(m_content != nullptr); - m_start = m_cursor = m_content; - m_limit = m_content + len; - } + /// a lexer from a buffer with given length + lexer(const lexer_char_t* buff, const size_t len) noexcept + : m_content(buff) + { + assert(m_content != nullptr); + m_start = m_cursor = m_content; + m_limit = m_content + len; + } - /// a lexer from an input stream - explicit lexer(std::istream& s) - : m_stream(&s), m_line_buffer() + /// a lexer from an input stream + explicit lexer(std::istream& s) + : m_stream(&s), m_line_buffer() + { + // immediately abort if stream is erroneous + if (s.fail()) { - // immediately abort if stream is erroneous - if (s.fail()) - { JSON_THROW(std::invalid_argument("stream error")); - } - - // 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 (due to pointer members) - lexer() = delete; - lexer(const lexer&) = delete; - lexer operator=(const lexer&) = delete; + // fill buffer + fill_line_buffer(); - /*! - @brief create a string from one or two Unicode code points - - There are two cases: (1) @a codepoint1 is in the Basic Multilingual - Plane (U+0000 through U+FFFF) and @a codepoint2 is 0, or (2) - @a codepoint1 and @a codepoint2 are a UTF-16 surrogate pair to - represent a code point above U+FFFF. - - @param[in] codepoint1 the code point (can be high surrogate) - @param[in] codepoint2 the code point (can be low surrogate or 0) - - @return string representation of the code point; the length of the - result string is between 1 and 4 characters. - - @throw std::out_of_range if code point is > 0x10ffff; example: `"code - points above 0x10FFFF are invalid"` - @throw std::invalid_argument if the low surrogate is invalid; example: - `""missing or wrong low surrogate""` - - @complexity Constant. - - @see <http://en.wikipedia.org/wiki/UTF-8#Sample_code> - */ - static string_t to_unicode(const std::size_t codepoint1, - const std::size_t codepoint2 = 0) + // skip UTF-8 byte-order mark + if (m_line_buffer.size() >= 3 and m_line_buffer.substr(0, 3) == "\xEF\xBB\xBF") { - // calculate the code point from the given code points - std::size_t codepoint = codepoint1; + m_line_buffer[0] = ' '; + m_line_buffer[1] = ' '; + m_line_buffer[2] = ' '; + } + } - // check if codepoint1 is a high surrogate - if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF) - { - // check if codepoint2 is a low surrogate - if (codepoint2 >= 0xDC00 and codepoint2 <= 0xDFFF) - { - codepoint = - // high surrogate occupies the most significant 22 bits - (codepoint1 << 10) - // low surrogate occupies the least significant 15 bits - + codepoint2 - // there is still the 0xD800, 0xDC00 and 0x10000 noise - // in the result so we have to subtract with: - // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - - 0x35FDC00; - } - else - { - JSON_THROW(std::invalid_argument("missing or wrong low surrogate")); - } - } + // switch off unwanted functions (due to pointer members) + lexer() = delete; + lexer(const lexer&) = delete; + lexer operator=(const lexer&) = delete; - string_t result; + /*! + @brief create a string from one or two Unicode code points - if (codepoint < 0x80) + There are two cases: (1) @a codepoint1 is in the Basic Multilingual + Plane (U+0000 through U+FFFF) and @a codepoint2 is 0, or (2) + @a codepoint1 and @a codepoint2 are a UTF-16 surrogate pair to + represent a code point above U+FFFF. + + @param[in] codepoint1 the code point (can be high surrogate) + @param[in] codepoint2 the code point (can be low surrogate or 0) + + @return string representation of the code point; the length of the + result string is between 1 and 4 characters. + + @throw std::out_of_range if code point is > 0x10ffff; example: `"code + points above 0x10FFFF are invalid"` + @throw std::invalid_argument if the low surrogate is invalid; example: + `""missing or wrong low surrogate""` + + @complexity Constant. + + @see <http://en.wikipedia.org/wiki/UTF-8#Sample_code> + */ + static string_t to_unicode(const std::size_t codepoint1, + const std::size_t codepoint2 = 0) + { + // calculate the code point from the given code points + std::size_t codepoint = codepoint1; + + // check if codepoint1 is a high surrogate + if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF) + { + // check if codepoint2 is a low surrogate + if (codepoint2 >= 0xDC00 and codepoint2 <= 0xDFFF) { - // 1-byte characters: 0xxxxxxx (ASCII) - result.append(1, static_cast<typename string_t::value_type>(codepoint)); - } - else if (codepoint <= 0x7ff) - { - // 2-byte characters: 110xxxxx 10xxxxxx - result.append(1, static_cast<typename string_t::value_type>(0xC0 | ((codepoint >> 6) & 0x1F))); - result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F))); - } - else if (codepoint <= 0xffff) - { - // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - result.append(1, static_cast<typename string_t::value_type>(0xE0 | ((codepoint >> 12) & 0x0F))); - result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 6) & 0x3F))); - result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F))); - } - else if (codepoint <= 0x10ffff) - { - // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - result.append(1, static_cast<typename string_t::value_type>(0xF0 | ((codepoint >> 18) & 0x07))); - result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 12) & 0x3F))); - result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 6) & 0x3F))); - result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F))); + codepoint = + // high surrogate occupies the most significant 22 bits + (codepoint1 << 10) + // low surrogate occupies the least significant 15 bits + + codepoint2 + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00; } else { + JSON_THROW(std::invalid_argument("missing or wrong low surrogate")); + } + } + + string_t result; + + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + result.append(1, static_cast<typename string_t::value_type>(codepoint)); + } + else if (codepoint <= 0x7ff) + { + // 2-byte characters: 110xxxxx 10xxxxxx + result.append(1, static_cast<typename string_t::value_type>(0xC0 | ((codepoint >> 6) & 0x1F))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F))); + } + else if (codepoint <= 0xffff) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + result.append(1, static_cast<typename string_t::value_type>(0xE0 | ((codepoint >> 12) & 0x0F))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 6) & 0x3F))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F))); + } + else if (codepoint <= 0x10ffff) + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + result.append(1, static_cast<typename string_t::value_type>(0xF0 | ((codepoint >> 18) & 0x07))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 12) & 0x3F))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 6) & 0x3F))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F))); + } + else + { JSON_THROW(std::out_of_range("code points above 0x10FFFF are invalid")); - } - - return result; } - /// return name of values of type token_type (only used for errors) - static std::string token_type_name(const token_type t) + return result; + } + + /// return name of values of type token_type (only used for errors) + static std::string token_type_name(const token_type t) + { + switch (t) { - switch (t) + case token_type::uninitialized: + return "<uninitialized>"; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case token_type::value_number: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return "<parse error>"; + case token_type::end_of_input: + return "end of input"; + default: { - case token_type::uninitialized: - return "<uninitialized>"; - case token_type::literal_true: - return "true literal"; - case token_type::literal_false: - return "false literal"; - case token_type::literal_null: - return "null literal"; - case token_type::value_string: - return "string literal"; - case token_type::value_number: - return "number literal"; - case token_type::begin_array: - return "'['"; - case token_type::begin_object: - return "'{'"; - case token_type::end_array: - return "']'"; - case token_type::end_object: - return "'}'"; - case token_type::name_separator: - return "':'"; - case token_type::value_separator: - return "','"; - case token_type::parse_error: - return "<parse error>"; - case token_type::end_of_input: - return "end of input"; - default: + // catch non-enum values + return "unknown token"; // LCOV_EXCL_LINE + } + } + } + + /*! + This function implements a scanner for JSON. It is specified using + regular expressions that try to follow RFC 7159 as close as possible. + These regular expressions are then translated into a minimized + deterministic finite automaton (DFA) by the tool + [re2c](http://re2c.org). As a result, the translated code for this + function consists of a large block of code with `goto` jumps. + + @return the class of the next token read from the buffer + + @complexity Linear in the length of the input.\n + + Proposition: The loop below will always terminate for finite input.\n + + Proof (by contradiction): Assume a finite input. To loop forever, the + loop must never hit code with a `break` statement. The only code + snippets without a `break` statement are the continue statements for + whitespace and byte-order-marks. To loop forever, the input must be an + infinite sequence of whitespace or byte-order-marks. This contradicts + the assumption of finite input, q.e.d. + */ + token_type scan() + { + while (true) + { + // pointer for backtracking information + m_marker = nullptr; + + // remember the begin of the token + m_start = m_cursor; + assert(m_start != nullptr); + + + { + lexer_char_t yych; + unsigned int yyaccept = 0; + static const unsigned char yybm[] = { - // catch non-enum values - return "unknown token"; // LCOV_EXCL_LINE + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 32, 0, 0, 32, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 160, 128, 0, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 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, 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) + { + fill_line_buffer(5); // LCOV_EXCL_LINE } - } - } - - /*! - This function implements a scanner for JSON. It is specified using - regular expressions that try to follow RFC 7159 as close as possible. - These regular expressions are then translated into a minimized - deterministic finite automaton (DFA) by the tool - [re2c](http://re2c.org). As a result, the translated code for this - function consists of a large block of code with `goto` jumps. - - @return the class of the next token read from the buffer - - @complexity Linear in the length of the input.\n - - Proposition: The loop below will always terminate for finite input.\n - - Proof (by contradiction): Assume a finite input. To loop forever, the - loop must never hit code with a `break` statement. The only code - snippets without a `break` statement are the continue statements for - whitespace and byte-order-marks. To loop forever, the input must be an - infinite sequence of whitespace or byte-order-marks. This contradicts - the assumption of finite input, q.e.d. - */ - token_type scan() - { - while (true) - { - // pointer for backtracking information - m_marker = nullptr; - - // remember the begin of the token - m_start = m_cursor; - assert(m_start != nullptr); - - + yych = *m_cursor; + if (yybm[0 + yych] & 32) { - lexer_char_t yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = + goto basic_json_parser_6; + } + if (yych <= '[') + { + if (yych <= '-') { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 32, 32, 0, 0, 32, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 160, 128, 0, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 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, 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) - { - 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 <= '"') { - if (yych <= '"') + if (yych <= 0x00) { - if (yych <= 0x00) - { - goto basic_json_parser_2; - } - if (yych <= '!') - { - goto basic_json_parser_4; - } - goto basic_json_parser_9; + goto basic_json_parser_2; } - else + if (yych <= '!') { - if (yych <= '+') - { - goto basic_json_parser_4; - } - if (yych <= ',') - { - goto basic_json_parser_10; - } - goto basic_json_parser_12; + goto basic_json_parser_4; } + goto basic_json_parser_9; } else { - if (yych <= '9') + if (yych <= '+') { - if (yych <= '/') - { - goto basic_json_parser_4; - } - if (yych <= '0') - { - goto basic_json_parser_13; - } - goto basic_json_parser_15; + goto basic_json_parser_4; } - else + if (yych <= ',') { - if (yych <= ':') - { - goto basic_json_parser_17; - } - if (yych <= 'Z') - { - goto basic_json_parser_4; - } - goto basic_json_parser_19; + goto basic_json_parser_10; } + goto basic_json_parser_12; } } else { - if (yych <= 'n') + if (yych <= '9') { - if (yych <= 'e') + if (yych <= '/') { - if (yych == ']') - { - goto basic_json_parser_21; - } goto basic_json_parser_4; } - else + if (yych <= '0') { - if (yych <= 'f') - { - goto basic_json_parser_23; - } - if (yych <= 'm') - { - goto basic_json_parser_4; - } - goto basic_json_parser_24; + goto basic_json_parser_13; } + goto basic_json_parser_15; } else { - if (yych <= 'z') + if (yych <= ':') + { + goto basic_json_parser_17; + } + if (yych <= 'Z') { - if (yych == 't') - { - goto basic_json_parser_25; - } - goto basic_json_parser_4; - } - else - { - if (yych <= '{') - { - goto basic_json_parser_26; - } - if (yych == '}') - { - goto basic_json_parser_28; - } goto basic_json_parser_4; } + goto basic_json_parser_19; } } + } + else + { + if (yych <= 'n') + { + if (yych <= 'e') + { + if (yych == ']') + { + goto basic_json_parser_21; + } + goto basic_json_parser_4; + } + else + { + if (yych <= 'f') + { + goto basic_json_parser_23; + } + if (yych <= 'm') + { + goto basic_json_parser_4; + } + goto basic_json_parser_24; + } + } + else + { + if (yych <= 'z') + { + if (yych == 't') + { + goto basic_json_parser_25; + } + goto basic_json_parser_4; + } + else + { + if (yych <= '{') + { + goto basic_json_parser_26; + } + if (yych == '}') + { + goto basic_json_parser_28; + } + goto basic_json_parser_4; + } + } + } basic_json_parser_2: - ++m_cursor; - { - last_token_type = token_type::end_of_input; - break; - } + ++m_cursor; + { + last_token_type = token_type::end_of_input; + break; + } basic_json_parser_4: - ++m_cursor; + ++m_cursor; basic_json_parser_5: - { - last_token_type = token_type::parse_error; - break; - } + { + last_token_type = token_type::parse_error; + break; + } basic_json_parser_6: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yybm[0 + yych] & 32) - { - goto basic_json_parser_6; - } - { - continue; - } + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yybm[0 + yych] & 32) + { + goto basic_json_parser_6; + } + { + continue; + } basic_json_parser_9: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych <= 0x1F) - { - goto basic_json_parser_5; - } - if (yych <= 0x7F) - { - goto basic_json_parser_31; - } - if (yych <= 0xC1) - { - goto basic_json_parser_5; - } - if (yych <= 0xF4) - { - goto basic_json_parser_31; - } + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych <= 0x1F) + { goto basic_json_parser_5; + } + 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; - { - last_token_type = token_type::value_separator; - break; - } + ++m_cursor; + { + last_token_type = token_type::value_separator; + break; + } basic_json_parser_12: - yych = *++m_cursor; + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_5; + } + if (yych <= '0') + { + goto basic_json_parser_13; + } + if (yych <= '9') + { + goto basic_json_parser_15; + } + goto basic_json_parser_5; +basic_json_parser_13: + yyaccept = 1; + yych = *(m_marker = ++m_cursor); + if (yych <= 'D') + { + if (yych == '.') + { + goto basic_json_parser_43; + } + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_44; + } + if (yych == 'e') + { + goto basic_json_parser_44; + } + } +basic_json_parser_14: + { + last_token_type = token_type::value_number; + break; + } +basic_json_parser_15: + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) + { + fill_line_buffer(3); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yybm[0 + yych] & 64) + { + goto basic_json_parser_15; + } + if (yych <= 'D') + { + if (yych == '.') + { + goto basic_json_parser_43; + } + goto basic_json_parser_14; + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_44; + } + if (yych == 'e') + { + goto basic_json_parser_44; + } + goto basic_json_parser_14; + } +basic_json_parser_17: + ++m_cursor; + { + last_token_type = token_type::name_separator; + break; + } +basic_json_parser_19: + ++m_cursor; + { + last_token_type = token_type::begin_array; + break; + } +basic_json_parser_21: + ++m_cursor; + { + last_token_type = token_type::end_array; + break; + } +basic_json_parser_23: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'a') + { + goto basic_json_parser_45; + } + goto basic_json_parser_5; +basic_json_parser_24: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'u') + { + goto basic_json_parser_46; + } + goto basic_json_parser_5; +basic_json_parser_25: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'r') + { + goto basic_json_parser_47; + } + goto basic_json_parser_5; +basic_json_parser_26: + ++m_cursor; + { + last_token_type = token_type::begin_object; + break; + } +basic_json_parser_28: + ++m_cursor; + { + last_token_type = token_type::end_object; + break; + } +basic_json_parser_30: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; +basic_json_parser_31: + if (yybm[0 + yych] & 128) + { + goto basic_json_parser_30; + } + if (yych <= 0xE0) + { + 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; + } + } + else + { + 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; + } + } + } +basic_json_parser_32: + m_cursor = m_marker; + if (yyaccept == 0) + { + goto basic_json_parser_5; + } + else + { + goto basic_json_parser_14; + } +basic_json_parser_33: + ++m_cursor; + { + last_token_type = token_type::value_string; + break; + } +basic_json_parser_35: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 'e') + { if (yych <= '/') { - goto basic_json_parser_5; - } - if (yych <= '0') - { - goto basic_json_parser_13; - } - if (yych <= '9') - { - goto basic_json_parser_15; - } - goto basic_json_parser_5; -basic_json_parser_13: - yyaccept = 1; - yych = *(m_marker = ++m_cursor); - if (yych <= 'D') - { - if (yych == '.') + if (yych == '"') { - goto basic_json_parser_43; + goto basic_json_parser_30; } - } - else - { - if (yych <= 'E') + if (yych <= '.') { - goto basic_json_parser_44; + goto basic_json_parser_32; } - if (yych == 'e') - { - goto basic_json_parser_44; - } - } -basic_json_parser_14: - { - last_token_type = token_type::value_number; - break; - } -basic_json_parser_15: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) - { - fill_line_buffer(3); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yybm[0 + yych] & 64) - { - goto basic_json_parser_15; - } - if (yych <= 'D') - { - if (yych == '.') - { - goto basic_json_parser_43; - } - goto basic_json_parser_14; - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_44; - } - if (yych == 'e') - { - goto basic_json_parser_44; - } - goto basic_json_parser_14; - } -basic_json_parser_17: - ++m_cursor; - { - last_token_type = token_type::name_separator; - break; - } -basic_json_parser_19: - ++m_cursor; - { - last_token_type = token_type::begin_array; - break; - } -basic_json_parser_21: - ++m_cursor; - { - last_token_type = token_type::end_array; - break; - } -basic_json_parser_23: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'a') - { - goto basic_json_parser_45; - } - goto basic_json_parser_5; -basic_json_parser_24: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'u') - { - goto basic_json_parser_46; - } - goto basic_json_parser_5; -basic_json_parser_25: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'r') - { - goto basic_json_parser_47; - } - goto basic_json_parser_5; -basic_json_parser_26: - ++m_cursor; - { - last_token_type = token_type::begin_object; - break; - } -basic_json_parser_28: - ++m_cursor; - { - last_token_type = token_type::end_object; - break; - } -basic_json_parser_30: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; -basic_json_parser_31: - if (yybm[0 + yych] & 128) - { goto basic_json_parser_30; } - if (yych <= 0xE0) + else { 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; - } - } - else - { - 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; - } - } - } -basic_json_parser_32: - m_cursor = m_marker; - if (yyaccept == 0) - { - goto basic_json_parser_5; - } - else - { - goto basic_json_parser_14; - } -basic_json_parser_33: - ++m_cursor; - { - last_token_type = token_type::value_string; - break; - } -basic_json_parser_35: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= 'e') - { - if (yych <= '/') - { - if (yych == '"') - { - goto basic_json_parser_30; - } - if (yych <= '.') + if (yych <= '[') { goto basic_json_parser_32; } @@ -10174,33 +10195,33 @@ basic_json_parser_35: } else { - if (yych <= '\\') + if (yych == 'b') { - if (yych <= '[') - { - goto basic_json_parser_32; - } goto basic_json_parser_30; } - else - { - if (yych == 'b') - { - goto basic_json_parser_30; - } - goto basic_json_parser_32; - } + goto basic_json_parser_32; } } + } + else + { + if (yych <= 'q') + { + if (yych <= 'f') + { + goto basic_json_parser_30; + } + if (yych == 'n') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } else { - if (yych <= 'q') + if (yych <= 's') { - if (yych <= 'f') - { - goto basic_json_parser_30; - } - if (yych == 'n') + if (yych <= 'r') { goto basic_json_parser_30; } @@ -10208,920 +10229,910 @@ basic_json_parser_35: } else { - if (yych <= 's') + if (yych <= 't') { - if (yych <= 'r') - { - goto basic_json_parser_30; - } - goto basic_json_parser_32; + goto basic_json_parser_30; } - else + if (yych <= 'u') { - if (yych <= 't') - { - goto basic_json_parser_30; - } - if (yych <= 'u') - { - goto basic_json_parser_48; - } - goto basic_json_parser_32; + goto basic_json_parser_48; } + 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; - } + ++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; - } + ++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; - } + ++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; - } + ++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; - } + ++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; - } + ++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; - } + ++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_32; - } - if (yych <= '9') - { - goto basic_json_parser_49; - } + yych = *++m_cursor; + if (yych <= '/') + { goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_49; + } + goto basic_json_parser_32; basic_json_parser_44: - yych = *++m_cursor; - if (yych <= ',') + yych = *++m_cursor; + if (yych <= ',') + { + if (yych == '+') { - if (yych == '+') - { - goto basic_json_parser_51; - } - goto basic_json_parser_32; - } - else - { - if (yych <= '-') - { - goto basic_json_parser_51; - } - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych <= '9') - { - goto basic_json_parser_52; - } - goto basic_json_parser_32; - } -basic_json_parser_45: - yych = *++m_cursor; - if (yych == 'l') - { - goto basic_json_parser_54; + goto basic_json_parser_51; } goto basic_json_parser_32; -basic_json_parser_46: - yych = *++m_cursor; - if (yych == 'l') + } + else + { + if (yych <= '-') { - goto basic_json_parser_55; + goto basic_json_parser_51; } - goto basic_json_parser_32; -basic_json_parser_47: - yych = *++m_cursor; - if (yych == 'u') - { - goto basic_json_parser_56; - } - goto basic_json_parser_32; -basic_json_parser_48: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych <= '9') - { - goto basic_json_parser_57; - } - goto basic_json_parser_32; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_57; - } - if (yych <= '`') - { - goto basic_json_parser_32; - } - if (yych <= 'f') - { - goto basic_json_parser_57; - } - goto basic_json_parser_32; - } -basic_json_parser_49: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) - { - fill_line_buffer(3); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= 'D') - { - if (yych <= '/') - { - goto basic_json_parser_14; - } - if (yych <= '9') - { - goto basic_json_parser_49; - } - goto basic_json_parser_14; - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_44; - } - if (yych == 'e') - { - goto basic_json_parser_44; - } - goto basic_json_parser_14; - } -basic_json_parser_51: - yych = *++m_cursor; if (yych <= '/') { goto basic_json_parser_32; } - if (yych >= ':') - { - goto basic_json_parser_32; - } -basic_json_parser_52: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= '/') - { - goto basic_json_parser_14; - } if (yych <= '9') { goto basic_json_parser_52; } - goto basic_json_parser_14; -basic_json_parser_54: - yych = *++m_cursor; - if (yych == 's') - { - goto basic_json_parser_58; - } goto basic_json_parser_32; -basic_json_parser_55: - yych = *++m_cursor; - if (yych == 'l') - { - goto basic_json_parser_59; - } - goto basic_json_parser_32; -basic_json_parser_56: - yych = *++m_cursor; - if (yych == 'e') - { - goto basic_json_parser_61; - } - goto basic_json_parser_32; -basic_json_parser_57: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych <= '9') - { - goto basic_json_parser_63; - } - goto basic_json_parser_32; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_63; - } - if (yych <= '`') - { - goto basic_json_parser_32; - } - if (yych <= 'f') - { - goto basic_json_parser_63; - } - goto basic_json_parser_32; - } -basic_json_parser_58: - yych = *++m_cursor; - if (yych == 'e') - { - goto basic_json_parser_64; - } - goto basic_json_parser_32; -basic_json_parser_59: - ++m_cursor; - { - last_token_type = token_type::literal_null; - break; - } -basic_json_parser_61: - ++m_cursor; - { - last_token_type = token_type::literal_true; - break; - } -basic_json_parser_63: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych <= '9') - { - goto basic_json_parser_66; - } - goto basic_json_parser_32; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_66; - } - if (yych <= '`') - { - goto basic_json_parser_32; - } - if (yych <= 'f') - { - goto basic_json_parser_66; - } - goto basic_json_parser_32; - } -basic_json_parser_64: - ++m_cursor; - { - last_token_type = token_type::literal_false; - break; - } -basic_json_parser_66: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych <= '9') - { - goto basic_json_parser_30; - } - goto basic_json_parser_32; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_30; - } - if (yych <= '`') - { - goto basic_json_parser_32; - } - if (yych <= 'f') - { - goto basic_json_parser_30; - } - goto basic_json_parser_32; - } } - - } - - return last_token_type; - } - - /*! - @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 auto 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()) - { - // 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); - - // 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) +basic_json_parser_45: + yych = *++m_cursor; + if (yych == 'l') { - m_line_buffer.append(n - 1, '\x01'); + goto basic_json_parser_54; } - } - 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'); - - // add line with newline symbol to the line buffer - m_line_buffer += m_line_buffer_tmp; - m_line_buffer.push_back('\n'); - } - - // 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_line_buffer.size(); - } - - /// return string representation of last read token - string_t get_token_string() const - { - assert(m_start != nullptr); - return string_t(reinterpret_cast<typename string_t::const_pointer>(m_start), - static_cast<size_t>(m_cursor - m_start)); - } - - /*! - @brief return string value for string tokens - - The function iterates the characters between the opening and closing - quotes of the string value. The complete string is the range - [m_start,m_cursor). Consequently, we iterate from m_start+1 to - m_cursor-1. - - We differentiate two cases: - - 1. Escaped characters. In this case, a new character is constructed - according to the nature of the escape. Some escapes create new - characters (e.g., `"\\n"` is replaced by `"\n"`), some are copied - as is (e.g., `"\\\\"`). Furthermore, Unicode escapes of the shape - `"\\uxxxx"` need special care. In this case, to_unicode takes care - of the construction of the values. - 2. Unescaped characters are copied as is. - - @pre `m_cursor - m_start >= 2`, meaning the length of the last token - is at least 2 bytes which is trivially true for any string (which - consists of at least two quotes). - - " c1 c2 c3 ... " - ^ ^ - m_start m_cursor - - @complexity Linear in the length of the string.\n - - Lemma: The loop body will always terminate.\n - - Proof (by contradiction): Assume the loop body does not terminate. As - the loop body does not contain another loop, one of the called - functions must never return. The called functions are `std::strtoul` - and to_unicode. Neither function can loop forever, so the loop body - will never loop forever which contradicts the assumption that the loop - body does not terminate, q.e.d.\n - - Lemma: The loop condition for the for loop is eventually false.\n - - Proof (by contradiction): Assume the loop does not terminate. Due to - the above lemma, this can only be due to a tautological loop - condition; that is, the loop condition i < m_cursor - 1 must always be - true. Let x be the change of i for any loop iteration. Then - m_start + 1 + x < m_cursor - 1 must hold to loop indefinitely. This - can be rephrased to m_cursor - m_start - 2 > x. With the - precondition, we x <= 0, meaning that the loop condition holds - indefinitly if i is always decreased. However, observe that the value - of i is strictly increasing with each iteration, as it is incremented - by 1 in the iteration expression and never decremented inside the loop - body. Hence, the loop condition will eventually be false which - contradicts the assumption that the loop condition is a tautology, - q.e.d. - - @return string value of current token without opening and closing - quotes - @throw std::out_of_range if to_unicode fails - */ - string_t get_string() const - { - assert(m_cursor - m_start >= 2); - - string_t result; - result.reserve(static_cast<size_t>(m_cursor - m_start - 2)); - - // iterate the result between the quotes - for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++i) - { - // find next escape character - auto e = std::find(i, m_cursor - 1, '\\'); - if (e != i) + goto basic_json_parser_32; +basic_json_parser_46: + yych = *++m_cursor; + if (yych == 'l') { - // see https://github.com/nlohmann/json/issues/365#issuecomment-262874705 - for (auto k = i; k < e; k++) + goto basic_json_parser_55; + } + goto basic_json_parser_32; +basic_json_parser_47: + yych = *++m_cursor; + if (yych == 'u') + { + goto basic_json_parser_56; + } + goto basic_json_parser_32; +basic_json_parser_48: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') { - result.push_back(static_cast<typename string_t::value_type>(*k)); + goto basic_json_parser_32; } - i = e - 1; // -1 because of ++i + if (yych <= '9') + { + goto basic_json_parser_57; + } + goto basic_json_parser_32; } else { - // processing escaped character - // read next character - ++i; - - switch (*i) + if (yych <= 'F') { - // the default escapes - case 't': - { - result += "\t"; - break; - } - case 'b': - { - result += "\b"; - break; - } - case 'f': - { - result += "\f"; - break; - } - case 'n': - { - result += "\n"; - break; - } - case 'r': - { - result += "\r"; - break; - } - case '\\': - { - result += "\\"; - break; - } - case '/': - { - result += "/"; - break; - } - case '"': - { - result += "\""; - break; - } - - // unicode - case 'u': - { - // get code xxxx from uxxxx - auto codepoint = std::strtoul(std::string(reinterpret_cast<typename string_t::const_pointer>(i + 1), - 4).c_str(), nullptr, 16); - - // check if codepoint is a high surrogate - if (codepoint >= 0xD800 and codepoint <= 0xDBFF) - { - // make sure there is a subsequent unicode - if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u') - { - JSON_THROW(std::invalid_argument("missing low surrogate")); - } - - // get code yyyy from uxxxx\uyyyy - auto codepoint2 = std::strtoul(std::string(reinterpret_cast<typename string_t::const_pointer> - (i + 7), 4).c_str(), nullptr, 16); - result += to_unicode(codepoint, codepoint2); - // skip the next 10 characters (xxxx\uyyyy) - i += 10; - } - else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF) - { - // we found a lone low surrogate - JSON_THROW(std::invalid_argument("missing high surrogate")); - } - else - { - // add unicode character(s) - result += to_unicode(codepoint); - // skip the next four characters (xxxx) - i += 4; - } - break; - } + goto basic_json_parser_57; } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_57; + } + goto basic_json_parser_32; } - } - - return result; - } - - /*! - @brief parse floating point number - - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function based on the type - supplied via the first parameter. Set this to @a - static_cast<number_float_t*>(nullptr). - - @param[in,out] endptr recieves a pointer to the first character after - the number - - @return the floating point number - */ - long double str_to_float_t(long double* /* type */, char** endptr) const - { - return std::strtold(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); - } - - /*! - @brief parse floating point number - - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function based on the type - supplied via the first parameter. Set this to @a - static_cast<number_float_t*>(nullptr). - - @param[in,out] endptr recieves a pointer to the first character after - the number - - @return the floating point number - */ - double str_to_float_t(double* /* type */, char** endptr) const - { - return std::strtod(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); - } - - /*! - @brief parse floating point number - - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function based on the type - supplied via the first parameter. Set this to @a - static_cast<number_float_t*>(nullptr). - - @param[in,out] endptr recieves a pointer to the first character after - the number - - @return the floating point number - */ - float str_to_float_t(float* /* type */, char** endptr) const - { - return std::strtof(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); - } - - /*! - @brief return number value for number tokens - - This function translates the last token into the most appropriate - number type (either integer, unsigned integer or floating point), - which is passed back to the caller via the result parameter. - - This function parses the integer component up to the radix point or - exponent while collecting information about the 'floating point - representation', which it stores in the result parameter. If there is - no radix point or exponent, and the number can fit into a @ref - number_integer_t or @ref number_unsigned_t then it sets the result - parameter accordingly. - - If the number is a floating point number the number is then parsed - using @a std:strtod (or @a std:strtof or @a std::strtold). - - @param[out] result @ref basic_json object to receive the number, or - NAN if the conversion read past the current token. The latter case - needs to be treated by the caller function. - */ - void get_number(basic_json& result) const - { - assert(m_start != nullptr); - - const lexer::lexer_char_t* curptr = m_start; - - // accumulate the integer conversion result (unsigned for now) - number_unsigned_t value = 0; - - // maximum absolute value of the relevant integer type - number_unsigned_t max; - - // temporarily store the type to avoid unecessary bitfield access - value_t type; - - // look for sign - if (*curptr == '-') - { - type = value_t::number_integer; - max = static_cast<uint64_t>((std::numeric_limits<number_integer_t>::max)()) + 1; - curptr++; - } - else - { - type = value_t::number_unsigned; - max = static_cast<uint64_t>((std::numeric_limits<number_unsigned_t>::max)()); - } - - // count the significant figures - for (; curptr < m_cursor; curptr++) - { - // quickly skip tests if a digit - if (*curptr < '0' || *curptr > '9') +basic_json_parser_49: + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) { - if (*curptr == '.') + fill_line_buffer(3); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 'D') + { + if (yych <= '/') { - // don't count '.' but change to float - type = value_t::number_float; - continue; + goto basic_json_parser_14; } - // assume exponent (if not then will fail parse): change to - // float, stop counting and record exponent details - type = value_t::number_float; + if (yych <= '9') + { + goto basic_json_parser_49; + } + goto basic_json_parser_14; + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_44; + } + if (yych == 'e') + { + goto basic_json_parser_44; + } + goto basic_json_parser_14; + } +basic_json_parser_51: + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych >= ':') + { + goto basic_json_parser_32; + } +basic_json_parser_52: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '/') + { + goto basic_json_parser_14; + } + if (yych <= '9') + { + goto basic_json_parser_52; + } + goto basic_json_parser_14; +basic_json_parser_54: + yych = *++m_cursor; + if (yych == 's') + { + goto basic_json_parser_58; + } + goto basic_json_parser_32; +basic_json_parser_55: + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_59; + } + goto basic_json_parser_32; +basic_json_parser_56: + yych = *++m_cursor; + if (yych == 'e') + { + goto basic_json_parser_61; + } + goto basic_json_parser_32; +basic_json_parser_57: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_63; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_63; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_63; + } + goto basic_json_parser_32; + } +basic_json_parser_58: + yych = *++m_cursor; + if (yych == 'e') + { + goto basic_json_parser_64; + } + goto basic_json_parser_32; +basic_json_parser_59: + ++m_cursor; + { + last_token_type = token_type::literal_null; break; } - - // skip if definitely not an integer - if (type != value_t::number_float) +basic_json_parser_61: + ++m_cursor; { - auto digit = static_cast<number_unsigned_t>(*curptr - '0'); - - // 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 - value = value * 10 + digit; - } + last_token_type = token_type::literal_true; + break; } - } - - // save the value (if not a float) - if (type == value_t::number_unsigned) - { - result.m_value.number_unsigned = value; - } - else if (type == value_t::number_integer) - { - // 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) +basic_json_parser_63: + ++m_cursor; + if (m_limit <= m_cursor) { - // 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); + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_66; + } + goto basic_json_parser_32; } else { - // all other values can be negated safely - result.m_value.number_integer = -static_cast<number_integer_t>(value); + if (yych <= 'F') + { + goto basic_json_parser_66; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_66; + } + goto basic_json_parser_32; } +basic_json_parser_64: + ++m_cursor; + { + last_token_type = token_type::literal_false; + break; + } +basic_json_parser_66: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_30; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + } + + } + + return last_token_type; + } + + /*! + @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 auto 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()) + { + // 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); + + // 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'); + + // add line with newline symbol to the line buffer + m_line_buffer += m_line_buffer_tmp; + m_line_buffer.push_back('\n'); + } + + // 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_line_buffer.size(); + } + + /// return string representation of last read token + string_t get_token_string() const + { + assert(m_start != nullptr); + return string_t(reinterpret_cast<typename string_t::const_pointer>(m_start), + static_cast<size_t>(m_cursor - m_start)); + } + + /*! + @brief return string value for string tokens + + The function iterates the characters between the opening and closing + quotes of the string value. The complete string is the range + [m_start,m_cursor). Consequently, we iterate from m_start+1 to + m_cursor-1. + + We differentiate two cases: + + 1. Escaped characters. In this case, a new character is constructed + according to the nature of the escape. Some escapes create new + characters (e.g., `"\\n"` is replaced by `"\n"`), some are copied + as is (e.g., `"\\\\"`). Furthermore, Unicode escapes of the shape + `"\\uxxxx"` need special care. In this case, to_unicode takes care + of the construction of the values. + 2. Unescaped characters are copied as is. + + @pre `m_cursor - m_start >= 2`, meaning the length of the last token + is at least 2 bytes which is trivially true for any string (which + consists of at least two quotes). + + " c1 c2 c3 ... " + ^ ^ + m_start m_cursor + + @complexity Linear in the length of the string.\n + + Lemma: The loop body will always terminate.\n + + Proof (by contradiction): Assume the loop body does not terminate. As + the loop body does not contain another loop, one of the called + functions must never return. The called functions are `std::strtoul` + and to_unicode. Neither function can loop forever, so the loop body + will never loop forever which contradicts the assumption that the loop + body does not terminate, q.e.d.\n + + Lemma: The loop condition for the for loop is eventually false.\n + + Proof (by contradiction): Assume the loop does not terminate. Due to + the above lemma, this can only be due to a tautological loop + condition; that is, the loop condition i < m_cursor - 1 must always be + true. Let x be the change of i for any loop iteration. Then + m_start + 1 + x < m_cursor - 1 must hold to loop indefinitely. This + can be rephrased to m_cursor - m_start - 2 > x. With the + precondition, we x <= 0, meaning that the loop condition holds + indefinitly if i is always decreased. However, observe that the value + of i is strictly increasing with each iteration, as it is incremented + by 1 in the iteration expression and never decremented inside the loop + body. Hence, the loop condition will eventually be false which + contradicts the assumption that the loop condition is a tautology, + q.e.d. + + @return string value of current token without opening and closing + quotes + @throw std::out_of_range if to_unicode fails + */ + string_t get_string() const + { + assert(m_cursor - m_start >= 2); + + string_t result; + result.reserve(static_cast<size_t>(m_cursor - m_start - 2)); + + // iterate the result between the quotes + for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++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 { - // parse with strtod - result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), nullptr); + // processing escaped character + // read next character + ++i; - // replace infinity and NAN by null - if (not std::isfinite(result.m_value.number_float)) + switch (*i) { - type = value_t::null; - result.m_value = basic_json::json_value(); + // the default escapes + case 't': + { + result += "\t"; + break; + } + case 'b': + { + result += "\b"; + break; + } + case 'f': + { + result += "\f"; + break; + } + case 'n': + { + result += "\n"; + break; + } + case 'r': + { + result += "\r"; + break; + } + case '\\': + { + result += "\\"; + break; + } + case '/': + { + result += "/"; + break; + } + case '"': + { + result += "\""; + break; + } + + // unicode + case 'u': + { + // get code xxxx from uxxxx + auto codepoint = std::strtoul(std::string(reinterpret_cast<typename string_t::const_pointer>(i + 1), + 4).c_str(), nullptr, 16); + + // check if codepoint is a high surrogate + if (codepoint >= 0xD800 and codepoint <= 0xDBFF) + { + // make sure there is a subsequent unicode + if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u') + { + JSON_THROW(std::invalid_argument("missing low surrogate")); + } + + // get code yyyy from uxxxx\uyyyy + auto codepoint2 = std::strtoul(std::string(reinterpret_cast<typename string_t::const_pointer> + (i + 7), 4).c_str(), nullptr, 16); + result += to_unicode(codepoint, codepoint2); + // skip the next 10 characters (xxxx\uyyyy) + i += 10; + } + else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF) + { + // we found a lone low surrogate + JSON_THROW(std::invalid_argument("missing high surrogate")); + } + else + { + // add unicode character(s) + result += to_unicode(codepoint); + // skip the next four characters (xxxx) + i += 4; + } + break; + } } } - - // save the type - result.m_type = type; } - private: - /// optional input stream - std::istream* m_stream = nullptr; - /// 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 - const lexer_char_t* m_start = nullptr; - /// pointer for backtracking information - const lexer_char_t* m_marker = nullptr; - /// pointer to the current symbol - const lexer_char_t* m_cursor = nullptr; - /// pointer to the end of the buffer - const lexer_char_t* m_limit = nullptr; - /// the last token type - token_type last_token_type = token_type::end_of_input; - }; + return result; + } + + /*! + @brief parse floating point number + + This function (and its overloads) serves to select the most approprate + standard floating point number parsing function based on the type + supplied via the first parameter. Set this to @a + static_cast<number_float_t*>(nullptr). + + @param[in,out] endptr recieves a pointer to the first character after + the number + + @return the floating point number + */ + long double str_to_float_t(long double* /* type */, char** endptr) const + { + return std::strtold(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); + } + + /*! + @brief parse floating point number + + This function (and its overloads) serves to select the most approprate + standard floating point number parsing function based on the type + supplied via the first parameter. Set this to @a + static_cast<number_float_t*>(nullptr). + + @param[in,out] endptr recieves a pointer to the first character after + the number + + @return the floating point number + */ + double str_to_float_t(double* /* type */, char** endptr) const + { + return std::strtod(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); + } + + /*! + @brief parse floating point number + + This function (and its overloads) serves to select the most approprate + standard floating point number parsing function based on the type + supplied via the first parameter. Set this to @a + static_cast<number_float_t*>(nullptr). + + @param[in,out] endptr recieves a pointer to the first character after + the number + + @return the floating point number + */ + float str_to_float_t(float* /* type */, char** endptr) const + { + return std::strtof(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); + } + + /*! + @brief return number value for number tokens + + This function translates the last token into the most appropriate + number type (either integer, unsigned integer or floating point), + which is passed back to the caller via the result parameter. + + This function parses the integer component up to the radix point or + exponent while collecting information about the 'floating point + representation', which it stores in the result parameter. If there is + no radix point or exponent, and the number can fit into a @ref + number_integer_t or @ref number_unsigned_t then it sets the result + parameter accordingly. + + If the number is a floating point number the number is then parsed + using @a std:strtod (or @a std:strtof or @a std::strtold). + + @param[out] result @ref basic_json object to receive the number, or + NAN if the conversion read past the current token. The latter case + needs to be treated by the caller function. + */ + void get_number(basic_json& result) const + { + assert(m_start != nullptr); + + const lexer::lexer_char_t* curptr = m_start; + + // accumulate the integer conversion result (unsigned for now) + number_unsigned_t value = 0; + + // maximum absolute value of the relevant integer type + number_unsigned_t max; + + // temporarily store the type to avoid unecessary bitfield access + value_t type; + + // look for sign + if (*curptr == '-') + { + type = value_t::number_integer; + max = static_cast<uint64_t>((std::numeric_limits<number_integer_t>::max)()) + 1; + curptr++; + } + else + { + type = value_t::number_unsigned; + max = static_cast<uint64_t>((std::numeric_limits<number_unsigned_t>::max)()); + } + + // count the significant figures + for (; curptr < m_cursor; curptr++) + { + // quickly skip tests if a digit + if (*curptr < '0' || *curptr > '9') + { + if (*curptr == '.') + { + // don't count '.' but change to float + type = value_t::number_float; + continue; + } + // assume exponent (if not then will fail parse): change to + // float, stop counting and record exponent details + type = value_t::number_float; + break; + } + + // skip if definitely not an integer + if (type != value_t::number_float) + { + auto digit = static_cast<number_unsigned_t>(*curptr - '0'); + + // 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 + value = value * 10 + digit; + } + } + } + + // save the value (if not a float) + if (type == value_t::number_unsigned) + { + result.m_value.number_unsigned = value; + } + else if (type == value_t::number_integer) + { + // 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), nullptr); + + // 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 + result.m_type = type; + } + + private: + /// optional input stream + std::istream* m_stream = nullptr; + /// 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 + const lexer_char_t* m_start = nullptr; + /// pointer for backtracking information + const lexer_char_t* m_marker = nullptr; + /// pointer to the current symbol + const lexer_char_t* m_cursor = nullptr; + /// pointer to the end of the buffer + const lexer_char_t* m_limit = nullptr; + /// the last token type + token_type last_token_type = token_type::end_of_input; + }; /*! @brief syntax analysis @@ -11130,282 +11141,282 @@ basic_json_parser_66: */ class parser { - public: + public: /// 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)) - {} + : 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) - : callback(cb), m_lexer(is) - {} + /// a parser reading from an input stream + 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))) - {} + /// 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() + /// public parser interface + basic_json parse() + { + // read first token + get_token(); + + basic_json result = parse_internal(true); + result.assert_invariant(); + + expect(lexer::token_type::end_of_input); + + // return parser result and replace it with null in case the + // top-level value was discarded by the callback function + return result.is_discarded() ? basic_json() : std::move(result); + } + + private: + /// the actual parser + basic_json parse_internal(bool keep) + { + auto result = basic_json(value_t::discarded); + + switch (last_token) { - // read first token - get_token(); - - basic_json result = parse_internal(true); - result.assert_invariant(); - - expect(lexer::token_type::end_of_input); - - // return parser result and replace it with null in case the - // top-level value was discarded by the callback function - return result.is_discarded() ? basic_json() : std::move(result); - } - - private: - /// the actual parser - basic_json parse_internal(bool keep) - { - auto result = basic_json(value_t::discarded); - - switch (last_token) + case lexer::token_type::begin_object: { - case lexer::token_type::begin_object: + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) { - 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; - result.m_value = value_t::object; - } + // explicitly set result to object to cope with {} + result.m_type = value_t::object; + result.m_value = value_t::object; + } - // read next token - get_token(); + // read next token + get_token(); - // closing } -> we are done - if (last_token == lexer::token_type::end_object) - { - get_token(); - if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) - { - result = basic_json(value_t::discarded); - } - return result; - } - - // no comma is expected here - unexpect(lexer::token_type::value_separator); - - // otherwise: parse key-value pairs - do - { - // ugly, but could be fixed with loop reorganization - if (last_token == lexer::token_type::value_separator) - { - get_token(); - } - - // store key - expect(lexer::token_type::value_string); - const auto key = m_lexer.get_string(); - - bool keep_tag = false; - if (keep) - { - if (callback) - { - basic_json k(key); - keep_tag = callback(depth, parse_event_t::key, k); - } - else - { - keep_tag = true; - } - } - - // parse separator (:) - get_token(); - expect(lexer::token_type::name_separator); - - // parse and add value - get_token(); - auto value = parse_internal(keep); - if (keep and keep_tag and not value.is_discarded()) - { - result[key] = std::move(value); - } - } - while (last_token == lexer::token_type::value_separator); - - // closing } - expect(lexer::token_type::end_object); + // closing } -> we are done + if (last_token == lexer::token_type::end_object) + { get_token(); if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) { result = basic_json(value_t::discarded); } - return result; } - case lexer::token_type::begin_array: + // no comma is expected here + unexpect(lexer::token_type::value_separator); + + // otherwise: parse key-value pairs + do { - 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; - result.m_value = value_t::array; - } - - // read next token - get_token(); - - // closing ] -> we are done - if (last_token == lexer::token_type::end_array) + // ugly, but could be fixed with loop reorganization + if (last_token == lexer::token_type::value_separator) { get_token(); - if (callback and not callback(--depth, parse_event_t::array_end, result)) - { - result = basic_json(value_t::discarded); - } - return result; } - // no comma is expected here - unexpect(lexer::token_type::value_separator); + // store key + expect(lexer::token_type::value_string); + const auto key = m_lexer.get_string(); - // otherwise: parse values - do + bool keep_tag = false; + if (keep) { - // ugly, but could be fixed with loop reorganization - if (last_token == lexer::token_type::value_separator) + if (callback) { - get_token(); + basic_json k(key); + keep_tag = callback(depth, parse_event_t::key, k); } - - // parse value - auto value = parse_internal(keep); - if (keep and not value.is_discarded()) + else { - result.push_back(std::move(value)); + keep_tag = true; } } - while (last_token == lexer::token_type::value_separator); - // closing ] - expect(lexer::token_type::end_array); + // parse separator (:) get_token(); - if (keep and callback and not callback(--depth, parse_event_t::array_end, result)) + expect(lexer::token_type::name_separator); + + // parse and add value + get_token(); + auto value = parse_internal(keep); + if (keep and keep_tag and not value.is_discarded()) + { + result[key] = std::move(value); + } + } + while (last_token == lexer::token_type::value_separator); + + // closing } + expect(lexer::token_type::end_object); + get_token(); + if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) + { + result = basic_json(value_t::discarded); + } + + return result; + } + + case lexer::token_type::begin_array: + { + 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; + result.m_value = value_t::array; + } + + // read next token + get_token(); + + // closing ] -> we are done + if (last_token == lexer::token_type::end_array) + { + get_token(); + if (callback and not callback(--depth, parse_event_t::array_end, result)) { result = basic_json(value_t::discarded); } - return result; } - case lexer::token_type::literal_null: + // no comma is expected here + unexpect(lexer::token_type::value_separator); + + // otherwise: parse values + do { - get_token(); - result.m_type = value_t::null; - break; + // ugly, but could be fixed with loop reorganization + if (last_token == lexer::token_type::value_separator) + { + get_token(); + } + + // parse value + auto value = parse_internal(keep); + if (keep and not value.is_discarded()) + { + result.push_back(std::move(value)); + } + } + while (last_token == lexer::token_type::value_separator); + + // closing ] + expect(lexer::token_type::end_array); + get_token(); + if (keep and callback and not callback(--depth, parse_event_t::array_end, result)) + { + result = basic_json(value_t::discarded); } - case lexer::token_type::value_string: - { - const auto s = m_lexer.get_string(); - get_token(); - result = basic_json(s); - break; - } - - case lexer::token_type::literal_true: - { - get_token(); - result.m_type = value_t::boolean; - result.m_value = true; - break; - } - - case lexer::token_type::literal_false: - { - get_token(); - result.m_type = value_t::boolean; - result.m_value = false; - break; - } - - case lexer::token_type::value_number: - { - m_lexer.get_number(result); - get_token(); - break; - } - - default: - { - // the last token was unexpected - unexpect(last_token); - } + return result; } - if (keep and callback and not callback(depth, parse_event_t::value, result)) + case lexer::token_type::literal_null: { - result = basic_json(value_t::discarded); + get_token(); + result.m_type = value_t::null; + break; } - return result; - } - /// get next token from lexer - typename lexer::token_type get_token() - { - last_token = m_lexer.scan(); - return last_token; - } - - void expect(typename lexer::token_type t) const - { - if (t != last_token) + case lexer::token_type::value_string: { - std::string error_msg = "parse error - unexpected "; - error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + - "'") : - lexer::token_type_name(last_token)); - error_msg += "; expected " + lexer::token_type_name(t); + const auto s = m_lexer.get_string(); + get_token(); + result = basic_json(s); + break; + } + + case lexer::token_type::literal_true: + { + get_token(); + result.m_type = value_t::boolean; + result.m_value = true; + break; + } + + case lexer::token_type::literal_false: + { + get_token(); + result.m_type = value_t::boolean; + result.m_value = false; + break; + } + + case lexer::token_type::value_number: + { + m_lexer.get_number(result); + get_token(); + break; + } + + default: + { + // the last token was unexpected + unexpect(last_token); + } + } + + if (keep and callback and not callback(depth, parse_event_t::value, result)) + { + result = basic_json(value_t::discarded); + } + return result; + } + + /// get next token from lexer + typename lexer::token_type get_token() + { + last_token = m_lexer.scan(); + return last_token; + } + + void expect(typename lexer::token_type t) const + { + if (t != last_token) + { + std::string error_msg = "parse error - unexpected "; + error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + + "'") : + lexer::token_type_name(last_token)); + error_msg += "; expected " + lexer::token_type_name(t); JSON_THROW(std::invalid_argument(error_msg)); - } } + } - void unexpect(typename lexer::token_type t) const + void unexpect(typename lexer::token_type t) const + { + if (t == last_token) { - if (t == last_token) - { - std::string error_msg = "parse error - unexpected "; - error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + - "'") : - lexer::token_type_name(last_token)); + std::string error_msg = "parse error - unexpected "; + error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + + "'") : + lexer::token_type_name(last_token)); JSON_THROW(std::invalid_argument(error_msg)); - } } + } - private: - /// current level of recursion - int depth = 0; - /// callback function - const parser_callback_t callback = nullptr; - /// the type of the last read token - typename lexer::token_type last_token = lexer::token_type::uninitialized; - /// the lexer - lexer m_lexer; - }; + private: + /// current level of recursion + int depth = 0; + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + typename lexer::token_type last_token = lexer::token_type::uninitialized; + /// the lexer + lexer m_lexer; + }; public: /*! @@ -11424,7 +11435,7 @@ basic_json_parser_66: /// allow basic_json to access private members friend class basic_json; - public: + public: /*! @brief create JSON pointer @@ -11448,562 +11459,562 @@ basic_json_parser_66: @since version 2.0.0 */ explicit json_pointer(const std::string& s = "") - : reference_tokens(split(s)) - {} + : reference_tokens(split(s)) + {} - /*! - @brief return a string representation of the JSON pointer + /*! + @brief return a string representation of the JSON pointer - @invariant For each JSON pointer `ptr`, it holds: - @code {.cpp} - ptr == json_pointer(ptr.to_string()); - @endcode + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode - @return a string representation of the JSON pointer + @return a string representation of the JSON pointer - @liveexample{The example shows the result of `to_string`., - json_pointer__to_string} + @liveexample{The example shows the result of `to_string`., + json_pointer__to_string} - @since version 2.0.0 - */ - std::string to_string() const noexcept + @since version 2.0.0 + */ + std::string to_string() const noexcept + { + return std::accumulate(reference_tokens.begin(), + reference_tokens.end(), std::string{}, + [](const std::string & a, const std::string & b) { - return std::accumulate(reference_tokens.begin(), - reference_tokens.end(), std::string{}, - [](const std::string & a, const std::string & b) - { - return a + "/" + escape(b); - }); - } + return a + "/" + escape(b); + }); + } - /// @copydoc to_string() - operator std::string() const - { - return to_string(); - } + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } - private: - /// remove and return last reference pointer - std::string pop_back() + private: + /// remove and return last reference pointer + std::string pop_back() + { + if (is_root()) { - if (is_root()) - { JSON_THROW(std::domain_error("JSON pointer has no parent")); - } - - auto last = reference_tokens.back(); - reference_tokens.pop_back(); - return last; } - /// return whether pointer points to the root document - bool is_root() const - { - return reference_tokens.empty(); - } + auto last = reference_tokens.back(); + reference_tokens.pop_back(); + return last; + } - json_pointer top() const + /// return whether pointer points to the root document + bool is_root() const + { + return reference_tokens.empty(); + } + + json_pointer top() const + { + if (is_root()) { - if (is_root()) - { JSON_THROW(std::domain_error("JSON pointer has no parent")); - } - - json_pointer result = *this; - result.reference_tokens = {reference_tokens[0]}; - return result; } - /*! - @brief create and return a reference to the pointed to value + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } - @complexity Linear in the number of reference tokens. - */ - reference get_and_create(reference j) const + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + */ + reference get_and_create(reference j) const + { + pointer result = &j; + + // in case no reference tokens exist, return a reference to the + // JSON value j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) { - pointer result = &j; - - // in case no reference tokens exist, return a reference to the - // JSON value j which will be overwritten by a primitive value - for (const auto& reference_token : reference_tokens) + switch (result->m_type) { - switch (result->m_type) + case value_t::null: { - case value_t::null: + if (reference_token == "0") { - if (reference_token == "0") - { - // start a new array if reference token is 0 - result = &result->operator[](0); - } - else - { - // start a new object otherwise - result = &result->operator[](reference_token); - } - break; + // start a new array if reference token is 0 + result = &result->operator[](0); } - - case value_t::object: + else { - // create an entry in the object + // start a new object otherwise result = &result->operator[](reference_token); - break; - } - - case value_t::array: - { - // create an entry in the array - result = &result->operator[](static_cast<size_type>(std::stoi(reference_token))); - break; - } - - /* - The following code is only reached if there exists a - reference token _and_ the current value is primitive. In - this case, we have an error situation, because primitive - values may only occur as single value; that is, with an - empty list of reference tokens. - */ - default: - { - JSON_THROW(std::domain_error("invalid value to unflatten")); - } - } - } - - return *result; - } - - /*! - @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 - - @complexity Linear in the length of the JSON pointer. - - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number - */ - reference get_unchecked(pointer ptr) const - { - 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: - { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } - - case value_t::array: - { - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - JSON_THROW(std::domain_error("array index must not begin with '0'")); - } - - if (reference_token == "-") - { - // explicityly treat "-" as index beyond the end - ptr = &ptr->operator[](ptr->m_value.array->size()); - } - else - { - // convert array index to number; unchecked access - ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token))); - } - break; - } - - default: - { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); - } - } - } - - return *ptr; - } - - reference get_checked(pointer ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->m_type) - { - case value_t::object: - { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; - } - - case value_t::array: - { - if (reference_token == "-") - { - // "-" always fails the range check - throw std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range"); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - JSON_THROW(std::domain_error("array index must not begin with '0'")); - } - - // note: at performs range check - ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token))); - break; - } - - default: - { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); - } - } - } - - return *ptr; - } - - /*! - @brief return a const reference to the pointed to value - - @param[in] ptr a JSON value - - @return const reference to the JSON value pointed to by the JSON - pointer - */ - const_reference get_unchecked(const_pointer ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->m_type) - { - case value_t::object: - { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } - - case value_t::array: - { - if (reference_token == "-") - { - // "-" cannot be used for const access - throw std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range"); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - JSON_THROW(std::domain_error("array index must not begin with '0'")); - } - - // use unchecked array access - ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token))); - break; - } - - default: - { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); - } - } - } - - return *ptr; - } - - const_reference get_checked(const_pointer ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->m_type) - { - case value_t::object: - { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; - } - - case value_t::array: - { - if (reference_token == "-") - { - // "-" always fails the range check - throw std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range"); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - JSON_THROW(std::domain_error("array index must not begin with '0'")); - } - - // note: at performs range check - ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token))); - break; - } - - default: - { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); - } - } - } - - return *ptr; - } - - /// split the string input to reference tokens - static std::vector<std::string> split(const std::string& reference_string) - { - std::vector<std::string> result; - - // special case: empty reference string -> no reference tokens - if (reference_string.empty()) - { - return result; - } - - // check if nonempty reference string begins with slash - if (reference_string[0] != '/') - { - JSON_THROW(std::domain_error("JSON pointer must be empty or begin with '/'")); - } - - // extract the reference tokens: - // - slash: position of the last read slash (or end of string) - // - start: position after the previous slash - for ( - // search for the first slash after the first character - size_t slash = reference_string.find_first_of('/', 1), - // set the beginning of the first reference token - start = 1; - // we can stop if start == string::npos+1 = 0 - start != 0; - // set the beginning of the next reference token - // (will eventually be 0 if slash == std::string::npos) - start = slash + 1, - // find next slash - slash = reference_string.find_first_of('/', start)) - { - // use the text between the beginning of the reference token - // (start) and the last slash (slash). - auto reference_token = reference_string.substr(start, slash - start); - - // check reference tokens are properly escaped - for (size_t pos = reference_token.find_first_of('~'); - pos != std::string::npos; - pos = reference_token.find_first_of('~', pos + 1)) - { - assert(reference_token[pos] == '~'); - - // ~ must be followed by 0 or 1 - if (pos == reference_token.size() - 1 or - (reference_token[pos + 1] != '0' and - reference_token[pos + 1] != '1')) - { - JSON_THROW(std::domain_error("escape error: '~' must be followed with '0' or '1'")); - } - } - - // finally, store the reference token - unescape(reference_token); - result.push_back(reference_token); - } - - return result; - } - - private: - /*! - @brief replace all occurrences of a substring by another string - - @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 - - @pre The search string @a f must not be empty. - - @since version 2.0.0 - */ - static void replace_substring(std::string& s, - const std::string& f, - const std::string& t) - { - assert(not f.empty()); - - for ( - size_t pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t - pos = s.find(f, pos + t.size()) // find next occurrence of f - ); - } - - /// escape tilde and slash - static std::string escape(std::string s) - { - // escape "~"" to "~0" and "/" to "~1" - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); - return s; - } - - /// unescape tilde and slash - static void unescape(std::string& s) - { - // first transform any occurrence of the sequence '~1' to '/' - replace_substring(s, "~1", "/"); - // then transform any occurrence of the sequence '~0' to '~' - replace_substring(s, "~0", "~"); - } - - /*! - @param[in] reference_string the reference string to the current value - @param[in] value the value to consider - @param[in,out] result the result object to insert values to - - @note Empty objects or arrays are flattened to `null`. - */ - static void flatten(const std::string& reference_string, - const basic_json& value, - basic_json& result) - { - switch (value.m_type) - { - case value_t::array: - { - if (value.m_value.array->empty()) - { - // flatten empty array as null - result[reference_string] = nullptr; - } - else - { - // iterate array and use index as reference string - for (size_t i = 0; i < value.m_value.array->size(); ++i) - { - flatten(reference_string + "/" + std::to_string(i), - value.m_value.array->operator[](i), result); - } } break; } case value_t::object: { - if (value.m_value.object->empty()) + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case value_t::array: + { + // create an entry in the array + result = &result->operator[](static_cast<size_type>(std::stoi(reference_token))); + break; + } + + /* + The following code is only reached if there exists a + reference token _and_ the current value is primitive. In + this case, we have an error situation, because primitive + values may only occur as single value; that is, with an + empty list of reference tokens. + */ + default: + { + JSON_THROW(std::domain_error("invalid value to unflatten")); + } + } + } + + return *result; + } + + /*! + @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 + + @complexity Linear in the length of the JSON pointer. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + */ + reference get_unchecked(pointer ptr) const + { + 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: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case value_t::array: + { + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') { - // flatten empty object as null - result[reference_string] = nullptr; + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } + + if (reference_token == "-") + { + // explicityly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); } else { - // iterate object and use keys as reference string - for (const auto& element : *value.m_value.object) - { - flatten(reference_string + "/" + escape(element.first), - element.second, result); - } + // convert array index to number; unchecked access + ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token))); } break; } default: { - // add primitive value with its reference string - result[reference_string] = value; - break; + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); } } } - /*! - @param[in] value flattened JSON + return *ptr; + } - @return unflattened JSON - */ - static basic_json unflatten(const basic_json& value) + reference get_checked(pointer ptr) const + { + for (const auto& reference_token : reference_tokens) { - if (not value.is_object()) + switch (ptr->m_type) { - JSON_THROW(std::domain_error("only objects can be unflattened")); - } - - basic_json result; - - // iterate the JSON object values - for (const auto& element : *value.m_value.object) - { - if (not element.second.is_primitive()) + case value_t::object: { - JSON_THROW(std::domain_error("values in object must be primitive")); + // note: at performs range check + ptr = &ptr->at(reference_token); + break; } - // assign value to reference pointed to by JSON pointer; Note - // that if the JSON pointer is "" (i.e., points to the whole - // value), function get_and_create returns a reference to - // result itself. An assignment will then create a primitive - // value. - json_pointer(element.first).get_and_create(result) = element.second; - } + case value_t::array: + { + if (reference_token == "-") + { + // "-" always fails the range check + throw std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range"); + } + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } + + // note: at performs range check + ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token))); + break; + } + + default: + { + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + } + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + */ + const_reference get_unchecked(const_pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case value_t::array: + { + if (reference_token == "-") + { + // "-" cannot be used for const access + throw std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range"); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } + + // use unchecked array access + ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token))); + break; + } + + default: + { + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + } + } + } + + return *ptr; + } + + const_reference get_checked(const_pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case value_t::array: + { + if (reference_token == "-") + { + // "-" always fails the range check + throw std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range"); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } + + // note: at performs range check + ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token))); + break; + } + + default: + { + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + } + } + } + + return *ptr; + } + + /// split the string input to reference tokens + static std::vector<std::string> split(const std::string& reference_string) + { + std::vector<std::string> result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { return result; } - private: - /// the reference tokens - std::vector<std::string> reference_tokens {}; - }; + // check if nonempty reference string begins with slash + if (reference_string[0] != '/') + { + JSON_THROW(std::domain_error("JSON pointer must be empty or begin with '/'")); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == string::npos+1 = 0 + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + assert(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (pos == reference_token.size() - 1 or + (reference_token[pos + 1] != '0' and + reference_token[pos + 1] != '1')) + { + JSON_THROW(std::domain_error("escape error: '~' must be followed with '0' or '1'")); + } + } + + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + private: + /*! + @brief replace all occurrences of a substring by another string + + @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 + + @pre The search string @a f must not be empty. + + @since version 2.0.0 + */ + static void replace_substring(std::string& s, + const std::string& f, + const std::string& t) + { + assert(not f.empty()); + + for ( + size_t pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t + pos = s.find(f, pos + t.size()) // find next occurrence of f + ); + } + + /// escape tilde and slash + static std::string escape(std::string s) + { + // escape "~"" to "~0" and "/" to "~1" + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } + + /// unescape tilde and slash + static void unescape(std::string& s) + { + // first transform any occurrence of the sequence '~1' to '/' + replace_substring(s, "~1", "/"); + // then transform any occurrence of the sequence '~0' to '~' + replace_substring(s, "~0", "~"); + } + + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const basic_json& value, + basic_json& result) + { + switch (value.m_type) + { + case value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + escape(element.first), + element.second, result); + } + } + break; + } + + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + */ + static basic_json unflatten(const basic_json& value) + { + if (not value.is_object()) + { + JSON_THROW(std::domain_error("only objects can be unflattened")); + } + + basic_json result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (not element.second.is_primitive()) + { + JSON_THROW(std::domain_error("values in object must be primitive")); + } + + // assign value to reference pointed to by JSON pointer; Note + // that if the JSON pointer is "" (i.e., points to the whole + // value), function get_and_create returns a reference to + // result itself. An assignment will then create a primitive + // value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + private: + /// the reference tokens + std::vector<std::string> reference_tokens {}; + }; ////////////////////////// // JSON Pointer support // @@ -12684,38 +12695,38 @@ using json = basic_json<>; // specialization of std::swap, and std::hash namespace std -{ -/*! -@brief exchanges the values of two JSON objects - -@since version 1.0.0 -*/ -template<> -inline void swap(nlohmann::json& j1, - nlohmann::json& j2) noexcept( - is_nothrow_move_constructible<nlohmann::json>::value and - is_nothrow_move_assignable<nlohmann::json>::value - ) -{ - j1.swap(j2); -} - -/// hash value for JSON objects -template<> -struct hash<nlohmann::json> { /*! - @brief return a hash value for a JSON object + @brief exchanges the values of two JSON objects @since version 1.0.0 */ - std::size_t operator()(const nlohmann::json& j) const + template<> + inline void swap(nlohmann::json& j1, + nlohmann::json& j2) noexcept( + is_nothrow_move_constructible<nlohmann::json>::value and + is_nothrow_move_assignable<nlohmann::json>::value + ) + { + j1.swap(j2); + } + + /// hash value for JSON objects + template<> + struct hash<nlohmann::json> + { + /*! + @brief return a hash value for a JSON object + + @since version 1.0.0 + */ + std::size_t operator()(const nlohmann::json& j) const { // a naive hashing via the string representation const auto& h = hash<nlohmann::json::string_t>(); return h(j.dump()); } -}; + }; } // namespace std /*! diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index f552a390..12fde56e 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -106,38 +106,356 @@ SOFTWARE. */ namespace nlohmann { +// alias templates to reduce boilerplate +template <bool B, typename T = void> +using enable_if_t = typename std::enable_if<B, T>::type; +template <typename T> +using remove_cv_t = typename std::remove_cv<T>::type; +template <typename T> +using remove_reference_t = typename std::remove_reference<T>::type; + +template <typename T> +using uncvref_t = remove_cv_t<remove_reference_t<T>>; + +template <bool If, typename Then, typename Else> +using conditional_t = typename std::conditional<If, Then, Else>::type; + +// Taken from http://stackoverflow.com/questions/26936640/how-to-implement-is-enum-class-type-trait +template <typename T> +using is_scoped_enum = + std::integral_constant<bool, not std::is_convertible<T, int>::value and + std::is_enum<T>::value>; + +template <typename T> +using is_unscoped_enum = + std::integral_constant<bool, std::is_convertible<T, int>::value and + std::is_enum<T>::value>; + +// TODO update this doc /*! @brief unnamed namespace with internal helper functions @since version 1.0.0 */ -namespace + +namespace detail { +// Very useful construct against boilerplate (more boilerplate needed than in C++17: http://en.cppreference.com/w/cpp/types/void_t) +template <typename...> struct make_void +{ + using type = void; +}; +template <typename... Ts> using void_t = typename make_void<Ts...>::type; + +// Implementation of 3 C++17 constructs: conjunction, disjunction, negation. +// This is needed to avoid evaluating all the traits in a condition +// +// For example: not std::is_same<void, T>::value and has_value_type<T>::value +// will not compile when T = void (on MSVC at least) +// Whereas conjunction<negation<std::is_same<void, T>>, has_value_type<T>>::value +// will stop evaluating if negation<...>::value == false +// +// Please note that those constructs must be used with caution, since symbols can +// become very long quickly (which can slow down compilation and cause MSVC internal compiler errors) +// Only use it when you have too (see example ahead) +template <class...> struct conjunction : std::true_type {}; +template <class B1> struct conjunction<B1> : B1 {}; +template <class B1, class... Bn> +struct conjunction<B1, Bn...> +: conditional_t<bool(B1::value), conjunction<Bn...>, B1> {}; + +template <class B> struct negation : std::integral_constant < bool, !B::value > {}; +template <class...> struct disjunction : std::false_type {}; +template <class B1> struct disjunction<B1> : B1 {}; +template <class B1, class... Bn> +struct disjunction<B1, Bn...> +: conditional_t<bool(B1::value), B1, disjunction<Bn...>> {}; + /*! @brief Helper to determine whether there's a key_type for T. - Thus helper is used to tell associative containers apart from other containers 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, overworked in version 2.0.6 */ -template<typename T> -struct has_mapped_type +#define NLOHMANN_JSON_HAS_HELPER(type) \ + template <typename T> struct has_##type { \ + private: \ + template <typename U, typename = typename U::type> \ + static int detect(U &&); \ + \ + static void detect(...); \ + \ + public: \ + static constexpr bool value = \ + std::is_integral<decltype(detect(std::declval<T>()))>::value; \ + }; + +NLOHMANN_JSON_HAS_HELPER(mapped_type) +NLOHMANN_JSON_HAS_HELPER(key_type) +NLOHMANN_JSON_HAS_HELPER(value_type) +NLOHMANN_JSON_HAS_HELPER(iterator) + +#undef NLOHMANN_JSON_HAS_HELPER + +template <bool B, class RealType, class CompatibleObjectType> +struct is_compatible_object_type_impl : std::false_type {}; + +template <class RealType, class CompatibleObjectType> +struct is_compatible_object_type_impl<true, RealType, CompatibleObjectType> +{ + static constexpr auto value = + std::is_constructible<typename RealType::key_type, + typename CompatibleObjectType::key_type>::value and + std::is_constructible<typename RealType::mapped_type, + typename CompatibleObjectType::mapped_type>::value; +}; + +template<class RealType, class CompatibleObjectType> +struct is_compatible_object_type +{ + // As noted ahead, we need to stop evaluating traits if CompatibleObjectType = void + // hence the conjunction + static auto constexpr value = is_compatible_object_type_impl < + conjunction<negation<std::is_same<void, CompatibleObjectType>>, + has_mapped_type<CompatibleObjectType>, + has_key_type<CompatibleObjectType>>::value, + RealType, CompatibleObjectType >::value; +}; + +template <bool B, class BasicJson, class CompatibleArrayType> +struct is_compatible_array_type_impl : std::false_type {}; + +template <class BasicJson, class CompatibleArrayType> +struct is_compatible_array_type_impl<true, BasicJson, CompatibleArrayType> +{ + static constexpr auto value = + not std::is_same<CompatibleArrayType, + typename BasicJson::iterator>::value and + not std::is_same<CompatibleArrayType, + typename BasicJson::const_iterator>::value and + not std::is_same<CompatibleArrayType, + typename BasicJson::reverse_iterator>::value and + not std::is_same<CompatibleArrayType, + typename BasicJson::const_reverse_iterator>::value and + not std::is_same<CompatibleArrayType, + typename BasicJson::array_t::iterator>::value and + not std::is_same<CompatibleArrayType, + typename BasicJson::array_t::const_iterator>::value; +}; + +template <class BasicJson, class CompatibleArrayType> +struct is_compatible_array_type +{ + // the check for CompatibleArrayType = void is done in is_compatible_object_type + // but we need the conjunction here as well + static auto constexpr value = is_compatible_array_type_impl < + conjunction<negation<is_compatible_object_type< + typename BasicJson::object_t, CompatibleArrayType>>, + has_value_type<CompatibleArrayType>, + has_iterator<CompatibleArrayType>>::value, + BasicJson, CompatibleArrayType >::value; +}; + +template <bool, typename, typename> +struct is_compatible_integer_type_impl : std::false_type {}; + +template <typename RealIntegerType, typename CompatibleNumberIntegerType> +struct is_compatible_integer_type_impl<true, RealIntegerType, CompatibleNumberIntegerType> +{ + using RealLimits = std::numeric_limits<RealIntegerType>; + using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>; + + static constexpr auto value = + std::is_constructible<RealIntegerType, + CompatibleNumberIntegerType>::value and + CompatibleLimits::is_integer and + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template <typename RealIntegerType, typename CompatibleNumberIntegerType> +struct is_compatible_integer_type +{ + static constexpr auto value = is_compatible_integer_type_impl < + std::is_arithmetic<CompatibleNumberIntegerType>::value, RealIntegerType, + CompatibleNumberIntegerType >::value; +}; + +template <typename RealFloat, typename CompatibleFloat> +struct is_compatible_float_type +{ + static constexpr auto value = + std::is_constructible<RealFloat, CompatibleFloat>::value and + std::is_floating_point<CompatibleFloat>::value; +}; + +template <typename T, typename BasicJson> +struct is_compatible_basic_json_type +{ + static auto constexpr value = + is_unscoped_enum<T>::value or + std::is_same<T, BasicJson>::value or + std::is_constructible<typename BasicJson::string_t, T>::value or + std::is_same<typename BasicJson::boolean_t, T>::value or + is_compatible_array_type<BasicJson, T>::value or + is_compatible_object_type<typename BasicJson::object_t, T>::value or + is_compatible_float_type<typename BasicJson::number_float_t, T>::value or + is_compatible_integer_type<typename BasicJson::number_integer_t, + T>::value or + is_compatible_integer_type<typename BasicJson::number_unsigned_t, + T>::value; +}; + +template <typename T, typename BasicJson, typename PrimitiveIterator> +struct is_basic_json_nested_class +{ + static auto constexpr value = std::is_same<T, typename BasicJson::iterator>::value or + std::is_same<T, typename BasicJson::const_iterator>::value or + std::is_same<T, typename BasicJson::reverse_iterator>::value or + std::is_same<T, typename BasicJson::const_reverse_iterator>::value or + std::is_same<T, PrimitiveIterator>::value or + std::is_same<T, typename BasicJson::json_pointer>::value; +}; + +// This trait checks if JSONSerializer<T>::from_json(json const&, udt&) exists +template <template <typename, typename> class JSONSerializer, typename Json, + typename T> +struct has_from_json { private: - template <typename U, typename = typename U::mapped_type> + // also check the return type of from_json + template <typename U, typename = enable_if_t<std::is_same<void, decltype(uncvref_t<U>::from_json( + std::declval<Json>(), std::declval<T&>()))>::value>> static int detect(U&&); static void detect(...); + public: - static constexpr bool value = - std::is_integral<decltype(detect(std::declval<T>()))>::value; + static constexpr bool value = std::is_integral<decltype( + detect(std::declval<JSONSerializer<T, void>>()))>::value; }; -} // namespace +// This trait checks if JSONSerializer<T>::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template <template <typename, typename> class JSONSerializer, typename Json, + typename T> +struct has_non_default_from_json +{ + private: + template <typename U, typename = enable_if_t<std::is_same<T, decltype(uncvref_t<U>::from_json(std::declval<Json>()))>::value>> + static int detect(U&&); + + static void detect(...); + + public: + static constexpr bool value = std::is_integral<decltype( + detect(std::declval<JSONSerializer<T, void>>()))>::value; +}; + +// This trait checks if JSONSerializer<T>::to_json exists +template <template <typename, typename> class JSONSerializer, typename Json, + typename T> +struct has_to_json +{ + private: + template <typename U, typename = decltype(uncvref_t<U>::to_json( + std::declval<Json&>(), std::declval<T>()))> + static int detect(U&&); + + static void detect(...); + + public: + static constexpr bool value = std::is_integral<decltype( + detect(std::declval<JSONSerializer<T, void>>()))>::value; +}; + +// those declarations are needed to workaround a MSVC bug related to ADL +// (taken from MSVC-Ranges implementation) +void to_json(); +void from_json(); + +struct to_json_fn +{ + // is it really useful to mark those as constexpr? + template <typename Json, typename T> + constexpr auto operator()(Json&& j, T&& val) const + noexcept(noexcept(to_json(std::forward<Json>(j), std::forward<T>(val)))) + -> decltype(to_json(std::forward<Json>(j), std::forward<T>(val)), + void()) + { + return to_json(std::forward<Json>(j), std::forward<T>(val)); + } +}; + +struct from_json_fn +{ + template <typename Json, typename T> + constexpr auto operator()(Json&& j, T& val) const + noexcept(noexcept(from_json(std::forward<Json>(j), val))) + -> decltype(from_json(std::forward<Json>(j), val), void()) + { + return from_json(std::forward<Json>(j), val); + } +}; + +/*! +@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 '.'; + } +}; + +} + +// taken from ranges-v3 +// TODO add doc +template <typename T> +struct static_const +{ + static constexpr T value{}; +}; + +template <typename T> +constexpr T static_const<T>::value; + +inline namespace +{ +constexpr auto const& to_json = static_const<detail::to_json_fn>::value; +constexpr auto const& from_json = static_const<detail::from_json_fn>::value; +} + +// default JSONSerializer template argument, doesn't care about template argument +// will use ADL for serialization +template <typename = void, typename = void> +struct adl_serializer +{ + template <typename Json, typename T> + static void from_json(Json&& j, T& val) + { + ::nlohmann::from_json(std::forward<Json>(j), val); + } + + template <typename Json, typename T> + static void to_json(Json& j, T&& val) + { + ::nlohmann::to_json(j, std::forward<T>(val)); + } +}; /*! @brief a class to store JSON values @@ -226,7 +544,8 @@ template < class NumberIntegerType = std::int64_t, class NumberUnsignedType = std::uint64_t, class NumberFloatType = double, - template<typename U> class AllocatorType = std::allocator + template<typename U> class AllocatorType = std::allocator, + template<typename T, typename SFINAE = void> class JSONSerializer = adl_serializer > class basic_json { @@ -234,7 +553,8 @@ class basic_json /// workaround type for MSVC using basic_json_t = basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, - AllocatorType>; + AllocatorType, JSONSerializer>; + class primitive_iterator_t; public: // forward declarations @@ -1215,11 +1535,11 @@ 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> - basic_json(const CompatibleObjectType& val) - : m_type(value_t::object) + template <class CompatibleObjectType, + enable_if_t<detail::is_compatible_object_type< + object_t, CompatibleObjectType>::value, + int> = 0> + basic_json(const CompatibleObjectType& val) : m_type(value_t::object) { using std::begin; using std::end; @@ -1278,16 +1598,11 @@ 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> - basic_json(const CompatibleArrayType& val) - : m_type(value_t::array) + template <class CompatibleArrayType, + enable_if_t<detail::is_compatible_array_type< + basic_json_t, CompatibleArrayType>::value, + int> = 0> + basic_json(const CompatibleArrayType& val) : m_type(value_t::array) { using std::begin; using std::end; @@ -1295,6 +1610,28 @@ class basic_json assert_invariant(); } + + // constructor chosen when: + // - JSONSerializer::to_json exists for type T + // - T is not a istream, nor convertible to basic_json (float, vectors, etc) + // is_compatible_basic_json_type == not is_user_defined_type + template < + typename T, + enable_if_t<not std::is_base_of<std::istream, uncvref_t<T>>::value and + not detail::is_basic_json_nested_class<uncvref_t<T>, basic_json_t, primitive_iterator_t>::value and + not std::is_same<uncvref_t<T>, typename basic_json_t::array_t::iterator>::value and + not std::is_same<uncvref_t<T>, typename basic_json_t::object_t::iterator>::value and + + detail::conjunction<detail::negation<detail::is_compatible_basic_json_type< + uncvref_t<T>, basic_json_t>>, + detail::has_to_json<JSONSerializer, basic_json, + uncvref_t<T>>>::value, + int> = 0 > + basic_json(T && val) + { + JSONSerializer<uncvref_t<T>>::to_json(*this, std::forward<T>(val)); + } + /*! @brief create a string (explicit) @@ -1456,7 +1793,10 @@ class basic_json @since version 1.0.0 */ - basic_json(const int val) noexcept + + // Constructor for unscoped enums (not enum classes) + template <typename T, enable_if_t<is_unscoped_enum<T>::value, int> = 0> + basic_json(T val) noexcept : m_type(value_t::number_integer), m_value(static_cast<number_integer_t>(val)) { @@ -1488,11 +1828,11 @@ class basic_json @since version 1.0.0 */ - 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> + template < + typename CompatibleNumberIntegerType, + enable_if_t<detail::is_compatible_integer_type< + number_integer_t, CompatibleNumberIntegerType>::value, + int> = 0 > basic_json(const CompatibleNumberIntegerType val) noexcept : m_type(value_t::number_integer), m_value(static_cast<number_integer_t>(val)) @@ -1546,11 +1886,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, + enable_if_t<detail::is_compatible_integer_type< + number_unsigned_t, CompatibleNumberUnsignedType>::value, + int> = 0 > basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), m_value(static_cast<number_unsigned_t>(val)) @@ -1626,9 +1966,10 @@ class basic_json @since version 1.0.0 */ - template<typename CompatibleNumberFloatType, typename = typename std::enable_if< - std::is_constructible<number_float_t, CompatibleNumberFloatType>::value and - std::is_floating_point<CompatibleNumberFloatType>::value>::type> + template <typename CompatibleNumberFloatType, + enable_if_t<detail::is_compatible_float_type< + number_float_t, CompatibleNumberFloatType>::value, + int> = 0> basic_json(const CompatibleNumberFloatType val) noexcept : basic_json(number_float_t(val)) { @@ -1788,7 +2129,7 @@ class basic_json @since version 1.0.0 */ static basic_json array(std::initializer_list<basic_json> init = - std::initializer_list<basic_json>()) + std::initializer_list<basic_json>()) { return basic_json(init, false, value_t::array); } @@ -1828,7 +2169,7 @@ class basic_json @since version 1.0.0 */ static basic_json object(std::initializer_list<basic_json> init = - std::initializer_list<basic_json>()) + std::initializer_list<basic_json>()) { return basic_json(init, false, value_t::object); } @@ -2169,7 +2510,7 @@ class basic_json std::is_nothrow_move_assignable<value_t>::value and std::is_nothrow_move_constructible<json_value>::value and std::is_nothrow_move_assignable<json_value>::value - ) + ) { // check that passed value is valid other.assert_invariant(); @@ -2182,6 +2523,18 @@ class basic_json return *this; } + // this overload is needed, since constructor for udt is explicit + template <typename T, enable_if_t<not detail::is_compatible_basic_json_type< + uncvref_t<T>, basic_json_t>::value and + detail::has_to_json<JSONSerializer, basic_json_t, uncvref_t<T>>::value>> + reference& operator=(T&& val) noexcept(std::is_nothrow_constructible<basic_json_t, uncvref_t<T>>::value and + std::is_nothrow_move_assignable<uncvref_t<T>>::value) + { + static_assert(sizeof(T) == 0 , ""); + // I'm not sure this a is good practice... + return *this = basic_json_t{std::forward<T>(val)}; + } + /*! @brief destructor @@ -2655,7 +3008,6 @@ class basic_json // value access // ////////////////// - /// 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> @@ -2681,13 +3033,16 @@ 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> - T get_impl(T* /*unused*/) const + 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 detail::has_mapped_type<T>::value, + int >::type = 0 > { if (is_array()) { @@ -2727,7 +3082,7 @@ 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> + not detail::has_mapped_type<T>::value, int>::type = 0> T get_impl(T* /*unused*/) const { if (is_array()) @@ -2956,13 +3311,46 @@ class basic_json @since version 1.0.0 */ - template<typename ValueType, typename std::enable_if< - not std::is_pointer<ValueType>::value, int>::type = 0> - ValueType get() const + template <typename ValueType, + enable_if_t<not std::is_pointer<ValueType>::value, int> = 0> + auto get() const + -> decltype(this->get_impl(static_cast<ValueType*>(nullptr))) { return get_impl(static_cast<ValueType*>(nullptr)); } + template < + typename T, + enable_if_t<detail::conjunction<detail::negation<detail::is_compatible_basic_json_type< + uncvref_t<T>, basic_json_t>>, + detail::has_from_json<JSONSerializer, basic_json_t, + uncvref_t<T>>>::value, + int> = 0 > + auto get() const -> uncvref_t<T> + { + using type = uncvref_t<T>; + static_assert(std::is_default_constructible<type>::value&& + std::is_copy_constructible<type>::value, + "user-defined types must be DefaultConstructible and " + "CopyConstructible when used with get"); + type ret; + JSONSerializer<type>::from_json(*this, ret); + return ret; + } + + // This overload is chosen for non-default constructible user-defined-types + template < + typename T, + enable_if_t<detail::conjunction<detail::negation<detail::is_compatible_basic_json_type< + uncvref_t<T>, basic_json_t>>, + detail::has_non_default_from_json<JSONSerializer, basic_json_t, + uncvref_t<T>>>::value, + short> = 0 > + T get() const + { + return JSONSerializer<T>::from_json(*this); + } + /*! @brief get a pointer value (explicit) @@ -3042,8 +3430,8 @@ class basic_json { // get the type of the PointerType (remove pointer and const) using pointee_t = typename std::remove_const<typename - std::remove_pointer<typename - std::remove_const<PointerType>::type>::type>::type; + std::remove_pointer<typename + std::remove_const<PointerType>::type>::type>::type; // make sure the type matches the allowed types static_assert( std::is_same<object_t, pointee_t>::value @@ -3070,8 +3458,8 @@ class basic_json { // get the type of the PointerType (remove pointer and const) using pointee_t = typename std::remove_const<typename - std::remove_pointer<typename - std::remove_const<PointerType>::type>::type>::type; + std::remove_pointer<typename + std::remove_const<PointerType>::type>::type>::type; // make sure the type matches the allowed types static_assert( std::is_same<object_t, pointee_t>::value @@ -4124,14 +4512,14 @@ class basic_json case value_t::object: { result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, - last.m_it.object_iterator); + last.m_it.object_iterator); break; } case value_t::array: { result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, - last.m_it.array_iterator); + last.m_it.array_iterator); break; } @@ -5348,9 +5736,9 @@ class basic_json // insert to array and return iterator iterator result(this); result.m_it.array_iterator = m_value.array->insert( - pos.m_it.array_iterator, - first.m_it.array_iterator, - last.m_it.array_iterator); + pos.m_it.array_iterator, + first.m_it.array_iterator, + last.m_it.array_iterator); return result; } @@ -5420,7 +5808,7 @@ class basic_json std::is_nothrow_move_assignable<value_t>::value and std::is_nothrow_move_constructible<json_value>::value and std::is_nothrow_move_assignable<json_value>::value - ) + ) { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); @@ -8020,13 +8408,13 @@ class basic_json o << (pretty_print ? ",\n" : ","); } o << string_t(new_indent, ' ') << "\"" - << escape_string(i->first) << "\":" - << (pretty_print ? " " : ""); - i->second.dump(o, pretty_print, indent_step, new_indent); + << escape_string(i->first) << "\":" + << (pretty_print ? " " : ""); + i->second.dump(o, pretty_print, indent_step, new_indent); } - // decrease indentation - if (pretty_print) + // decrease indentation + if (pretty_print) { new_indent -= indent_step; o << "\n"; @@ -8071,16 +8459,16 @@ class basic_json } o << string_t(new_indent, ' ') << "]"; - return; + return; } - case value_t::string: + case value_t::string: { o << string_t("\"") << escape_string(*m_value.string) << "\""; - return; + return; } - case value_t::boolean: + case value_t::boolean: { o << (m_value.boolean ? "true" : "false"); return; @@ -8154,50 +8542,126 @@ class basic_json */ class primitive_iterator_t { - public: - /// set iterator to a defined beginning - void set_begin() noexcept - { - m_it = begin_value; - } + public: - /// set iterator to a defined past the end - void set_end() noexcept - { - m_it = end_value; - } + difference_type get_value() const noexcept + { + return m_it; + } + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } - /// return whether the iterator can be dereferenced - constexpr bool is_begin() const noexcept - { - return (m_it == begin_value); - } + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } - /// return whether the iterator is at end - constexpr bool is_end() const noexcept - { - return (m_it == end_value); - } + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return (m_it == begin_value); + } - /// return reference to the value to change and compare - operator difference_type& () noexcept - { - return m_it; - } + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return (m_it == end_value); + } - /// return value to compare - constexpr operator difference_type () const noexcept - { - return m_it; - } + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } - private: - static constexpr difference_type begin_value = 0; - static constexpr difference_type end_value = begin_value + 1; + friend constexpr bool operator!=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return !(lhs == rhs); + } - /// iterator as signed integer type - difference_type m_it = std::numeric_limits<std::ptrdiff_t>::denorm_min(); - }; + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + friend constexpr bool operator<=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it <= rhs.m_it; + } + + friend constexpr bool operator>(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it > rhs.m_it; + } + + friend constexpr bool operator>=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it >= rhs.m_it; + } + + primitive_iterator_t operator+(difference_type i) + { + auto result = *this; + result += i; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + friend std::ostream& operator<<(std::ostream& os, primitive_iterator_t it) + { + return os << it.m_it; + } + + primitive_iterator_t& operator++() + { + ++m_it; + return *this; + } + + primitive_iterator_t& operator++(int) + { + m_it++; + return *this; + } + + primitive_iterator_t& operator--() + { + --m_it; + return *this; + } + + primitive_iterator_t& operator--(int) + { + m_it--; + return *this; + } + + primitive_iterator_t& operator+=(difference_type n) + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) + { + m_it -= n; + return *this; + } + + private: + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + /// iterator as signed integer type + difference_type m_it = std::numeric_limits<std::ptrdiff_t>::denorm_min(); + }; /*! @brief an iterator value @@ -8217,104 +8681,104 @@ class basic_json /// create an uninitialized internal_iterator internal_iterator() noexcept - : object_iterator(), array_iterator(), primitive_iterator() - {} + : object_iterator(), array_iterator(), primitive_iterator() + {} }; /// proxy class for the iterator_wrapper functions template<typename IteratorType> class iteration_proxy { - private: + private: /// helper class for iteration class iteration_proxy_internal { - private: + private: /// the iterator IteratorType anchor; /// an index for arrays (used to create key names) size_t array_index = 0; - public: + public: explicit iteration_proxy_internal(IteratorType it) noexcept - : anchor(it) - {} + : anchor(it) + {} - /// dereference operator (needed for range-based for) - iteration_proxy_internal& operator*() + /// dereference operator (needed for range-based for) + iteration_proxy_internal& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_internal& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// inequality operator (needed for range-based for) + bool operator!= (const iteration_proxy_internal& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + typename basic_json::string_t key() const + { + assert(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: { - return *this; + return std::to_string(array_index); } - /// increment operator (needed for range-based for) - iteration_proxy_internal& operator++() + // use key from the object + case value_t::object: { - ++anchor; - ++array_index; - - return *this; + return anchor.key(); } - /// inequality operator (needed for range-based for) - bool operator!= (const iteration_proxy_internal& o) const + // use an empty key for all primitive types + default: { - return anchor != o.anchor; + return ""; } + } + } - /// return key of the iterator - typename basic_json::string_t key() const - { - assert(anchor.m_object != nullptr); - - switch (anchor.m_object->type()) - { - // use integer array index as key - case value_t::array: - { - return std::to_string(array_index); - } - - // use key from the object - case value_t::object: - { - return anchor.key(); - } - - // use an empty key for all primitive types - default: - { - return ""; - } - } - } - - /// return value of the iterator - typename IteratorType::reference value() const - { - return anchor.value(); - } + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } }; - /// the container to iterate - typename IteratorType::reference container; + /// the container to iterate + typename IteratorType::reference container; - public: - /// construct iteration proxy from a container - explicit iteration_proxy(typename IteratorType::reference cont) - : container(cont) - {} + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) + : container(cont) + {} - /// return iterator begin (needed for range-based for) - iteration_proxy_internal begin() noexcept - { - return iteration_proxy_internal(container.begin()); - } + /// return iterator begin (needed for range-based for) + iteration_proxy_internal begin() noexcept + { + return iteration_proxy_internal(container.begin()); + } - /// return iterator end (needed for range-based for) - iteration_proxy_internal end() noexcept - { - return iteration_proxy_internal(container.end()); - } + /// return iterator end (needed for range-based for) + iteration_proxy_internal end() noexcept + { + return iteration_proxy_internal(container.end()); + } }; public: @@ -8338,7 +8802,7 @@ class basic_json @since version 1.0.0, simplified in version 2.0.9 */ template<typename U> - class iter_impl : public std::iterator<std::random_access_iterator_tag, U> + class iter_impl : public std::iterator<std::random_access_iterator_tag, U> { /// allow basic_json to access private members friend class basic_json; @@ -8348,19 +8812,19 @@ class basic_json or std::is_same<U, const basic_json>::value, "iter_impl only accepts (const) basic_json"); - public: + 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 std::conditional<std::is_const<U>::value, - typename basic_json::const_pointer, - typename basic_json::pointer>::type; + typename basic_json::const_pointer, + typename basic_json::pointer>::type; /// defines a reference to the type iterated over (value_type) using reference = typename std::conditional<std::is_const<U>::value, - typename basic_json::const_reference, - typename basic_json::reference>::type; + typename basic_json::const_reference, + typename basic_json::reference>::type; /// the category of the iterator using iterator_category = std::bidirectional_iterator_tag; @@ -8374,566 +8838,566 @@ class basic_json @post The iterator is initialized; i.e. `m_object != nullptr`. */ explicit iter_impl(pointer object) noexcept - : m_object(object) + : m_object(object) + { + assert(m_object != nullptr); + + switch (m_object->m_type) { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::object: { - case basic_json::value_t::object: - { - m_it.object_iterator = typename object_t::iterator(); - break; - } - - case basic_json::value_t::array: - { - m_it.array_iterator = typename array_t::iterator(); - break; - } - - default: - { - m_it.primitive_iterator = primitive_iterator_t(); - break; - } - } - } - - /* - 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. - */ - operator const_iterator() const - { - const_iterator ret; - - if (m_object) - { - ret.m_object = m_object; - ret.m_it = m_it; + m_it.object_iterator = typename object_t::iterator(); + break; } - return ret; - } - - /*! - @brief copy constructor - @param[in] other iterator to copy from - @note It is not checked whether @a other is initialized. - */ - iter_impl(const iter_impl& other) noexcept - : m_object(other.m_object), m_it(other.m_it) - {} - - /*! - @brief copy assignment - @param[in,out] other iterator to copy from - @note It is not checked whether @a other is initialized. - */ - 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 - std::is_nothrow_move_assignable<internal_iterator>::value - ) - { - std::swap(m_object, other.m_object); - std::swap(m_it, other.m_it); - return *this; - } - - private: - /*! - @brief set the iterator to the first value - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - void set_begin() noexcept - { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::array: { - case basic_json::value_t::object: - { - m_it.object_iterator = m_object->m_value.object->begin(); - break; - } + m_it.array_iterator = typename array_t::iterator(); + break; + } - case basic_json::value_t::array: - { - m_it.array_iterator = m_object->m_value.array->begin(); - break; - } - - case basic_json::value_t::null: - { - // set to end so begin()==end() is true: null is empty - m_it.primitive_iterator.set_end(); - break; - } - - default: - { - m_it.primitive_iterator.set_begin(); - break; - } + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; } } + } - /*! - @brief set the iterator past the last value - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - void set_end() noexcept + /* + 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. + */ + operator const_iterator() const + { + const_iterator ret; + + if (m_object) { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - m_it.object_iterator = m_object->m_value.object->end(); - break; - } - - case basic_json::value_t::array: - { - m_it.array_iterator = m_object->m_value.array->end(); - break; - } - - default: - { - m_it.primitive_iterator.set_end(); - break; - } - } + ret.m_object = m_object; + ret.m_it = m_it; } - public: - /*! - @brief return a reference to the value pointed to by the iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - reference operator*() const + return ret; + } + + /*! + @brief copy constructor + @param[in] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief copy assignment + @param[in,out] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ + 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 + std::is_nothrow_move_assignable<internal_iterator>::value + ) + { + std::swap(m_object, other.m_object); + std::swap(m_it, other.m_it); + return *this; + } + + private: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::object: { - case basic_json::value_t::object: - { - assert(m_it.object_iterator != m_object->m_value.object->end()); - return m_it.object_iterator->second; - } + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } - case basic_json::value_t::array: - { - assert(m_it.array_iterator != m_object->m_value.array->end()); - return *m_it.array_iterator; - } + case basic_json::value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } - case basic_json::value_t::null: - { + case basic_json::value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case basic_json::value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case basic_json::value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case basic_json::value_t::null: + { JSON_THROW(std::out_of_range("cannot get value")); - } + } - default: + default: + { + if (m_it.primitive_iterator.is_begin()) { - if (m_it.primitive_iterator.is_begin()) - { - return *m_object; - } + return *m_object; + } JSON_THROW(std::out_of_range("cannot get value")); - } } } + } - /*! - @brief dereference the iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - pointer operator->() const + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::object: { - case basic_json::value_t::object: - { - assert(m_it.object_iterator != m_object->m_value.object->end()); - return &(m_it.object_iterator->second); - } + assert(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } - case basic_json::value_t::array: - { - assert(m_it.array_iterator != m_object->m_value.array->end()); - return &*m_it.array_iterator; - } + case basic_json::value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } - default: + default: + { + if (m_it.primitive_iterator.is_begin()) { - if (m_it.primitive_iterator.is_begin()) - { - return m_object; - } + return m_object; + } JSON_THROW(std::out_of_range("cannot get value")); - } + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator++(int) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case basic_json::value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + default: + { + ++m_it.primitive_iterator; + break; } } - /*! - @brief post-increment (it++) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator++(int) - { - auto result = *this; - ++(*this); - return result; - } + return *this; + } - /*! - @brief pre-increment (++it) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator++() - { - assert(m_object != nullptr); + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator--(int) + { + auto result = *this; + --(*this); + return result; + } - switch (m_object->m_type) + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: { - case basic_json::value_t::object: - { - std::advance(m_it.object_iterator, 1); - break; - } - - case basic_json::value_t::array: - { - std::advance(m_it.array_iterator, 1); - break; - } - - default: - { - ++m_it.primitive_iterator; - break; - } + std::advance(m_it.object_iterator, -1); + break; } - return *this; - } - - /*! - @brief post-decrement (it--) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator--(int) - { - auto result = *this; - --(*this); - return result; - } - - /*! - @brief pre-decrement (--it) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator--() - { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::array: { - case basic_json::value_t::object: - { - std::advance(m_it.object_iterator, -1); - break; - } - - case basic_json::value_t::array: - { - std::advance(m_it.array_iterator, -1); - break; - } - - default: - { - --m_it.primitive_iterator; - break; - } + std::advance(m_it.array_iterator, -1); + break; } - return *this; + default: + { + --m_it.primitive_iterator; + break; + } } - /*! - @brief comparison: equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator==(const iter_impl& other) const + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator==(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) { - // if objects are not the same, the comparison is undefined - if (m_object != other.m_object) - { JSON_THROW(std::domain_error("cannot compare iterators of different containers")); - } - - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - return (m_it.object_iterator == other.m_it.object_iterator); - } - - case basic_json::value_t::array: - { - return (m_it.array_iterator == other.m_it.array_iterator); - } - - default: - { - return (m_it.primitive_iterator == other.m_it.primitive_iterator); - } - } } - /*! - @brief comparison: not equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator!=(const iter_impl& other) const - { - return not operator==(other); - } + assert(m_object != nullptr); - /*! - @brief comparison: smaller - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator<(const iter_impl& other) const + switch (m_object->m_type) { - // if objects are not the same, the comparison is undefined - if (m_object != other.m_object) + case basic_json::value_t::object: { + return (m_it.object_iterator == other.m_it.object_iterator); + } + + case basic_json::value_t::array: + { + return (m_it.array_iterator == other.m_it.array_iterator); + } + + default: + { + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator!=(const iter_impl& other) const + { + return not operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) + { JSON_THROW(std::domain_error("cannot compare iterators of different containers")); - } + } - assert(m_object != nullptr); + assert(m_object != nullptr); - switch (m_object->m_type) + switch (m_object->m_type) + { + case basic_json::value_t::object: { - case basic_json::value_t::object: - { JSON_THROW(std::domain_error("cannot compare order of object iterators")); - } + } - case basic_json::value_t::array: - { - return (m_it.array_iterator < other.m_it.array_iterator); - } + case basic_json::value_t::array: + { + return (m_it.array_iterator < other.m_it.array_iterator); + } - default: - { - return (m_it.primitive_iterator < other.m_it.primitive_iterator); - } + default: + { + return (m_it.primitive_iterator < other.m_it.primitive_iterator); } } + } - /*! - @brief comparison: less than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator<=(const iter_impl& other) const + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return not other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return not operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return not operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + assert(m_object != nullptr); + + switch (m_object->m_type) { - return not other.operator < (*this); - } - - /*! - @brief comparison: greater than - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator>(const iter_impl& other) const - { - return not operator<=(other); - } - - /*! - @brief comparison: greater than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator>=(const iter_impl& other) const - { - return not operator<(other); - } - - /*! - @brief add to iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator+=(difference_type i) - { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::object: { - case basic_json::value_t::object: - { JSON_THROW(std::domain_error("cannot use offsets with object iterators")); - } - - case basic_json::value_t::array: - { - std::advance(m_it.array_iterator, i); - break; - } - - default: - { - m_it.primitive_iterator += i; - break; - } } - return *this; - } - - /*! - @brief subtract from iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator-=(difference_type i) - { - return operator+=(-i); - } - - /*! - @brief add to iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator+(difference_type i) - { - auto result = *this; - result += i; - return result; - } - - /*! - @brief subtract from iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator-(difference_type i) - { - auto result = *this; - result -= i; - return result; - } - - /*! - @brief return difference - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - difference_type operator-(const iter_impl& other) const - { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: { - case basic_json::value_t::object: - { JSON_THROW(std::domain_error("cannot use offsets with object iterators")); - } + } - case basic_json::value_t::array: - { - return m_it.array_iterator - other.m_it.array_iterator; - } + case basic_json::value_t::array: + { + return m_it.array_iterator - other.m_it.array_iterator; + } - default: - { - return m_it.primitive_iterator - other.m_it.primitive_iterator; - } + default: + { + return m_it.primitive_iterator - other.m_it.primitive_iterator; } } + } - /*! - @brief access to successor - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - reference operator[](difference_type n) const + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) { - assert(m_object != nullptr); - - switch (m_object->m_type) + case basic_json::value_t::object: { - case basic_json::value_t::object: - { JSON_THROW(std::domain_error("cannot use operator[] for object iterators")); - } + } - case basic_json::value_t::array: - { - return *std::next(m_it.array_iterator, n); - } + case basic_json::value_t::array: + { + return *std::next(m_it.array_iterator, n); + } - case basic_json::value_t::null: - { + case basic_json::value_t::null: + { JSON_THROW(std::out_of_range("cannot get value")); - } + } - default: + default: + { + if (m_it.primitive_iterator.get_value() == -n) { - if (m_it.primitive_iterator == -n) - { - return *m_object; - } + return *m_object; + } JSON_THROW(std::out_of_range("cannot get value")); - } } } + } - /*! - @brief return the key of an object iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - typename object_t::key_type key() const + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + typename object_t::key_type key() const + { + assert(m_object != nullptr); + + if (m_object->is_object()) { - assert(m_object != nullptr); - - if (m_object->is_object()) - { - return m_it.object_iterator->first; - } + return m_it.object_iterator->first; + } JSON_THROW(std::domain_error("cannot use key() for non-object iterators")); - } + } - /*! - @brief return the value of an iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - reference value() const - { - return operator*(); - } + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } - private: - /// associated JSON instance - pointer m_object = nullptr; - /// the actual iterator of the associated instance - internal_iterator m_it = internal_iterator(); - }; + private: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator m_it = internal_iterator(); + }; /*! @brief a template for a reverse iterator class @@ -8953,9 +9417,9 @@ class basic_json @since version 1.0.0 */ template<typename Base> - class json_reverse_iterator : public std::reverse_iterator<Base> + class json_reverse_iterator : public std::reverse_iterator<Base> { - public: + public: /// shortcut to the reverse iterator adaptor using base_iterator = std::reverse_iterator<Base>; /// the reference type for the pointed-to element @@ -8963,89 +9427,89 @@ class basic_json /// create reverse iterator from iterator json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept - : base_iterator(it) - {} + : base_iterator(it) + {} - /// create reverse iterator from base class - json_reverse_iterator(const base_iterator& it) noexcept - : base_iterator(it) - {} + /// create reverse iterator from base class + json_reverse_iterator(const base_iterator& it) noexcept + : base_iterator(it) + {} - /// post-increment (it++) - json_reverse_iterator operator++(int) - { - return base_iterator::operator++(1); - } + /// post-increment (it++) + json_reverse_iterator operator++(int) + { + return base_iterator::operator++(1); + } - /// pre-increment (++it) - json_reverse_iterator& operator++() - { - base_iterator::operator++(); - return *this; - } + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + base_iterator::operator++(); + return *this; + } - /// post-decrement (it--) - json_reverse_iterator operator--(int) - { - return base_iterator::operator--(1); - } + /// post-decrement (it--) + json_reverse_iterator operator--(int) + { + return base_iterator::operator--(1); + } - /// pre-decrement (--it) - json_reverse_iterator& operator--() - { - base_iterator::operator--(); - return *this; - } + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + base_iterator::operator--(); + return *this; + } - /// add to iterator - json_reverse_iterator& operator+=(difference_type i) - { - base_iterator::operator+=(i); - return *this; - } + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + base_iterator::operator+=(i); + return *this; + } - /// add to iterator - json_reverse_iterator operator+(difference_type i) const - { - auto result = *this; - result += i; - return result; - } + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } - /// subtract from iterator - json_reverse_iterator operator-(difference_type i) const - { - auto result = *this; - result -= i; - return result; - } + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } - /// return difference - difference_type operator-(const json_reverse_iterator& other) const - { - return this->base() - other.base(); - } + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return this->base() - other.base(); + } - /// access to successor - reference operator[](difference_type n) const - { - return *(this->operator+(n)); - } + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } - /// return the key of an object iterator - typename object_t::key_type key() const - { - auto it = --this->base(); - return it.key(); - } + /// return the key of an object iterator + typename object_t::key_type key() const + { + auto it = --this->base(); + return it.key(); + } - /// return the value of an iterator - reference value() const - { - auto it = --this->base(); - return it.operator * (); - } - }; + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } + }; private: @@ -9062,761 +9526,761 @@ class basic_json */ class lexer { - public: + public: /// token types for the parser enum class token_type - { - uninitialized, ///< indicating the scanner is uninitialized - literal_true, ///< the `true` literal - literal_false, ///< the `false` literal - literal_null, ///< the `null` literal - value_string, ///< a string -- use get_string() for actual value - value_number, ///< a number -- use get_number() for actual value - begin_array, ///< the character for array begin `[` - begin_object, ///< the character for object begin `{` - end_array, ///< the character for array end `]` - end_object, ///< the character for object end `}` - name_separator, ///< the name separator `:` - value_separator, ///< the value separator `,` - parse_error, ///< indicating a parse error - end_of_input ///< indicating the end of the input buffer - }; + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_number, ///< a number -- use get_number() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input ///< indicating the end of the input buffer + }; - /// the char type to use in the lexer - using lexer_char_t = unsigned char; + /// the char type to use in the lexer + using lexer_char_t = unsigned char; - /// a lexer from a buffer with given length - lexer(const lexer_char_t* buff, const size_t len) noexcept - : m_content(buff) - { - assert(m_content != nullptr); - m_start = m_cursor = m_content; - m_limit = m_content + len; - } + /// a lexer from a buffer with given length + lexer(const lexer_char_t* buff, const size_t len) noexcept + : m_content(buff) + { + assert(m_content != nullptr); + m_start = m_cursor = m_content; + m_limit = m_content + len; + } - /// a lexer from an input stream - explicit lexer(std::istream& s) - : m_stream(&s), m_line_buffer() + /// a lexer from an input stream + explicit lexer(std::istream& s) + : m_stream(&s), m_line_buffer() + { + // immediately abort if stream is erroneous + if (s.fail()) { - // immediately abort if stream is erroneous - if (s.fail()) - { JSON_THROW(std::invalid_argument("stream error")); - } - - // 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 (due to pointer members) - lexer() = delete; - lexer(const lexer&) = delete; - lexer operator=(const lexer&) = delete; + // fill buffer + fill_line_buffer(); - /*! - @brief create a string from one or two Unicode code points - - There are two cases: (1) @a codepoint1 is in the Basic Multilingual - Plane (U+0000 through U+FFFF) and @a codepoint2 is 0, or (2) - @a codepoint1 and @a codepoint2 are a UTF-16 surrogate pair to - represent a code point above U+FFFF. - - @param[in] codepoint1 the code point (can be high surrogate) - @param[in] codepoint2 the code point (can be low surrogate or 0) - - @return string representation of the code point; the length of the - result string is between 1 and 4 characters. - - @throw std::out_of_range if code point is > 0x10ffff; example: `"code - points above 0x10FFFF are invalid"` - @throw std::invalid_argument if the low surrogate is invalid; example: - `""missing or wrong low surrogate""` - - @complexity Constant. - - @see <http://en.wikipedia.org/wiki/UTF-8#Sample_code> - */ - static string_t to_unicode(const std::size_t codepoint1, - const std::size_t codepoint2 = 0) + // skip UTF-8 byte-order mark + if (m_line_buffer.size() >= 3 and m_line_buffer.substr(0, 3) == "\xEF\xBB\xBF") { - // calculate the code point from the given code points - std::size_t codepoint = codepoint1; + m_line_buffer[0] = ' '; + m_line_buffer[1] = ' '; + m_line_buffer[2] = ' '; + } + } - // check if codepoint1 is a high surrogate - if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF) + // switch off unwanted functions (due to pointer members) + lexer() = delete; + lexer(const lexer&) = delete; + lexer operator=(const lexer&) = delete; + + /*! + @brief create a string from one or two Unicode code points + + There are two cases: (1) @a codepoint1 is in the Basic Multilingual + Plane (U+0000 through U+FFFF) and @a codepoint2 is 0, or (2) + @a codepoint1 and @a codepoint2 are a UTF-16 surrogate pair to + represent a code point above U+FFFF. + + @param[in] codepoint1 the code point (can be high surrogate) + @param[in] codepoint2 the code point (can be low surrogate or 0) + + @return string representation of the code point; the length of the + result string is between 1 and 4 characters. + + @throw std::out_of_range if code point is > 0x10ffff; example: `"code + points above 0x10FFFF are invalid"` + @throw std::invalid_argument if the low surrogate is invalid; example: + `""missing or wrong low surrogate""` + + @complexity Constant. + + @see <http://en.wikipedia.org/wiki/UTF-8#Sample_code> + */ + static string_t to_unicode(const std::size_t codepoint1, + const std::size_t codepoint2 = 0) + { + // calculate the code point from the given code points + std::size_t codepoint = codepoint1; + + // check if codepoint1 is a high surrogate + if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF) + { + // check if codepoint2 is a low surrogate + if (codepoint2 >= 0xDC00 and codepoint2 <= 0xDFFF) + { + codepoint = + // high surrogate occupies the most significant 22 bits + (codepoint1 << 10) + // low surrogate occupies the least significant 15 bits + + codepoint2 + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00; + } + else { - // check if codepoint2 is a low surrogate - if (codepoint2 >= 0xDC00 and codepoint2 <= 0xDFFF) - { - codepoint = - // high surrogate occupies the most significant 22 bits - (codepoint1 << 10) - // low surrogate occupies the least significant 15 bits - + codepoint2 - // there is still the 0xD800, 0xDC00 and 0x10000 noise - // in the result so we have to subtract with: - // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - - 0x35FDC00; - } - else - { JSON_THROW(std::invalid_argument("missing or wrong low surrogate")); - } } + } - string_t result; + string_t result; - if (codepoint < 0x80) - { - // 1-byte characters: 0xxxxxxx (ASCII) - result.append(1, static_cast<typename string_t::value_type>(codepoint)); - } - else if (codepoint <= 0x7ff) - { - // 2-byte characters: 110xxxxx 10xxxxxx - result.append(1, static_cast<typename string_t::value_type>(0xC0 | ((codepoint >> 6) & 0x1F))); - result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F))); - } - else if (codepoint <= 0xffff) - { - // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - result.append(1, static_cast<typename string_t::value_type>(0xE0 | ((codepoint >> 12) & 0x0F))); - result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 6) & 0x3F))); - result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F))); - } - else if (codepoint <= 0x10ffff) - { - // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - result.append(1, static_cast<typename string_t::value_type>(0xF0 | ((codepoint >> 18) & 0x07))); - result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 12) & 0x3F))); - result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 6) & 0x3F))); - result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F))); - } - else - { + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + result.append(1, static_cast<typename string_t::value_type>(codepoint)); + } + else if (codepoint <= 0x7ff) + { + // 2-byte characters: 110xxxxx 10xxxxxx + result.append(1, static_cast<typename string_t::value_type>(0xC0 | ((codepoint >> 6) & 0x1F))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F))); + } + else if (codepoint <= 0xffff) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + result.append(1, static_cast<typename string_t::value_type>(0xE0 | ((codepoint >> 12) & 0x0F))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 6) & 0x3F))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F))); + } + else if (codepoint <= 0x10ffff) + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + result.append(1, static_cast<typename string_t::value_type>(0xF0 | ((codepoint >> 18) & 0x07))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 12) & 0x3F))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 6) & 0x3F))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F))); + } + else + { JSON_THROW(std::out_of_range("code points above 0x10FFFF are invalid")); - } - - return result; } - /// return name of values of type token_type (only used for errors) - static std::string token_type_name(const token_type t) + return result; + } + + /// return name of values of type token_type (only used for errors) + static std::string token_type_name(const token_type t) + { + switch (t) { - switch (t) + case token_type::uninitialized: + return "<uninitialized>"; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case token_type::value_number: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return "<parse error>"; + case token_type::end_of_input: + return "end of input"; + default: { - case token_type::uninitialized: - return "<uninitialized>"; - case token_type::literal_true: - return "true literal"; - case token_type::literal_false: - return "false literal"; - case token_type::literal_null: - return "null literal"; - case token_type::value_string: - return "string literal"; - case token_type::value_number: - return "number literal"; - case token_type::begin_array: - return "'['"; - case token_type::begin_object: - return "'{'"; - case token_type::end_array: - return "']'"; - case token_type::end_object: - return "'}'"; - case token_type::name_separator: - return "':'"; - case token_type::value_separator: - return "','"; - case token_type::parse_error: - return "<parse error>"; - case token_type::end_of_input: - return "end of input"; - default: - { - // catch non-enum values - return "unknown token"; // LCOV_EXCL_LINE - } + // catch non-enum values + return "unknown token"; // LCOV_EXCL_LINE } } + } - /*! - This function implements a scanner for JSON. It is specified using - regular expressions that try to follow RFC 7159 as close as possible. - These regular expressions are then translated into a minimized - deterministic finite automaton (DFA) by the tool - [re2c](http://re2c.org). As a result, the translated code for this - function consists of a large block of code with `goto` jumps. + /*! + This function implements a scanner for JSON. It is specified using + regular expressions that try to follow RFC 7159 as close as possible. + These regular expressions are then translated into a minimized + deterministic finite automaton (DFA) by the tool + [re2c](http://re2c.org). As a result, the translated code for this + function consists of a large block of code with `goto` jumps. - @return the class of the next token read from the buffer + @return the class of the next token read from the buffer - @complexity Linear in the length of the input.\n + @complexity Linear in the length of the input.\n - Proposition: The loop below will always terminate for finite input.\n + Proposition: The loop below will always terminate for finite input.\n - Proof (by contradiction): Assume a finite input. To loop forever, the - loop must never hit code with a `break` statement. The only code - snippets without a `break` statement are the continue statements for - whitespace and byte-order-marks. To loop forever, the input must be an - infinite sequence of whitespace or byte-order-marks. This contradicts - the assumption of finite input, q.e.d. - */ - token_type scan() + Proof (by contradiction): Assume a finite input. To loop forever, the + loop must never hit code with a `break` statement. The only code + snippets without a `break` statement are the continue statements for + whitespace and byte-order-marks. To loop forever, the input must be an + infinite sequence of whitespace or byte-order-marks. This contradicts + the assumption of finite input, q.e.d. + */ + token_type scan() + { + while (true) { - while (true) - { - // pointer for backtracking information - m_marker = nullptr; + // pointer for backtracking information + m_marker = nullptr; - // remember the begin of the token - m_start = m_cursor; - assert(m_start != nullptr); + // remember the begin of the token + m_start = m_cursor; + assert(m_start != nullptr); - /*!re2c - re2c:define:YYCTYPE = lexer_char_t; - re2c:define:YYCURSOR = m_cursor; - re2c:define:YYLIMIT = m_limit; - re2c:define:YYMARKER = m_marker; - re2c:define:YYFILL = "fill_line_buffer(@@); // LCOV_EXCL_LINE"; - re2c:define:YYFILL:naked = 1; - re2c:yyfill:enable = 1; - re2c:indent:string = " "; - re2c:indent:top = 1; - re2c:labelprefix = "basic_json_parser_"; + /*!re2c + re2c:define:YYCTYPE = lexer_char_t; + re2c:define:YYCURSOR = m_cursor; + re2c:define:YYLIMIT = m_limit; + re2c:define:YYMARKER = m_marker; + re2c:define:YYFILL = "fill_line_buffer(@@); // LCOV_EXCL_LINE"; + re2c:define:YYFILL:naked = 1; + re2c:yyfill:enable = 1; + re2c:indent:string = " "; + re2c:indent:top = 1; + re2c:labelprefix = "basic_json_parser_"; - // ignore whitespace - ws = [ \t\n\r]+; - ws { continue; } + // ignore whitespace + ws = [ \t\n\r]+; + ws { continue; } - // structural characters - "[" { last_token_type = token_type::begin_array; break; } - "]" { last_token_type = token_type::end_array; break; } - "{" { last_token_type = token_type::begin_object; break; } - "}" { last_token_type = token_type::end_object; break; } - "," { last_token_type = token_type::value_separator; break; } - ":" { last_token_type = token_type::name_separator; break; } + // structural characters + "[" { last_token_type = token_type::begin_array; break; } + "]" { last_token_type = token_type::end_array; break; } + "{" { last_token_type = token_type::begin_object; break; } + "}" { last_token_type = token_type::end_object; break; } + "," { last_token_type = token_type::value_separator; break; } + ":" { last_token_type = token_type::name_separator; break; } - // literal names - "null" { last_token_type = token_type::literal_null; break; } - "true" { last_token_type = token_type::literal_true; break; } - "false" { last_token_type = token_type::literal_false; break; } + // literal names + "null" { last_token_type = token_type::literal_null; break; } + "true" { last_token_type = token_type::literal_true; break; } + "false" { last_token_type = token_type::literal_false; break; } - // number - decimal_point = "."; - digit = [0-9]; - digit_1_9 = [1-9]; - e = "e" | "E"; - minus = "-"; - plus = "+"; - zero = "0"; - exp = e (minus | plus)? digit+; - frac = decimal_point digit+; - int = (zero | digit_1_9 digit*); - number = minus? int frac? exp?; - number { last_token_type = token_type::value_number; break; } + // number + decimal_point = "."; + digit = [0-9]; + digit_1_9 = [1-9]; + e = "e" | "E"; + minus = "-"; + plus = "+"; + zero = "0"; + exp = e (minus | plus)? digit+; + frac = decimal_point digit+; + int = (zero | digit_1_9 digit*); + number = minus? int frac? exp?; + number { last_token_type = token_type::value_number; break; } - // string - quotation_mark = "\""; - escape = "\\"; - unescaped = [^"\\\x00-\x1f]; - single_escaped = "\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t"; - unicode_escaped = "u" [0-9a-fA-F]{4}; - escaped = escape (single_escaped | unicode_escaped); - char = unescaped | escaped; - string = quotation_mark char* quotation_mark; - string { last_token_type = token_type::value_string; break; } + // string + quotation_mark = "\""; + escape = "\\"; + unescaped = [^"\\\x00-\x1f]; + single_escaped = "\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t"; + unicode_escaped = "u" [0-9a-fA-F]{4}; + escaped = escape (single_escaped | unicode_escaped); + char = unescaped | escaped; + string = quotation_mark char* quotation_mark; + string { last_token_type = token_type::value_string; break; } - // end of file - "\x00" { last_token_type = token_type::end_of_input; break; } + // end of file + "\x00" { last_token_type = token_type::end_of_input; break; } - // anything else is an error - * { last_token_type = token_type::parse_error; break; } - */ - } - - return last_token_type; + // anything else is an error + * { last_token_type = token_type::parse_error; break; } + */ } - /*! - @brief append data from the stream to the line buffer + return last_token_type; + } - 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. + /*! + @brief append data from the stream to the line buffer - 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. + 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 an input stream, this function reads the next - line of the input. + 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. - @pre - p p p p p p u u u u u x . . . . . . - ^ ^ ^ ^ - m_content m_start | m_limit - m_cursor + If the lexer reads from an input stream, this function reads the next + line of the input. - @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())); + @pre + p p p p p p u u u u u x . . . . . . + ^ ^ ^ ^ + m_content m_start | m_limit + m_cursor - // 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()); + @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())); - // 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); + // 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()); - // number of processed characters (p) + // 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 auto 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; + // 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()) + // no stream is used or end of file is reached + if (m_stream == nullptr or m_stream->eof()) + { + // 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); + + // 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_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); + 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'); - // 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) + // add line with newline symbol to the line buffer + m_line_buffer += m_line_buffer_tmp; + m_line_buffer.push_back('\n'); + } + + // 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_line_buffer.size(); + } + + /// return string representation of last read token + string_t get_token_string() const + { + assert(m_start != nullptr); + return string_t(reinterpret_cast<typename string_t::const_pointer>(m_start), + static_cast<size_t>(m_cursor - m_start)); + } + + /*! + @brief return string value for string tokens + + The function iterates the characters between the opening and closing + quotes of the string value. The complete string is the range + [m_start,m_cursor). Consequently, we iterate from m_start+1 to + m_cursor-1. + + We differentiate two cases: + + 1. Escaped characters. In this case, a new character is constructed + according to the nature of the escape. Some escapes create new + characters (e.g., `"\\n"` is replaced by `"\n"`), some are copied + as is (e.g., `"\\\\"`). Furthermore, Unicode escapes of the shape + `"\\uxxxx"` need special care. In this case, to_unicode takes care + of the construction of the values. + 2. Unescaped characters are copied as is. + + @pre `m_cursor - m_start >= 2`, meaning the length of the last token + is at least 2 bytes which is trivially true for any string (which + consists of at least two quotes). + + " c1 c2 c3 ... " + ^ ^ + m_start m_cursor + + @complexity Linear in the length of the string.\n + + Lemma: The loop body will always terminate.\n + + Proof (by contradiction): Assume the loop body does not terminate. As + the loop body does not contain another loop, one of the called + functions must never return. The called functions are `std::strtoul` + and to_unicode. Neither function can loop forever, so the loop body + will never loop forever which contradicts the assumption that the loop + body does not terminate, q.e.d.\n + + Lemma: The loop condition for the for loop is eventually false.\n + + Proof (by contradiction): Assume the loop does not terminate. Due to + the above lemma, this can only be due to a tautological loop + condition; that is, the loop condition i < m_cursor - 1 must always be + true. Let x be the change of i for any loop iteration. Then + m_start + 1 + x < m_cursor - 1 must hold to loop indefinitely. This + can be rephrased to m_cursor - m_start - 2 > x. With the + precondition, we x <= 0, meaning that the loop condition holds + indefinitly if i is always decreased. However, observe that the value + of i is strictly increasing with each iteration, as it is incremented + by 1 in the iteration expression and never decremented inside the loop + body. Hence, the loop condition will eventually be false which + contradicts the assumption that the loop condition is a tautology, + q.e.d. + + @return string value of current token without opening and closing + quotes + @throw std::out_of_range if to_unicode fails + */ + string_t get_string() const + { + assert(m_cursor - m_start >= 2); + + string_t result; + result.reserve(static_cast<size_t>(m_cursor - m_start - 2)); + + // iterate the result between the quotes + for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++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++) { - m_line_buffer.append(n - 1, '\x01'); + result.push_back(static_cast<typename string_t::value_type>(*k)); } + i = e - 1; // -1 because of ++i } 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'); + // processing escaped character + // read next character + ++i; - // add line with newline symbol to the line buffer - m_line_buffer += m_line_buffer_tmp; - m_line_buffer.push_back('\n'); - } - - // 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_line_buffer.size(); - } - - /// return string representation of last read token - string_t get_token_string() const - { - assert(m_start != nullptr); - return string_t(reinterpret_cast<typename string_t::const_pointer>(m_start), - static_cast<size_t>(m_cursor - m_start)); - } - - /*! - @brief return string value for string tokens - - The function iterates the characters between the opening and closing - quotes of the string value. The complete string is the range - [m_start,m_cursor). Consequently, we iterate from m_start+1 to - m_cursor-1. - - We differentiate two cases: - - 1. Escaped characters. In this case, a new character is constructed - according to the nature of the escape. Some escapes create new - characters (e.g., `"\\n"` is replaced by `"\n"`), some are copied - as is (e.g., `"\\\\"`). Furthermore, Unicode escapes of the shape - `"\\uxxxx"` need special care. In this case, to_unicode takes care - of the construction of the values. - 2. Unescaped characters are copied as is. - - @pre `m_cursor - m_start >= 2`, meaning the length of the last token - is at least 2 bytes which is trivially true for any string (which - consists of at least two quotes). - - " c1 c2 c3 ... " - ^ ^ - m_start m_cursor - - @complexity Linear in the length of the string.\n - - Lemma: The loop body will always terminate.\n - - Proof (by contradiction): Assume the loop body does not terminate. As - the loop body does not contain another loop, one of the called - functions must never return. The called functions are `std::strtoul` - and to_unicode. Neither function can loop forever, so the loop body - will never loop forever which contradicts the assumption that the loop - body does not terminate, q.e.d.\n - - Lemma: The loop condition for the for loop is eventually false.\n - - Proof (by contradiction): Assume the loop does not terminate. Due to - the above lemma, this can only be due to a tautological loop - condition; that is, the loop condition i < m_cursor - 1 must always be - true. Let x be the change of i for any loop iteration. Then - m_start + 1 + x < m_cursor - 1 must hold to loop indefinitely. This - can be rephrased to m_cursor - m_start - 2 > x. With the - precondition, we x <= 0, meaning that the loop condition holds - indefinitly if i is always decreased. However, observe that the value - of i is strictly increasing with each iteration, as it is incremented - by 1 in the iteration expression and never decremented inside the loop - body. Hence, the loop condition will eventually be false which - contradicts the assumption that the loop condition is a tautology, - q.e.d. - - @return string value of current token without opening and closing - quotes - @throw std::out_of_range if to_unicode fails - */ - string_t get_string() const - { - assert(m_cursor - m_start >= 2); - - string_t result; - result.reserve(static_cast<size_t>(m_cursor - m_start - 2)); - - // iterate the result between the quotes - for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++i) - { - // find next escape character - auto e = std::find(i, m_cursor - 1, '\\'); - if (e != i) + switch (*i) { - // see https://github.com/nlohmann/json/issues/365#issuecomment-262874705 - for (auto k = i; k < e; k++) + // the default escapes + case 't': { - result.push_back(static_cast<typename string_t::value_type>(*k)); + result += "\t"; + break; } - i = e - 1; // -1 because of ++i - } - else - { - // processing escaped character - // read next character - ++i; - - switch (*i) + case 'b': { - // the default escapes - case 't': - { - result += "\t"; - break; - } - case 'b': - { - result += "\b"; - break; - } - case 'f': - { - result += "\f"; - break; - } - case 'n': - { - result += "\n"; - break; - } - case 'r': - { - result += "\r"; - break; - } - case '\\': - { - result += "\\"; - break; - } - case '/': - { - result += "/"; - break; - } - case '"': - { - result += "\""; - break; - } + result += "\b"; + break; + } + case 'f': + { + result += "\f"; + break; + } + case 'n': + { + result += "\n"; + break; + } + case 'r': + { + result += "\r"; + break; + } + case '\\': + { + result += "\\"; + break; + } + case '/': + { + result += "/"; + break; + } + case '"': + { + result += "\""; + break; + } - // unicode - case 'u': - { - // get code xxxx from uxxxx - auto codepoint = std::strtoul(std::string(reinterpret_cast<typename string_t::const_pointer>(i + 1), - 4).c_str(), nullptr, 16); + // unicode + case 'u': + { + // get code xxxx from uxxxx + auto codepoint = std::strtoul(std::string(reinterpret_cast<typename string_t::const_pointer>(i + 1), + 4).c_str(), nullptr, 16); - // check if codepoint is a high surrogate - if (codepoint >= 0xD800 and codepoint <= 0xDBFF) + // check if codepoint is a high surrogate + if (codepoint >= 0xD800 and codepoint <= 0xDBFF) + { + // make sure there is a subsequent unicode + if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u') { - // make sure there is a subsequent unicode - if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u') - { JSON_THROW(std::invalid_argument("missing low surrogate")); - } + } - // get code yyyy from uxxxx\uyyyy - auto codepoint2 = std::strtoul(std::string(reinterpret_cast<typename string_t::const_pointer> - (i + 7), 4).c_str(), nullptr, 16); - result += to_unicode(codepoint, codepoint2); - // skip the next 10 characters (xxxx\uyyyy) - i += 10; - } - else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF) - { - // we found a lone low surrogate - JSON_THROW(std::invalid_argument("missing high surrogate")); - } - else - { - // add unicode character(s) - result += to_unicode(codepoint); - // skip the next four characters (xxxx) - i += 4; - } - break; + // get code yyyy from uxxxx\uyyyy + auto codepoint2 = std::strtoul(std::string(reinterpret_cast<typename string_t::const_pointer> + (i + 7), 4).c_str(), nullptr, 16); + result += to_unicode(codepoint, codepoint2); + // skip the next 10 characters (xxxx\uyyyy) + i += 10; } + else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF) + { + // we found a lone low surrogate + JSON_THROW(std::invalid_argument("missing high surrogate")); + } + else + { + // add unicode character(s) + result += to_unicode(codepoint); + // skip the next four characters (xxxx) + i += 4; + } + break; } } } - - return result; } - /*! - @brief parse floating point number + return result; + } - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function based on the type - supplied via the first parameter. Set this to @a - static_cast<number_float_t*>(nullptr). + /*! + @brief parse floating point number - @param[in,out] endptr recieves a pointer to the first character after - the number + This function (and its overloads) serves to select the most approprate + standard floating point number parsing function based on the type + supplied via the first parameter. Set this to @a + static_cast<number_float_t*>(nullptr). - @return the floating point number - */ - long double str_to_float_t(long double* /* type */, char** endptr) const + @param[in,out] endptr recieves a pointer to the first character after + the number + + @return the floating point number + */ + long double str_to_float_t(long double* /* type */, char** endptr) const + { + return std::strtold(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); + } + + /*! + @brief parse floating point number + + This function (and its overloads) serves to select the most approprate + standard floating point number parsing function based on the type + supplied via the first parameter. Set this to @a + static_cast<number_float_t*>(nullptr). + + @param[in,out] endptr recieves a pointer to the first character after + the number + + @return the floating point number + */ + double str_to_float_t(double* /* type */, char** endptr) const + { + return std::strtod(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); + } + + /*! + @brief parse floating point number + + This function (and its overloads) serves to select the most approprate + standard floating point number parsing function based on the type + supplied via the first parameter. Set this to @a + static_cast<number_float_t*>(nullptr). + + @param[in,out] endptr recieves a pointer to the first character after + the number + + @return the floating point number + */ + float str_to_float_t(float* /* type */, char** endptr) const + { + return std::strtof(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); + } + + /*! + @brief return number value for number tokens + + This function translates the last token into the most appropriate + number type (either integer, unsigned integer or floating point), + which is passed back to the caller via the result parameter. + + This function parses the integer component up to the radix point or + exponent while collecting information about the 'floating point + representation', which it stores in the result parameter. If there is + no radix point or exponent, and the number can fit into a @ref + number_integer_t or @ref number_unsigned_t then it sets the result + parameter accordingly. + + If the number is a floating point number the number is then parsed + using @a std:strtod (or @a std:strtof or @a std::strtold). + + @param[out] result @ref basic_json object to receive the number, or + NAN if the conversion read past the current token. The latter case + needs to be treated by the caller function. + */ + void get_number(basic_json& result) const + { + assert(m_start != nullptr); + + const lexer::lexer_char_t* curptr = m_start; + + // accumulate the integer conversion result (unsigned for now) + number_unsigned_t value = 0; + + // maximum absolute value of the relevant integer type + number_unsigned_t max; + + // temporarily store the type to avoid unecessary bitfield access + value_t type; + + // look for sign + if (*curptr == '-') { - return std::strtold(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); + type = value_t::number_integer; + max = static_cast<uint64_t>((std::numeric_limits<number_integer_t>::max)()) + 1; + curptr++; + } + else + { + type = value_t::number_unsigned; + max = static_cast<uint64_t>((std::numeric_limits<number_unsigned_t>::max)()); } - /*! - @brief parse floating point number - - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function based on the type - supplied via the first parameter. Set this to @a - static_cast<number_float_t*>(nullptr). - - @param[in,out] endptr recieves a pointer to the first character after - the number - - @return the floating point number - */ - double str_to_float_t(double* /* type */, char** endptr) const + // count the significant figures + for (; curptr < m_cursor; curptr++) { - return std::strtod(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); - } - - /*! - @brief parse floating point number - - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function based on the type - supplied via the first parameter. Set this to @a - static_cast<number_float_t*>(nullptr). - - @param[in,out] endptr recieves a pointer to the first character after - the number - - @return the floating point number - */ - float str_to_float_t(float* /* type */, char** endptr) const - { - return std::strtof(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); - } - - /*! - @brief return number value for number tokens - - This function translates the last token into the most appropriate - number type (either integer, unsigned integer or floating point), - which is passed back to the caller via the result parameter. - - This function parses the integer component up to the radix point or - exponent while collecting information about the 'floating point - representation', which it stores in the result parameter. If there is - no radix point or exponent, and the number can fit into a @ref - number_integer_t or @ref number_unsigned_t then it sets the result - parameter accordingly. - - If the number is a floating point number the number is then parsed - using @a std:strtod (or @a std:strtof or @a std::strtold). - - @param[out] result @ref basic_json object to receive the number, or - NAN if the conversion read past the current token. The latter case - needs to be treated by the caller function. - */ - void get_number(basic_json& result) const - { - assert(m_start != nullptr); - - const lexer::lexer_char_t* curptr = m_start; - - // accumulate the integer conversion result (unsigned for now) - number_unsigned_t value = 0; - - // maximum absolute value of the relevant integer type - number_unsigned_t max; - - // temporarily store the type to avoid unecessary bitfield access - value_t type; - - // look for sign - if (*curptr == '-') + // quickly skip tests if a digit + if (*curptr < '0' || *curptr > '9') { - type = value_t::number_integer; - max = static_cast<uint64_t>((std::numeric_limits<number_integer_t>::max)()) + 1; - curptr++; - } - else - { - type = value_t::number_unsigned; - max = static_cast<uint64_t>((std::numeric_limits<number_unsigned_t>::max)()); - } - - // count the significant figures - for (; curptr < m_cursor; curptr++) - { - // quickly skip tests if a digit - if (*curptr < '0' || *curptr > '9') + if (*curptr == '.') { - if (*curptr == '.') - { - // don't count '.' but change to float - type = value_t::number_float; - continue; - } - // assume exponent (if not then will fail parse): change to - // float, stop counting and record exponent details + // don't count '.' but change to float type = value_t::number_float; - break; - } - - // skip if definitely not an integer - if (type != value_t::number_float) - { - auto digit = static_cast<number_unsigned_t>(*curptr - '0'); - - // 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 - value = value * 10 + digit; - } + continue; } + // assume exponent (if not then will fail parse): change to + // float, stop counting and record exponent details + type = value_t::number_float; + break; } - // save the value (if not a float) - if (type == value_t::number_unsigned) + // skip if definitely not an integer + if (type != value_t::number_float) { - result.m_value.number_unsigned = value; - } - else if (type == value_t::number_integer) - { - // invariant: if we parsed a '-', the absolute value is between - // 0 (we allow -0) and max == -INT64_MIN - assert(value >= 0); - assert(value <= max); + auto digit = static_cast<number_unsigned_t>(*curptr - '0'); - if (value == max) + // overflow if value * 10 + digit > max, move terms around + // to avoid overflow in intermediate values + if (value > (max - digit) / 10) { - // 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); + // overflow + type = value_t::number_float; } else { - // all other values can be negated safely - result.m_value.number_integer = -static_cast<number_integer_t>(value); + // no overflow + value = value * 10 + digit; } } + } + + // save the value (if not a float) + if (type == value_t::number_unsigned) + { + result.m_value.number_unsigned = value; + } + else if (type == value_t::number_integer) + { + // 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 { - // parse with strtod + // 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), nullptr); - // 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(); - } + // 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 - result.m_type = type; } - private: - /// optional input stream - std::istream* m_stream = nullptr; - /// 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 - const lexer_char_t* m_start = nullptr; - /// pointer for backtracking information - const lexer_char_t* m_marker = nullptr; - /// pointer to the current symbol - const lexer_char_t* m_cursor = nullptr; - /// pointer to the end of the buffer - const lexer_char_t* m_limit = nullptr; - /// the last token type - token_type last_token_type = token_type::end_of_input; - }; + // save the type + result.m_type = type; + } + + private: + /// optional input stream + std::istream* m_stream = nullptr; + /// 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 + const lexer_char_t* m_start = nullptr; + /// pointer for backtracking information + const lexer_char_t* m_marker = nullptr; + /// pointer to the current symbol + const lexer_char_t* m_cursor = nullptr; + /// pointer to the end of the buffer + const lexer_char_t* m_limit = nullptr; + /// the last token type + token_type last_token_type = token_type::end_of_input; + }; /*! @brief syntax analysis @@ -9825,282 +10289,282 @@ class basic_json */ class parser { - public: + public: /// 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)) - {} + : 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) - : callback(cb), m_lexer(is) - {} + /// a parser reading from an input stream + 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))) - {} + /// 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() + /// public parser interface + basic_json parse() + { + // read first token + get_token(); + + basic_json result = parse_internal(true); + result.assert_invariant(); + + expect(lexer::token_type::end_of_input); + + // return parser result and replace it with null in case the + // top-level value was discarded by the callback function + return result.is_discarded() ? basic_json() : std::move(result); + } + + private: + /// the actual parser + basic_json parse_internal(bool keep) + { + auto result = basic_json(value_t::discarded); + + switch (last_token) { - // read first token - get_token(); - - basic_json result = parse_internal(true); - result.assert_invariant(); - - expect(lexer::token_type::end_of_input); - - // return parser result and replace it with null in case the - // top-level value was discarded by the callback function - return result.is_discarded() ? basic_json() : std::move(result); - } - - private: - /// the actual parser - basic_json parse_internal(bool keep) - { - auto result = basic_json(value_t::discarded); - - switch (last_token) + case lexer::token_type::begin_object: { - case lexer::token_type::begin_object: + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) { - 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; - result.m_value = value_t::object; - } + // explicitly set result to object to cope with {} + result.m_type = value_t::object; + result.m_value = value_t::object; + } - // read next token - get_token(); + // read next token + get_token(); - // closing } -> we are done - if (last_token == lexer::token_type::end_object) - { - get_token(); - if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) - { - result = basic_json(value_t::discarded); - } - return result; - } - - // no comma is expected here - unexpect(lexer::token_type::value_separator); - - // otherwise: parse key-value pairs - do - { - // ugly, but could be fixed with loop reorganization - if (last_token == lexer::token_type::value_separator) - { - get_token(); - } - - // store key - expect(lexer::token_type::value_string); - const auto key = m_lexer.get_string(); - - bool keep_tag = false; - if (keep) - { - if (callback) - { - basic_json k(key); - keep_tag = callback(depth, parse_event_t::key, k); - } - else - { - keep_tag = true; - } - } - - // parse separator (:) - get_token(); - expect(lexer::token_type::name_separator); - - // parse and add value - get_token(); - auto value = parse_internal(keep); - if (keep and keep_tag and not value.is_discarded()) - { - result[key] = std::move(value); - } - } - while (last_token == lexer::token_type::value_separator); - - // closing } - expect(lexer::token_type::end_object); + // closing } -> we are done + if (last_token == lexer::token_type::end_object) + { get_token(); if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) { result = basic_json(value_t::discarded); } - return result; } - case lexer::token_type::begin_array: + // no comma is expected here + unexpect(lexer::token_type::value_separator); + + // otherwise: parse key-value pairs + do { - 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; - result.m_value = value_t::array; - } - - // read next token - get_token(); - - // closing ] -> we are done - if (last_token == lexer::token_type::end_array) + // ugly, but could be fixed with loop reorganization + if (last_token == lexer::token_type::value_separator) { get_token(); - if (callback and not callback(--depth, parse_event_t::array_end, result)) - { - result = basic_json(value_t::discarded); - } - return result; } - // no comma is expected here - unexpect(lexer::token_type::value_separator); + // store key + expect(lexer::token_type::value_string); + const auto key = m_lexer.get_string(); - // otherwise: parse values - do + bool keep_tag = false; + if (keep) { - // ugly, but could be fixed with loop reorganization - if (last_token == lexer::token_type::value_separator) + if (callback) { - get_token(); + basic_json k(key); + keep_tag = callback(depth, parse_event_t::key, k); } - - // parse value - auto value = parse_internal(keep); - if (keep and not value.is_discarded()) + else { - result.push_back(std::move(value)); + keep_tag = true; } } - while (last_token == lexer::token_type::value_separator); - // closing ] - expect(lexer::token_type::end_array); + // parse separator (:) get_token(); - if (keep and callback and not callback(--depth, parse_event_t::array_end, result)) + expect(lexer::token_type::name_separator); + + // parse and add value + get_token(); + auto value = parse_internal(keep); + if (keep and keep_tag and not value.is_discarded()) + { + result[key] = std::move(value); + } + } + while (last_token == lexer::token_type::value_separator); + + // closing } + expect(lexer::token_type::end_object); + get_token(); + if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) + { + result = basic_json(value_t::discarded); + } + + return result; + } + + case lexer::token_type::begin_array: + { + 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; + result.m_value = value_t::array; + } + + // read next token + get_token(); + + // closing ] -> we are done + if (last_token == lexer::token_type::end_array) + { + get_token(); + if (callback and not callback(--depth, parse_event_t::array_end, result)) { result = basic_json(value_t::discarded); } - return result; } - case lexer::token_type::literal_null: + // no comma is expected here + unexpect(lexer::token_type::value_separator); + + // otherwise: parse values + do { - get_token(); - result.m_type = value_t::null; - break; + // ugly, but could be fixed with loop reorganization + if (last_token == lexer::token_type::value_separator) + { + get_token(); + } + + // parse value + auto value = parse_internal(keep); + if (keep and not value.is_discarded()) + { + result.push_back(std::move(value)); + } + } + while (last_token == lexer::token_type::value_separator); + + // closing ] + expect(lexer::token_type::end_array); + get_token(); + if (keep and callback and not callback(--depth, parse_event_t::array_end, result)) + { + result = basic_json(value_t::discarded); } - case lexer::token_type::value_string: - { - const auto s = m_lexer.get_string(); - get_token(); - result = basic_json(s); - break; - } - - case lexer::token_type::literal_true: - { - get_token(); - result.m_type = value_t::boolean; - result.m_value = true; - break; - } - - case lexer::token_type::literal_false: - { - get_token(); - result.m_type = value_t::boolean; - result.m_value = false; - break; - } - - case lexer::token_type::value_number: - { - m_lexer.get_number(result); - get_token(); - break; - } - - default: - { - // the last token was unexpected - unexpect(last_token); - } + return result; } - if (keep and callback and not callback(depth, parse_event_t::value, result)) + case lexer::token_type::literal_null: { - result = basic_json(value_t::discarded); + get_token(); + result.m_type = value_t::null; + break; } - return result; - } - /// get next token from lexer - typename lexer::token_type get_token() - { - last_token = m_lexer.scan(); - return last_token; - } - - void expect(typename lexer::token_type t) const - { - if (t != last_token) + case lexer::token_type::value_string: { - std::string error_msg = "parse error - unexpected "; - error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + - "'") : - lexer::token_type_name(last_token)); - error_msg += "; expected " + lexer::token_type_name(t); + const auto s = m_lexer.get_string(); + get_token(); + result = basic_json(s); + break; + } + + case lexer::token_type::literal_true: + { + get_token(); + result.m_type = value_t::boolean; + result.m_value = true; + break; + } + + case lexer::token_type::literal_false: + { + get_token(); + result.m_type = value_t::boolean; + result.m_value = false; + break; + } + + case lexer::token_type::value_number: + { + m_lexer.get_number(result); + get_token(); + break; + } + + default: + { + // the last token was unexpected + unexpect(last_token); + } + } + + if (keep and callback and not callback(depth, parse_event_t::value, result)) + { + result = basic_json(value_t::discarded); + } + return result; + } + + /// get next token from lexer + typename lexer::token_type get_token() + { + last_token = m_lexer.scan(); + return last_token; + } + + void expect(typename lexer::token_type t) const + { + if (t != last_token) + { + std::string error_msg = "parse error - unexpected "; + error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + + "'") : + lexer::token_type_name(last_token)); + error_msg += "; expected " + lexer::token_type_name(t); JSON_THROW(std::invalid_argument(error_msg)); - } } + } - void unexpect(typename lexer::token_type t) const + void unexpect(typename lexer::token_type t) const + { + if (t == last_token) { - if (t == last_token) - { - std::string error_msg = "parse error - unexpected "; - error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + - "'") : - lexer::token_type_name(last_token)); + std::string error_msg = "parse error - unexpected "; + error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + + "'") : + lexer::token_type_name(last_token)); JSON_THROW(std::invalid_argument(error_msg)); - } } + } - private: - /// current level of recursion - int depth = 0; - /// callback function - const parser_callback_t callback = nullptr; - /// the type of the last read token - typename lexer::token_type last_token = lexer::token_type::uninitialized; - /// the lexer - lexer m_lexer; - }; + private: + /// current level of recursion + int depth = 0; + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + typename lexer::token_type last_token = lexer::token_type::uninitialized; + /// the lexer + lexer m_lexer; + }; public: /*! @@ -10119,7 +10583,7 @@ class basic_json /// allow basic_json to access private members friend class basic_json; - public: + public: /*! @brief create JSON pointer @@ -10143,562 +10607,562 @@ class basic_json @since version 2.0.0 */ explicit json_pointer(const std::string& s = "") - : reference_tokens(split(s)) - {} + : reference_tokens(split(s)) + {} - /*! - @brief return a string representation of the JSON pointer + /*! + @brief return a string representation of the JSON pointer - @invariant For each JSON pointer `ptr`, it holds: - @code {.cpp} - ptr == json_pointer(ptr.to_string()); - @endcode + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode - @return a string representation of the JSON pointer + @return a string representation of the JSON pointer - @liveexample{The example shows the result of `to_string`., - json_pointer__to_string} + @liveexample{The example shows the result of `to_string`., + json_pointer__to_string} - @since version 2.0.0 - */ - std::string to_string() const noexcept + @since version 2.0.0 + */ + std::string to_string() const noexcept + { + return std::accumulate(reference_tokens.begin(), + reference_tokens.end(), std::string{}, + [](const std::string & a, const std::string & b) { - return std::accumulate(reference_tokens.begin(), - reference_tokens.end(), std::string{}, - [](const std::string & a, const std::string & b) - { - return a + "/" + escape(b); - }); - } + return a + "/" + escape(b); + }); + } - /// @copydoc to_string() - operator std::string() const - { - return to_string(); - } + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } - private: - /// remove and return last reference pointer - std::string pop_back() + private: + /// remove and return last reference pointer + std::string pop_back() + { + if (is_root()) { - if (is_root()) - { JSON_THROW(std::domain_error("JSON pointer has no parent")); - } - - auto last = reference_tokens.back(); - reference_tokens.pop_back(); - return last; } - /// return whether pointer points to the root document - bool is_root() const - { - return reference_tokens.empty(); - } + auto last = reference_tokens.back(); + reference_tokens.pop_back(); + return last; + } - json_pointer top() const + /// return whether pointer points to the root document + bool is_root() const + { + return reference_tokens.empty(); + } + + json_pointer top() const + { + if (is_root()) { - if (is_root()) - { JSON_THROW(std::domain_error("JSON pointer has no parent")); - } - - json_pointer result = *this; - result.reference_tokens = {reference_tokens[0]}; - return result; } - /*! - @brief create and return a reference to the pointed to value + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } - @complexity Linear in the number of reference tokens. - */ - reference get_and_create(reference j) const + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + */ + reference get_and_create(reference j) const + { + pointer result = &j; + + // in case no reference tokens exist, return a reference to the + // JSON value j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) { - pointer result = &j; - - // in case no reference tokens exist, return a reference to the - // JSON value j which will be overwritten by a primitive value - for (const auto& reference_token : reference_tokens) + switch (result->m_type) { - switch (result->m_type) + case value_t::null: { - case value_t::null: + if (reference_token == "0") { - if (reference_token == "0") - { - // start a new array if reference token is 0 - result = &result->operator[](0); - } - else - { - // start a new object otherwise - result = &result->operator[](reference_token); - } - break; + // start a new array if reference token is 0 + result = &result->operator[](0); } - - case value_t::object: + else { - // create an entry in the object + // start a new object otherwise result = &result->operator[](reference_token); - break; - } - - case value_t::array: - { - // create an entry in the array - result = &result->operator[](static_cast<size_type>(std::stoi(reference_token))); - break; - } - - /* - The following code is only reached if there exists a - reference token _and_ the current value is primitive. In - this case, we have an error situation, because primitive - values may only occur as single value; that is, with an - empty list of reference tokens. - */ - default: - { - JSON_THROW(std::domain_error("invalid value to unflatten")); - } - } - } - - return *result; - } - - /*! - @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 - - @complexity Linear in the length of the JSON pointer. - - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number - */ - reference get_unchecked(pointer ptr) const - { - 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: - { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } - - case value_t::array: - { - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - JSON_THROW(std::domain_error("array index must not begin with '0'")); - } - - if (reference_token == "-") - { - // explicityly treat "-" as index beyond the end - ptr = &ptr->operator[](ptr->m_value.array->size()); - } - else - { - // convert array index to number; unchecked access - ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token))); - } - break; - } - - default: - { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); - } - } - } - - return *ptr; - } - - reference get_checked(pointer ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->m_type) - { - case value_t::object: - { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; - } - - case value_t::array: - { - if (reference_token == "-") - { - // "-" always fails the range check - throw std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range"); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - JSON_THROW(std::domain_error("array index must not begin with '0'")); - } - - // note: at performs range check - ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token))); - break; - } - - default: - { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); - } - } - } - - return *ptr; - } - - /*! - @brief return a const reference to the pointed to value - - @param[in] ptr a JSON value - - @return const reference to the JSON value pointed to by the JSON - pointer - */ - const_reference get_unchecked(const_pointer ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->m_type) - { - case value_t::object: - { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } - - case value_t::array: - { - if (reference_token == "-") - { - // "-" cannot be used for const access - throw std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range"); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - JSON_THROW(std::domain_error("array index must not begin with '0'")); - } - - // use unchecked array access - ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token))); - break; - } - - default: - { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); - } - } - } - - return *ptr; - } - - const_reference get_checked(const_pointer ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->m_type) - { - case value_t::object: - { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; - } - - case value_t::array: - { - if (reference_token == "-") - { - // "-" always fails the range check - throw std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range"); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - JSON_THROW(std::domain_error("array index must not begin with '0'")); - } - - // note: at performs range check - ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token))); - break; - } - - default: - { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); - } - } - } - - return *ptr; - } - - /// split the string input to reference tokens - static std::vector<std::string> split(const std::string& reference_string) - { - std::vector<std::string> result; - - // special case: empty reference string -> no reference tokens - if (reference_string.empty()) - { - return result; - } - - // check if nonempty reference string begins with slash - if (reference_string[0] != '/') - { - JSON_THROW(std::domain_error("JSON pointer must be empty or begin with '/'")); - } - - // extract the reference tokens: - // - slash: position of the last read slash (or end of string) - // - start: position after the previous slash - for ( - // search for the first slash after the first character - size_t slash = reference_string.find_first_of('/', 1), - // set the beginning of the first reference token - start = 1; - // we can stop if start == string::npos+1 = 0 - start != 0; - // set the beginning of the next reference token - // (will eventually be 0 if slash == std::string::npos) - start = slash + 1, - // find next slash - slash = reference_string.find_first_of('/', start)) - { - // use the text between the beginning of the reference token - // (start) and the last slash (slash). - auto reference_token = reference_string.substr(start, slash - start); - - // check reference tokens are properly escaped - for (size_t pos = reference_token.find_first_of('~'); - pos != std::string::npos; - pos = reference_token.find_first_of('~', pos + 1)) - { - assert(reference_token[pos] == '~'); - - // ~ must be followed by 0 or 1 - if (pos == reference_token.size() - 1 or - (reference_token[pos + 1] != '0' and - reference_token[pos + 1] != '1')) - { - JSON_THROW(std::domain_error("escape error: '~' must be followed with '0' or '1'")); - } - } - - // finally, store the reference token - unescape(reference_token); - result.push_back(reference_token); - } - - return result; - } - - private: - /*! - @brief replace all occurrences of a substring by another string - - @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 - - @pre The search string @a f must not be empty. - - @since version 2.0.0 - */ - static void replace_substring(std::string& s, - const std::string& f, - const std::string& t) - { - assert(not f.empty()); - - for ( - size_t pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t - pos = s.find(f, pos + t.size()) // find next occurrence of f - ); - } - - /// escape tilde and slash - static std::string escape(std::string s) - { - // escape "~"" to "~0" and "/" to "~1" - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); - return s; - } - - /// unescape tilde and slash - static void unescape(std::string& s) - { - // first transform any occurrence of the sequence '~1' to '/' - replace_substring(s, "~1", "/"); - // then transform any occurrence of the sequence '~0' to '~' - replace_substring(s, "~0", "~"); - } - - /*! - @param[in] reference_string the reference string to the current value - @param[in] value the value to consider - @param[in,out] result the result object to insert values to - - @note Empty objects or arrays are flattened to `null`. - */ - static void flatten(const std::string& reference_string, - const basic_json& value, - basic_json& result) - { - switch (value.m_type) - { - case value_t::array: - { - if (value.m_value.array->empty()) - { - // flatten empty array as null - result[reference_string] = nullptr; - } - else - { - // iterate array and use index as reference string - for (size_t i = 0; i < value.m_value.array->size(); ++i) - { - flatten(reference_string + "/" + std::to_string(i), - value.m_value.array->operator[](i), result); - } } break; } case value_t::object: { - if (value.m_value.object->empty()) + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case value_t::array: + { + // create an entry in the array + result = &result->operator[](static_cast<size_type>(std::stoi(reference_token))); + break; + } + + /* + The following code is only reached if there exists a + reference token _and_ the current value is primitive. In + this case, we have an error situation, because primitive + values may only occur as single value; that is, with an + empty list of reference tokens. + */ + default: + { + JSON_THROW(std::domain_error("invalid value to unflatten")); + } + } + } + + return *result; + } + + /*! + @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 + + @complexity Linear in the length of the JSON pointer. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + */ + reference get_unchecked(pointer ptr) const + { + 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: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case value_t::array: + { + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') { - // flatten empty object as null - result[reference_string] = nullptr; + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } + + if (reference_token == "-") + { + // explicityly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); } else { - // iterate object and use keys as reference string - for (const auto& element : *value.m_value.object) - { - flatten(reference_string + "/" + escape(element.first), - element.second, result); - } + // convert array index to number; unchecked access + ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token))); } break; } default: { - // add primitive value with its reference string - result[reference_string] = value; - break; + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); } } } - /*! - @param[in] value flattened JSON + return *ptr; + } - @return unflattened JSON - */ - static basic_json unflatten(const basic_json& value) + reference get_checked(pointer ptr) const + { + for (const auto& reference_token : reference_tokens) { - if (not value.is_object()) + switch (ptr->m_type) { - JSON_THROW(std::domain_error("only objects can be unflattened")); - } - - basic_json result; - - // iterate the JSON object values - for (const auto& element : *value.m_value.object) - { - if (not element.second.is_primitive()) + case value_t::object: { - JSON_THROW(std::domain_error("values in object must be primitive")); + // note: at performs range check + ptr = &ptr->at(reference_token); + break; } - // assign value to reference pointed to by JSON pointer; Note - // that if the JSON pointer is "" (i.e., points to the whole - // value), function get_and_create returns a reference to - // result itself. An assignment will then create a primitive - // value. - json_pointer(element.first).get_and_create(result) = element.second; - } + case value_t::array: + { + if (reference_token == "-") + { + // "-" always fails the range check + throw std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range"); + } + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } + + // note: at performs range check + ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token))); + break; + } + + default: + { + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + } + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + */ + const_reference get_unchecked(const_pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case value_t::array: + { + if (reference_token == "-") + { + // "-" cannot be used for const access + throw std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range"); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } + + // use unchecked array access + ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token))); + break; + } + + default: + { + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + } + } + } + + return *ptr; + } + + const_reference get_checked(const_pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case value_t::array: + { + if (reference_token == "-") + { + // "-" always fails the range check + throw std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range"); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } + + // note: at performs range check + ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token))); + break; + } + + default: + { + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + } + } + } + + return *ptr; + } + + /// split the string input to reference tokens + static std::vector<std::string> split(const std::string& reference_string) + { + std::vector<std::string> result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { return result; } - private: - /// the reference tokens - std::vector<std::string> reference_tokens {}; - }; + // check if nonempty reference string begins with slash + if (reference_string[0] != '/') + { + JSON_THROW(std::domain_error("JSON pointer must be empty or begin with '/'")); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == string::npos+1 = 0 + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + assert(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (pos == reference_token.size() - 1 or + (reference_token[pos + 1] != '0' and + reference_token[pos + 1] != '1')) + { + JSON_THROW(std::domain_error("escape error: '~' must be followed with '0' or '1'")); + } + } + + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + private: + /*! + @brief replace all occurrences of a substring by another string + + @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 + + @pre The search string @a f must not be empty. + + @since version 2.0.0 + */ + static void replace_substring(std::string& s, + const std::string& f, + const std::string& t) + { + assert(not f.empty()); + + for ( + size_t pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t + pos = s.find(f, pos + t.size()) // find next occurrence of f + ); + } + + /// escape tilde and slash + static std::string escape(std::string s) + { + // escape "~"" to "~0" and "/" to "~1" + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } + + /// unescape tilde and slash + static void unescape(std::string& s) + { + // first transform any occurrence of the sequence '~1' to '/' + replace_substring(s, "~1", "/"); + // then transform any occurrence of the sequence '~0' to '~' + replace_substring(s, "~0", "~"); + } + + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const basic_json& value, + basic_json& result) + { + switch (value.m_type) + { + case value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + escape(element.first), + element.second, result); + } + } + break; + } + + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + */ + static basic_json unflatten(const basic_json& value) + { + if (not value.is_object()) + { + JSON_THROW(std::domain_error("only objects can be unflattened")); + } + + basic_json result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (not element.second.is_primitive()) + { + JSON_THROW(std::domain_error("values in object must be primitive")); + } + + // assign value to reference pointed to by JSON pointer; Note + // that if the JSON pointer is "" (i.e., points to the whole + // value), function get_and_create returns a reference to + // result itself. An assignment will then create a primitive + // value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + private: + /// the reference tokens + std::vector<std::string> reference_tokens {}; + }; ////////////////////////// // JSON Pointer support // @@ -11379,38 +11843,38 @@ using json = basic_json<>; // specialization of std::swap, and std::hash namespace std -{ -/*! -@brief exchanges the values of two JSON objects - -@since version 1.0.0 -*/ -template<> -inline void swap(nlohmann::json& j1, - nlohmann::json& j2) noexcept( - is_nothrow_move_constructible<nlohmann::json>::value and - is_nothrow_move_assignable<nlohmann::json>::value - ) -{ - j1.swap(j2); -} - -/// hash value for JSON objects -template<> -struct hash<nlohmann::json> { /*! - @brief return a hash value for a JSON object + @brief exchanges the values of two JSON objects @since version 1.0.0 */ - std::size_t operator()(const nlohmann::json& j) const + template<> + inline void swap(nlohmann::json& j1, + nlohmann::json& j2) noexcept( + is_nothrow_move_constructible<nlohmann::json>::value and + is_nothrow_move_assignable<nlohmann::json>::value + ) + { + j1.swap(j2); + } + + /// hash value for JSON objects + template<> + struct hash<nlohmann::json> + { + /*! + @brief return a hash value for a JSON object + + @since version 1.0.0 + */ + std::size_t operator()(const nlohmann::json& j) const { // a naive hashing via the string representation const auto& h = hash<nlohmann::json::string_t>(); return h(j.dump()); } -}; + }; } // namespace std /*! diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 40867b19..71e75df4 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -39,66 +39,66 @@ TEST_CASE("lexer class") SECTION("structural characters") { CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("["), - 1).scan() == json::lexer::token_type::begin_array)); + 1).scan() == json::lexer::token_type::begin_array)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("]"), - 1).scan() == json::lexer::token_type::end_array)); + 1).scan() == json::lexer::token_type::end_array)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("{"), - 1).scan() == json::lexer::token_type::begin_object)); + 1).scan() == json::lexer::token_type::begin_object)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("}"), - 1).scan() == json::lexer::token_type::end_object)); + 1).scan() == json::lexer::token_type::end_object)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(","), - 1).scan() == json::lexer::token_type::value_separator)); + 1).scan() == json::lexer::token_type::value_separator)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(":"), - 1).scan() == json::lexer::token_type::name_separator)); + 1).scan() == json::lexer::token_type::name_separator)); } SECTION("literal names") { CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("null"), - 4).scan() == json::lexer::token_type::literal_null)); + 4).scan() == json::lexer::token_type::literal_null)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("true"), - 4).scan() == json::lexer::token_type::literal_true)); + 4).scan() == json::lexer::token_type::literal_true)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("false"), - 5).scan() == json::lexer::token_type::literal_false)); + 5).scan() == json::lexer::token_type::literal_false)); } SECTION("numbers") { CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("0"), - 1).scan() == json::lexer::token_type::value_number)); + 1).scan() == json::lexer::token_type::value_number)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("1"), - 1).scan() == json::lexer::token_type::value_number)); + 1).scan() == json::lexer::token_type::value_number)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("2"), - 1).scan() == json::lexer::token_type::value_number)); + 1).scan() == json::lexer::token_type::value_number)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("3"), - 1).scan() == json::lexer::token_type::value_number)); + 1).scan() == json::lexer::token_type::value_number)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("4"), - 1).scan() == json::lexer::token_type::value_number)); + 1).scan() == json::lexer::token_type::value_number)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("5"), - 1).scan() == json::lexer::token_type::value_number)); + 1).scan() == json::lexer::token_type::value_number)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("6"), - 1).scan() == json::lexer::token_type::value_number)); + 1).scan() == json::lexer::token_type::value_number)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("7"), - 1).scan() == json::lexer::token_type::value_number)); + 1).scan() == json::lexer::token_type::value_number)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("8"), - 1).scan() == json::lexer::token_type::value_number)); + 1).scan() == json::lexer::token_type::value_number)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("9"), - 1).scan() == json::lexer::token_type::value_number)); + 1).scan() == json::lexer::token_type::value_number)); } SECTION("whitespace") { // result is end_of_input, because not token is following CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" "), - 1).scan() == json::lexer::token_type::end_of_input)); + 1).scan() == json::lexer::token_type::end_of_input)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\t"), - 1).scan() == json::lexer::token_type::end_of_input)); + 1).scan() == json::lexer::token_type::end_of_input)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\n"), - 1).scan() == json::lexer::token_type::end_of_input)); + 1).scan() == json::lexer::token_type::end_of_input)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\r"), - 1).scan() == json::lexer::token_type::end_of_input)); + 1).scan() == json::lexer::token_type::end_of_input)); CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" \t\n\r\n\t "), - 7).scan() == json::lexer::token_type::end_of_input)); + 7).scan() == json::lexer::token_type::end_of_input)); } } diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index 364c5ae7..0cd834d6 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -38,252 +38,257 @@ using nlohmann::json; namespace udt { - enum class country - { +enum class country +{ china, france, russia - }; +}; - struct age - { +struct age +{ int m_val; - }; +}; - struct name - { +struct name +{ std::string m_val; - }; +}; - struct address - { +struct address +{ std::string m_val; - }; +}; - struct person - { +struct person +{ age m_age; name m_name; country m_country; - }; +}; - struct contact - { +struct contact +{ person m_person; address m_address; - }; +}; - struct contact_book - { +struct contact_book +{ name m_book_name; std::vector<contact> m_contacts; - }; +}; } // to_json methods namespace udt { - // templates because of the custom_json tests (see below) - template <typename Json> - void to_json(Json& j, age a) - { +// templates because of the custom_json tests (see below) +template <typename Json> +void to_json(Json& j, age a) +{ j = a.m_val; - } +} - template <typename Json> - void to_json(Json& j, name const& n) - { +template <typename Json> +void to_json(Json& j, name const& n) +{ j = n.m_val; - } +} - template <typename Json> - void to_json(Json& j, country c) - { +template <typename Json> +void to_json(Json& j, country c) +{ switch (c) { - case country::china: - j = u8"中华人民共和国"; - return; - case country::france: - j = "France"; - return; - case country::russia: - j = u8"Российская Федерация"; - return; + case country::china: + j = u8"中华人民共和国"; + return; + case country::france: + j = "France"; + return; + case country::russia: + j = u8"Российская Федерация"; + return; } - } +} - template <typename Json> - void to_json(Json& j, person const& p) - { +template <typename Json> +void to_json(Json& j, person const& p) +{ j = Json{{"age", p.m_age}, {"name", p.m_name}, {"country", p.m_country}}; - } +} - void to_json(nlohmann::json& j, address const& a) - { +void to_json(nlohmann::json& j, address const& a) +{ j = a.m_val; - } +} - void to_json(nlohmann::json& j, contact const& c) - { +void to_json(nlohmann::json& j, contact const& c) +{ j = json{{"person", c.m_person}, {"address", c.m_address}}; - } +} - void to_json(nlohmann::json& j, contact_book const& cb) - { +void to_json(nlohmann::json& j, contact_book const& cb) +{ j = json{{"name", cb.m_book_name}, {"contacts", cb.m_contacts}}; - } +} - // operators - bool operator==(age lhs, age rhs) - { +// operators +bool operator==(age lhs, age rhs) +{ return lhs.m_val == rhs.m_val; - } +} - bool operator==(address const &lhs, address const &rhs) - { +bool operator==(address const& lhs, address const& rhs) +{ return lhs.m_val == rhs.m_val; - } +} - bool operator==(name const &lhs, name const &rhs) - { +bool operator==(name const& lhs, name const& rhs) +{ return lhs.m_val == rhs.m_val; - } +} - bool operator==(person const &lhs, person const &rhs) - { +bool operator==(person const& lhs, person const& rhs) +{ return std::tie(lhs.m_name, lhs.m_age) == std::tie(rhs.m_name, rhs.m_age); - } +} - bool operator==(contact const &lhs, contact const &rhs) - { +bool operator==(contact const& lhs, contact const& rhs) +{ return std::tie(lhs.m_person, lhs.m_address) == std::tie(rhs.m_person, rhs.m_address); - } +} - bool operator==(contact_book const &lhs, contact_book const &rhs) - { +bool operator==(contact_book const& lhs, contact_book const& rhs) +{ return std::tie(lhs.m_book_name, lhs.m_contacts) == std::tie(rhs.m_book_name, rhs.m_contacts); - } +} } // from_json methods namespace udt { - template <typename Json> - void from_json(Json const& j, age &a) - { +template <typename Json> +void from_json(Json const& j, age& a) +{ a.m_val = j.template get<int>(); - } +} - template <typename Json> - void from_json(Json const& j, name &n) - { +template <typename Json> +void from_json(Json const& j, name& n) +{ n.m_val = j.template get<std::string>(); - } +} - template <typename Json> - void from_json(Json const &j, country &c) - { +template <typename Json> +void from_json(Json const& j, country& c) +{ const auto str = j.template get<std::string>(); - static const std::map<std::string, country> m = { + static const std::map<std::string, country> m = + { {u8"中华人民共和国", country::china}, {"France", country::france}, - {"Российская Федерация", country::russia}}; + {"Российская Федерация", country::russia} + }; const auto it = m.find(str); // TODO test exceptions c = it->second; - } +} - template <typename Json> - void from_json(Json const& j, person &p) - { +template <typename Json> +void from_json(Json const& j, person& p) +{ p.m_age = j["age"].template get<age>(); p.m_name = j["name"].template get<name>(); p.m_country = j["country"].template get<country>(); - } +} - void from_json(nlohmann::json const &j, address &a) - { +void from_json(nlohmann::json const& j, address& a) +{ a.m_val = j.get<std::string>(); - } +} - void from_json(nlohmann::json const& j, contact &c) - { +void from_json(nlohmann::json const& j, contact& c) +{ c.m_person = j["person"].get<person>(); c.m_address = j["address"].get<address>(); - } +} - void from_json(nlohmann::json const&j, contact_book &cb) - { +void from_json(nlohmann::json const& j, contact_book& cb) +{ cb.m_book_name = j["name"].get<name>(); cb.m_contacts = j["contacts"].get<std::vector<contact>>(); - } +} } TEST_CASE("basic usage", "[udt]") { - // a bit narcissic maybe :) ? - const udt::age a{23}; - const udt::name n{"theo"}; - const udt::country c{udt::country::france}; - const udt::person sfinae_addict{a, n, c}; - const udt::person senior_programmer{{42}, {u8"王芳"}, udt::country::china}; - const udt::address addr{"Paris"}; - const udt::contact cpp_programmer{sfinae_addict, addr}; - const udt::contact_book book{{"C++"}, {cpp_programmer, {senior_programmer, addr}}}; + // a bit narcissic maybe :) ? + const udt::age a + { + 23 + }; + const udt::name n{"theo"}; + const udt::country c{udt::country::france}; + const udt::person sfinae_addict{a, n, c}; + const udt::person senior_programmer{{42}, {u8"王芳"}, udt::country::china}; + const udt::address addr{"Paris"}; + const udt::contact cpp_programmer{sfinae_addict, addr}; + const udt::contact_book book{{"C++"}, {cpp_programmer, {senior_programmer, addr}}}; - SECTION("conversion to json via free-functions") - { - CHECK(json(a) == json(23)); - CHECK(json(n) == json("theo")); - CHECK(json(c) == json("France")); - CHECK(json(sfinae_addict) == R"({"name":"theo", "age":23, "country":"France"})"_json); - CHECK(json("Paris") == json(addr)); - CHECK(json(cpp_programmer) == - R"({"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"})"_json); + SECTION("conversion to json via free-functions") + { + CHECK(json(a) == json(23)); + CHECK(json(n) == json("theo")); + CHECK(json(c) == json("France")); + CHECK(json(sfinae_addict) == R"({"name":"theo", "age":23, "country":"France"})"_json); + CHECK(json("Paris") == json(addr)); + CHECK(json(cpp_programmer) == + R"({"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"})"_json); - CHECK( - json(book) == - u8R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json); + CHECK( + json(book) == + u8R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json); - } + } - SECTION("conversion from json via free-functions") - { - const auto big_json = - u8R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json; - const auto parsed_book = big_json.get<udt::contact_book>(); - const auto book_name = big_json["name"].get<udt::name>(); - const auto contacts = big_json["contacts"].get<std::vector<udt::contact>>(); - const auto contact_json = big_json["contacts"].at(0); - const auto contact = contact_json.get<udt::contact>(); - const auto person = contact_json["person"].get<udt::person>(); - const auto address = contact_json["address"].get<udt::address>(); - const auto age = contact_json["person"]["age"].get<udt::age>(); - const auto country = contact_json["person"]["country"].get<udt::country>(); - const auto name = contact_json["person"]["name"].get<udt::name>(); + SECTION("conversion from json via free-functions") + { + const auto big_json = + u8R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json; + const auto parsed_book = big_json.get<udt::contact_book>(); + const auto book_name = big_json["name"].get<udt::name>(); + const auto contacts = big_json["contacts"].get<std::vector<udt::contact>>(); + const auto contact_json = big_json["contacts"].at(0); + const auto contact = contact_json.get<udt::contact>(); + const auto person = contact_json["person"].get<udt::person>(); + const auto address = contact_json["address"].get<udt::address>(); + const auto age = contact_json["person"]["age"].get<udt::age>(); + const auto country = contact_json["person"]["country"].get<udt::country>(); + const auto name = contact_json["person"]["name"].get<udt::name>(); - CHECK(age == a); - CHECK(name == n); - CHECK(country == c); - CHECK(address == addr); - CHECK(person == sfinae_addict); - CHECK(contact == cpp_programmer); - CHECK(contacts == book.m_contacts); - CHECK(book_name == udt::name{"C++"}); - CHECK(book == parsed_book); - } + CHECK(age == a); + CHECK(name == n); + CHECK(country == c); + CHECK(address == addr); + CHECK(person == sfinae_addict); + CHECK(contact == cpp_programmer); + CHECK(contacts == book.m_contacts); + CHECK(book_name == udt::name{"C++"}); + CHECK(book == parsed_book); + } } namespace udt { struct legacy_type { - std::string number; + std::string number; }; } @@ -292,88 +297,96 @@ namespace nlohmann template <typename T> struct adl_serializer<std::shared_ptr<T>> { - static void to_json(json& j, std::shared_ptr<T> const& opt) - { - if (opt) - j = *opt; - else - j = nullptr; - } + static void to_json(json& j, std::shared_ptr<T> const& opt) + { + if (opt) + { + j = *opt; + } + else + { + j = nullptr; + } + } - static void from_json(json const &j, std::shared_ptr<T> &opt) - { - if (j.is_null()) - opt = nullptr; - else - opt.reset(new T(j.get<T>())); - } + static void from_json(json const& j, std::shared_ptr<T>& opt) + { + if (j.is_null()) + { + opt = nullptr; + } + else + { + opt.reset(new T(j.get<T>())); + } + } }; template <> struct adl_serializer<udt::legacy_type> { - static void to_json(json& j, udt::legacy_type const& l) - { - j = std::stoi(l.number); - } + static void to_json(json& j, udt::legacy_type const& l) + { + j = std::stoi(l.number); + } - static void from_json(json const& j, udt::legacy_type& l) - { - l.number = std::to_string(j.get<int>()); - } + static void from_json(json const& j, udt::legacy_type& l) + { + l.number = std::to_string(j.get<int>()); + } }; } TEST_CASE("adl_serializer specialization", "[udt]") { - SECTION("partial specialization") - { - SECTION("to_json") + SECTION("partial specialization") { - std::shared_ptr<udt::person> optPerson; + SECTION("to_json") + { + std::shared_ptr<udt::person> optPerson; - json j = optPerson; - CHECK(j.is_null()); + json j = optPerson; + CHECK(j.is_null()); - optPerson.reset(new udt::person{{42}, {"John Doe"}}); - j = optPerson; - CHECK_FALSE(j.is_null()); + optPerson.reset(new udt::person{{42}, {"John Doe"}}); + j = optPerson; + CHECK_FALSE(j.is_null()); - CHECK(j.get<udt::person>() == *optPerson); + CHECK(j.get<udt::person>() == *optPerson); + } + + SECTION("from_json") + { + auto person = udt::person{{42}, {"John Doe"}}; + json j = person; + + auto optPerson = j.get<std::shared_ptr<udt::person>>(); + REQUIRE(optPerson); + CHECK(*optPerson == person); + + j = nullptr; + optPerson = j.get<std::shared_ptr<udt::person>>(); + CHECK(!optPerson); + } } - SECTION("from_json") + SECTION("total specialization") { - auto person = udt::person{{42}, {"John Doe"}}; - json j = person; + SECTION("to_json") + { + udt::legacy_type lt{"4242"}; - auto optPerson = j.get<std::shared_ptr<udt::person>>(); - REQUIRE(optPerson); - CHECK(*optPerson == person); + json j = lt; + CHECK(j.get<int>() == 4242); + } - j = nullptr; - optPerson = j.get<std::shared_ptr<udt::person>>(); - CHECK(!optPerson); + SECTION("from_json") + { + json j = 4242; + auto lt = j.get<udt::legacy_type>(); + CHECK(lt.number == "4242"); + } } - } - - SECTION("total specialization") - { - SECTION("to_json") - { - udt::legacy_type lt{"4242"}; - - json j = lt; - CHECK(j.get<int>() == 4242); - } - - SECTION("from_json") - { - json j = 4242; - auto lt = j.get<udt::legacy_type>(); - CHECK(lt.number == "4242"); - } - } } namespace nlohmann @@ -383,22 +396,22 @@ namespace nlohmann template <typename T> struct adl_serializer<std::vector<T>> { - static void to_json(json& j, std::vector<T> const& opt) - { - } + static void to_json(json& j, std::vector<T> const& opt) + { + } - static void from_json(json const &j, std::vector<T> &opt) - { - } + static void from_json(json const& j, std::vector<T>& opt) + { + } }; } TEST_CASE("current supported types are preferred over specializations", "[udt]") { - json j = std::vector<int>{1, 2, 3}; - auto f = j.get<std::vector<int>>(); - CHECK((f == std::vector<int>{1, 2, 3})); + json j = std::vector<int> {1, 2, 3}; + auto f = j.get<std::vector<int>>(); + CHECK((f == std::vector<int> {1, 2, 3})); } namespace nlohmann @@ -406,55 +419,63 @@ namespace nlohmann template <typename T> struct adl_serializer<std::unique_ptr<T>> { - static void to_json(json& j, std::unique_ptr<T> const& opt) - { - if (opt) - j = *opt; - else - j = nullptr; - } + static void to_json(json& j, std::unique_ptr<T> const& opt) + { + if (opt) + { + j = *opt; + } + else + { + j = nullptr; + } + } - // this is the overload needed for non-copyable types, - // should we add a priority tag in the implementation to prefer this overload if it exists? - static std::unique_ptr<T> from_json(json const &j) - { - if (j.is_null()) - return nullptr; - else - return std::unique_ptr<T>(new T(j.get<T>())); - } + // this is the overload needed for non-copyable types, + // should we add a priority tag in the implementation to prefer this overload if it exists? + static std::unique_ptr<T> from_json(json const& j) + { + if (j.is_null()) + { + return nullptr; + } + else + { + return std::unique_ptr<T>(new T(j.get<T>())); + } + } }; } TEST_CASE("Non-copyable types", "[udt]") { - SECTION("to_json") - { - std::unique_ptr<udt::person> optPerson; + SECTION("to_json") + { + std::unique_ptr<udt::person> optPerson; - json j = optPerson; - CHECK(j.is_null()); + json j = optPerson; + CHECK(j.is_null()); - optPerson.reset(new udt::person{{42}, {"John Doe"}}); - j = optPerson; - CHECK_FALSE(j.is_null()); + optPerson.reset(new udt::person{{42}, {"John Doe"}}); + j = optPerson; + CHECK_FALSE(j.is_null()); - CHECK(j.get<udt::person>() == *optPerson); - } + CHECK(j.get<udt::person>() == *optPerson); + } - SECTION("from_json") - { - auto person = udt::person{{42}, {"John Doe"}}; - json j = person; + SECTION("from_json") + { + auto person = udt::person{{42}, {"John Doe"}}; + json j = person; - auto optPerson = j.get<std::unique_ptr<udt::person>>(); - REQUIRE(optPerson); - CHECK(*optPerson == person); + auto optPerson = j.get<std::unique_ptr<udt::person>>(); + REQUIRE(optPerson); + CHECK(*optPerson == person); - j = nullptr; - optPerson = j.get<std::unique_ptr<udt::person>>(); - CHECK(!optPerson); - } + j = nullptr; + optPerson = j.get<std::unique_ptr<udt::person>>(); + CHECK(!optPerson); + } } // custom serializer @@ -462,87 +483,89 @@ TEST_CASE("Non-copyable types", "[udt]") template <typename T, typename = typename std::enable_if<std::is_pod<T>::value>::type> struct pod_serializer { - // I could forward-declare this struct, and add a basic_json alias - template <typename Json> - static void from_json(Json const& j , T& t) - { - auto value = j.template get<std::uint64_t>(); - auto bytes = static_cast<char*>(static_cast<void*>(&value)); - std::memcpy(&t, bytes, sizeof(value)); - } + // I could forward-declare this struct, and add a basic_json alias + template <typename Json> + static void from_json(Json const& j , T& t) + { + auto value = j.template get<std::uint64_t>(); + auto bytes = static_cast<char*>(static_cast<void*>(&value)); + std::memcpy(&t, bytes, sizeof(value)); + } - template <typename Json> - static void to_json(Json& j, T const& t) - { - auto bytes = static_cast<char const*>(static_cast<void const*>(&t)); - std::uint64_t value = bytes[0]; - for (auto i = 1; i < 8; ++i) - value |= bytes[i] << 8 * i; + template <typename Json> + static void to_json(Json& j, T const& t) + { + auto bytes = static_cast<char const*>(static_cast<void const*>(&t)); + std::uint64_t value = bytes[0]; + for (auto i = 1; i < 8; ++i) + { + value |= bytes[i] << 8 * i; + } - j = value; - } + j = value; + } }; namespace udt { struct small_pod { - int begin; - char middle; - short end; + int begin; + char middle; + short end; }; bool operator==(small_pod lhs, small_pod rhs) { - return std::tie(lhs.begin, lhs.middle, lhs.end) == - std::tie(lhs.begin, lhs.middle, lhs.end); + return std::tie(lhs.begin, lhs.middle, lhs.end) == + std::tie(lhs.begin, lhs.middle, lhs.end); } } TEST_CASE("custom serializer for pods", "[udt]") { - using custom_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, double, std::allocator, pod_serializer>; + using custom_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, double, std::allocator, pod_serializer>; - auto p = udt::small_pod{42, '/', 42}; - custom_json j = p; + auto p = udt::small_pod{42, '/', 42}; + custom_json j = p; - auto p2 = j.get<udt::small_pod>(); + auto p2 = j.get<udt::small_pod>(); - CHECK(p == p2); + CHECK(p == p2); } template <typename T, typename> struct another_adl_serializer; - using custom_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, double, std::allocator, another_adl_serializer>; +using custom_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, double, std::allocator, another_adl_serializer>; template <typename T, typename> struct another_adl_serializer { - static void from_json(custom_json const& j , T& t) - { - using nlohmann::from_json; - from_json(j, t); - } + static void from_json(custom_json const& j , T& t) + { + using nlohmann::from_json; + from_json(j, t); + } - static void to_json(custom_json& j , T const& t) - { - using nlohmann::to_json; - to_json(j, t); - } + static void to_json(custom_json& j , T const& t) + { + using nlohmann::to_json; + to_json(j, t); + } }; TEST_CASE("custom serializer that does adl by default", "[udt]") { - using json = nlohmann::json; + using json = nlohmann::json; - auto me = udt::person{23, "theo", udt::country::france}; + auto me = udt::person{23, "theo", udt::country::france}; - json j = me; - custom_json cj = me; + json j = me; + custom_json cj = me; - CHECK(j.dump() == cj.dump()); + CHECK(j.dump() == cj.dump()); - CHECK(me == j.get<udt::person>()); - CHECK(me == cj.get<udt::person>()); + CHECK(me == j.get<udt::person>()); + CHECK(me == cj.get<udt::person>()); }