diff --git a/src/json.hpp b/src/json.hpp index 9c0a8168..fc7b60f2 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -136,15 +136,15 @@ the default value for a given type */ enum class value_t : uint8_t { - null, ///< null value - object, ///< object (unordered set of name/value pairs) - array, ///< array (ordered collection of values) - string, ///< string value - boolean, ///< boolean value - number_integer, ///< number value (signed integer) - number_unsigned, ///< number value (unsigned integer) - number_float, ///< number value (floating-point) - discarded ///< discarded by the the parser callback function + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + discarded ///< discarded by the the parser callback function }; // alias templates to reduce boilerplate @@ -168,29 +168,29 @@ using is_unscoped_enum = namespace detail { -template std::string type_name(const Json &j) +template std::string type_name(const Json& j) { - switch (j.m_type) - { - case value_t::null: - return "null"; - case value_t::object: - return "object"; - case value_t::array: - return "array"; - case value_t::string: - return "string"; - case value_t::boolean: - return "boolean"; - case value_t::discarded: - return "discarded"; - default: - return "number"; - } + switch (j.m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::discarded: + return "discarded"; + default: + return "number"; + } } // dispatch utility (taken from ranges-v3) -template struct priority_tag : priority_tag {}; +template struct priority_tag : priority_tag < N - 1 > {}; template <> struct priority_tag<0> {}; @@ -201,120 +201,120 @@ template struct external_constructor; template <> struct external_constructor { - template - static void construct(Json &j, typename Json::boolean_t b) noexcept - { - j.m_type = value_t::boolean; - j.m_value = b; - j.assert_invariant(); - } + template + static void construct(Json& j, typename Json::boolean_t b) noexcept + { + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } }; template <> struct external_constructor { - template - static void construct(Json &j, const typename Json::string_t& s) - { - j.m_type = value_t::string; - j.m_value = s; - j.assert_invariant(); - } + template + static void construct(Json& j, const typename Json::string_t& s) + { + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } }; template <> struct external_constructor { - template - static void construct(Json &j, typename Json::number_float_t val) noexcept - { - // replace infinity and NAN by null - if (not std::isfinite(val)) - j = Json{}; - else + template + static void construct(Json& j, typename Json::number_float_t val) noexcept { - j.m_type = value_t::number_float; - j.m_value = val; + // replace infinity and NAN by null + if (not std::isfinite(val)) + j = Json{}; + else + { + j.m_type = value_t::number_float; + j.m_value = val; + } + j.assert_invariant(); } - j.assert_invariant(); - } }; template <> struct external_constructor { - template - static void construct(Json &j, typename Json::number_unsigned_t val) noexcept - { - j.m_type = value_t::number_unsigned; - j.m_value = val; - j.assert_invariant(); - } + template + static void construct(Json& j, typename Json::number_unsigned_t val) noexcept + { + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } }; template <> struct external_constructor { - template - static void construct(Json &j, typename Json::number_integer_t val) noexcept - { - j.m_type = value_t::number_integer; - j.m_value = val; - j.assert_invariant(); - } + template + static void construct(Json& j, typename Json::number_integer_t val) noexcept + { + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } }; template <> struct external_constructor { - template - static void construct(Json &j, const typename Json::array_t& arr) - { - j.m_type = value_t::array; - j.m_value = arr; - j.assert_invariant(); - } + template + static void construct(Json& j, const typename Json::array_t& arr) + { + j.m_type = value_t::array; + j.m_value = arr; + j.assert_invariant(); + } - template ::value, - int> = 0> - static void construct(Json &j, const CompatibleArrayType &arr) - { - using std::begin; - using std::end; - j.m_type = value_t::array; - j.m_value.array = - j.template create(begin(arr), end(arr)); - j.assert_invariant(); - } + template ::value, + int> = 0> + static void construct(Json& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + j.m_type = value_t::array; + j.m_value.array = + j.template create(begin(arr), end(arr)); + j.assert_invariant(); + } }; template <> struct external_constructor { - template - static void construct(Json &j, const typename Json::object_t& obj) - { - j.m_type = value_t::object; - j.m_value = obj; - j.assert_invariant(); - } + template + static void construct(Json& j, const typename Json::object_t& obj) + { + j.m_type = value_t::object; + j.m_value = obj; + j.assert_invariant(); + } - template ::value, - int> = 0> - static void construct(Json &j, const CompatibleObjectType &obj) - { - using std::begin; - using std::end; + template ::value, + int> = 0> + static void construct(Json& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; - j.m_type = value_t::object; - j.m_value.object = - j.template create(begin(obj), end(obj)); - j.assert_invariant(); - } + j.m_type = value_t::object; + j.m_value.object = + j.template create(begin(obj), end(obj)); + j.assert_invariant(); + } }; // Implementation of 2 C++17 constructs: conjunction, negation. @@ -380,11 +380,11 @@ struct is_compatible_object_type_impl template struct is_compatible_object_type { - static auto constexpr value = is_compatible_object_type_impl< - conjunction>, - has_mapped_type, - has_key_type>::value, - typename BasicJson::object_t, CompatibleObjectType>::value; + static auto constexpr value = is_compatible_object_type_impl < + conjunction>, + has_mapped_type, + has_key_type>::value, + typename BasicJson::object_t, CompatibleObjectType >::value; }; template @@ -400,17 +400,17 @@ struct is_basic_json_nested_type template struct is_compatible_array_type { - // TODO concept Container? - // this might not make VS happy - static auto constexpr value = + // TODO concept Container? + // this might not make VS happy + static auto constexpr value = conjunction>, - negation>, - negation>, - negation>, - has_value_type, - has_iterator>::value; + negation>, + negation>, + negation>, + has_value_type, + has_iterator>::value; }; template @@ -419,7 +419,7 @@ struct is_compatible_integer_type_impl : std::false_type {}; template struct is_compatible_integer_type_impl { - // is there an assert somewhere on overflows? + // is there an assert somewhere on overflows? using RealLimits = std::numeric_limits; using CompatibleLimits = std::numeric_limits; @@ -433,11 +433,11 @@ struct is_compatible_integer_type_impl struct is_compatible_integer_type { - static constexpr auto - value = is_compatible_integer_type_impl < - std::is_integral::value and - not std::is_same::value, - RealIntegerType, CompatibleNumberIntegerType > ::value; + static constexpr auto + value = is_compatible_integer_type_impl < + std::is_integral::value and + not std::is_same::value, + RealIntegerType, CompatibleNumberIntegerType > ::value; }; // This trait checks if JSONSerializer::from_json(json const&, udt&) exists @@ -461,261 +461,287 @@ struct has_from_json template struct has_non_default_from_json { -private: - template < - typename U, - typename = enable_if_t::from_json(std::declval()))>::value>> - static int detect(U &&); - static void detect(...); + private: + template < + typename U, + typename = enable_if_t::from_json(std::declval()))>::value >> + static int detect(U&&); + static void detect(...); -public: - static constexpr bool value = std::is_integral>()))>::value; + public: + static constexpr bool value = std::is_integral>()))>::value; }; // This trait checks if Json::json_serializer::to_json exists template struct has_to_json { -private: - template ::to_json( - std::declval(), std::declval()))> - static int detect(U &&); - static void detect(...); + private: + template ::to_json( + std::declval(), std::declval()))> + static int detect(U&&); + static void detect(...); -public: - static constexpr bool value = std::is_integral>()))>::value; + public: + static constexpr bool value = std::is_integral>()))>::value; }; // overloads for basic_json template parameters template ::value and - not std::is_same::value, + not std::is_same::value, int> = 0> -void get_arithmetic_value(const Json &j, ArithmeticType &val) +void get_arithmetic_value(const Json& j, ArithmeticType& val) { - // unsigned must be checked first, since is_number_integer() == true for unsigned - if (j.is_number_unsigned()) - val = static_cast(*j.template get_ptr()); - else if (j.is_number_integer()) - val = static_cast(*j.template get_ptr()); - else if (j.is_number_float()) - val = static_cast(*j.template get_ptr()); - else - throw std::domain_error("type must be number, but is " + type_name(j)); + // unsigned must be checked first, since is_number_integer() == true for unsigned + if (j.is_number_unsigned()) + { + val = static_cast(*j.template get_ptr()); + } + else if (j.is_number_integer()) + { + val = static_cast(*j.template get_ptr()); + } + else if (j.is_number_float()) + { + val = static_cast(*j.template get_ptr()); + } + else + { + throw std::domain_error("type must be number, but is " + type_name(j)); + } } template -void to_json(Json &j, typename Json::boolean_t b) noexcept +void to_json(Json& j, typename Json::boolean_t b) noexcept { - external_constructor::construct(j, b); + external_constructor::construct(j, b); } template ::value, + CompatibleString>::value, int> = 0> -void to_json(Json &j, const CompatibleString &s) +void to_json(Json& j, const CompatibleString& s) { - external_constructor::construct(j, s); + external_constructor::construct(j, s); } template ::value, int> = 0> -void to_json(Json &j, FloatType val) noexcept +void to_json(Json& j, FloatType val) noexcept { - external_constructor::construct(j, static_cast(val)); + external_constructor::construct(j, static_cast(val)); } template < typename Json, typename CompatibleNumberUnsignedType, - enable_if_t::value, - int> = 0> -void to_json(Json &j, CompatibleNumberUnsignedType val) noexcept + enable_if_t::value, + int> = 0 > +void to_json(Json& j, CompatibleNumberUnsignedType val) noexcept { - external_constructor::construct(j, static_cast(val)); + external_constructor::construct(j, static_cast(val)); } template < typename Json, typename CompatibleNumberIntegerType, - enable_if_t::value, - int> = 0> -void to_json(Json &j, CompatibleNumberIntegerType val) noexcept + enable_if_t::value, + int> = 0 > +void to_json(Json& j, CompatibleNumberIntegerType val) noexcept { - external_constructor::construct(j, static_cast(val)); + external_constructor::construct(j, static_cast(val)); } template ::value, int> = 0> -void to_json(Json &j, UnscopedEnumType e) noexcept +void to_json(Json& j, UnscopedEnumType e) noexcept { - external_constructor::construct(j, e); + external_constructor::construct(j, e); } template < typename Json, typename CompatibleArrayType, - enable_if_t< + enable_if_t < is_compatible_array_type::value or - std::is_same::value, - int> = 0> -void to_json(Json &j, const CompatibleArrayType &arr) + std::is_same::value, + int > = 0 > +void to_json(Json& j, const CompatibleArrayType& arr) { - external_constructor::construct(j, arr); + external_constructor::construct(j, arr); } template < typename Json, typename CompatibleObjectType, enable_if_t::value, - int> = 0> -void to_json(Json &j, const CompatibleObjectType &arr) + int> = 0 > +void to_json(Json& j, const CompatibleObjectType& arr) { - external_constructor::construct(j, arr); + external_constructor::construct(j, arr); } template -void from_json(const Json & j, typename Json::boolean_t& b) +void from_json(const Json& j, typename Json::boolean_t& b) { - if (!j.is_boolean()) - throw std::domain_error("type must be boolean, but is " + type_name(j)); - b = *j.template get_ptr(); + if (!j.is_boolean()) + { + throw std::domain_error("type must be boolean, but is " + type_name(j)); + } + b = *j.template get_ptr(); } template -void from_json(const Json & j, typename Json::string_t& s) +void from_json(const Json& j, typename Json::string_t& s) { - if (!j.is_string()) - throw std::domain_error("type must be string, but is " + type_name(j)); - s = *j.template get_ptr(); + if (!j.is_string()) + { + throw std::domain_error("type must be string, but is " + type_name(j)); + } + s = *j.template get_ptr(); } template -void from_json(const Json & j, typename Json::number_float_t& val) +void from_json(const Json& j, typename Json::number_float_t& val) { - get_arithmetic_value(j, val); + get_arithmetic_value(j, val); } template -void from_json(const Json & j, typename Json::number_unsigned_t& val) +void from_json(const Json& j, typename Json::number_unsigned_t& val) { - get_arithmetic_value(j, val); + get_arithmetic_value(j, val); } template -void from_json(const Json & j, typename Json::number_integer_t& val) +void from_json(const Json& j, typename Json::number_integer_t& val) { - get_arithmetic_value(j, val); + get_arithmetic_value(j, val); } template ::value, int> = 0> -void from_json(const Json &j, UnscopedEnumType& e) +void from_json(const Json& j, UnscopedEnumType& e) { - typename std::underlying_type::type val = e; - get_arithmetic_value(j, val); - e = static_cast(val); + typename std::underlying_type::type val = e; + get_arithmetic_value(j, val); + e = static_cast(val); } template -void from_json(const Json &j, typename Json::array_t &arr) +void from_json(const Json& j, typename Json::array_t& arr) { - if (!j.is_array()) - throw std::domain_error("type must be array, but is " + type_name(j)); - arr = *j.template get_ptr(); + if (!j.is_array()) + { + throw std::domain_error("type must be array, but is " + type_name(j)); + } + arr = *j.template get_ptr(); } // forward_list doesn't have an insert method, TODO find a way to avoid including forward_list template -void from_json(const Json &j, std::forward_list& l) +void from_json(const Json& j, std::forward_list& l) { - // do not perform the check when user wants to retrieve jsons - // (except when it's null.. ?) - if (j.is_null()) - throw std::domain_error("type must be array, but is " + type_name(j)); - if (not std::is_same::value) - { - if (!j.is_array()) - throw std::domain_error("type must be array, but is " + type_name(j)); - } - for (auto it = j.rbegin(), end = j.rend(); it != end; ++it) - l.push_front(it->template get()); + // do not perform the check when user wants to retrieve jsons + // (except when it's null.. ?) + if (j.is_null()) + { + throw std::domain_error("type must be array, but is " + type_name(j)); + } + if (not std::is_same::value) + { + if (!j.is_array()) + { + throw std::domain_error("type must be array, but is " + type_name(j)); + } + } + for (auto it = j.rbegin(), end = j.rend(); it != end; ++it) + { + l.push_front(it->template get()); + } } template -void from_json_array_impl(const Json &j, CompatibleArrayType &arr, priority_tag<0>) +void from_json_array_impl(const Json& j, CompatibleArrayType& arr, priority_tag<0>) { - using std::begin; - using std::end; + using std::begin; + using std::end; - std::transform( - j.begin(), j.end(), std::inserter(arr, end(arr)), [](const Json &i) - { + std::transform( + j.begin(), j.end(), std::inserter(arr, end(arr)), [](const Json & i) + { // get() returns *this, this won't call a from_json method when // value_type is Json return i.template get(); - }); + }); } template -auto from_json_array_impl(const Json &j, CompatibleArrayType &arr, priority_tag<1>) - -> decltype( - arr.reserve(std::declval()), - void()) +auto from_json_array_impl(const Json& j, CompatibleArrayType& arr, priority_tag<1>) +-> decltype( + arr.reserve(std::declval()), + void()) { - using std::begin; - using std::end; + using std::begin; + using std::end; - arr.reserve(j.size()); - std::transform( - j.begin(), j.end(), std::inserter(arr, end(arr)), [](const Json &i) - { + arr.reserve(j.size()); + std::transform( + j.begin(), j.end(), std::inserter(arr, end(arr)), [](const Json & i) + { // get() returns *this, this won't call a from_json method when // value_type is Json return i.template get(); - }); + }); } template < typename Json, typename CompatibleArrayType, enable_if_t::value and - not std::is_same::value, - int> = 0> -void from_json(const Json &j, CompatibleArrayType &arr) + not std::is_same::value, + int> = 0 > +void from_json(const Json& j, CompatibleArrayType& arr) { - if (j.is_null()) - throw std::domain_error("type must be array, but is " + type_name(j)); - // when T == Json, do not check if value_t is correct - if (not std::is_same::value) - { - if (!j.is_array()) - throw std::domain_error("type must be array, but is " + type_name(j)); - } - from_json_array_impl(j, arr, priority_tag<1>{}); + if (j.is_null()) + { + throw std::domain_error("type must be array, but is " + type_name(j)); + } + // when T == Json, do not check if value_t is correct + if (not std::is_same::value) + { + if (!j.is_array()) + { + throw std::domain_error("type must be array, but is " + type_name(j)); + } + } + from_json_array_impl(j, arr, priority_tag<1> {}); } template < typename Json, typename CompatibleObjectType, enable_if_t::value, - int> = 0> -void from_json(const Json &j, CompatibleObjectType &obj) + int> = 0 > +void from_json(const Json& j, CompatibleObjectType& obj) { - if (!j.is_object()) - throw std::domain_error("type must be object, but is " + type_name(j)); + if (!j.is_object()) + { + throw std::domain_error("type must be object, but is " + type_name(j)); + } - auto inner_object = j.template get_ptr(); - using std::begin; - using std::end; - // we could avoid the assignment, but this might require a for loop, which - // might be less efficient than the container constructor for some containers (would it?) - obj = CompatibleObjectType(begin(*inner_object), end(*inner_object)); + auto inner_object = j.template get_ptr(); + using std::begin; + using std::end; + // we could avoid the assignment, but this might require a for loop, which + // might be less efficient than the container constructor for some containers (would it?) + obj = CompatibleObjectType(begin(*inner_object), end(*inner_object)); } // overload for arithmetic types, not chosen for basic_json template arguments (BooleanType, etc..) @@ -724,25 +750,35 @@ void from_json(const Json &j, CompatibleObjectType &obj) // in case of a custom BooleanType which is not an arithmetic type? template < typename Json, typename ArithmeticType, - enable_if_t< + enable_if_t < std::is_arithmetic::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value, - int> = 0> -void from_json(const Json &j, ArithmeticType &val) + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value, + int > = 0 > +void from_json(const Json& j, ArithmeticType& val) { - if (j.is_number_unsigned()) - val = static_cast(*j.template get_ptr()); - else if (j.is_number_integer()) - val = static_cast(*j.template get_ptr()); - else if (j.is_number_float()) - val = static_cast(*j.template get_ptr()); - else if (j.is_boolean()) - val = static_cast(*j.template get_ptr()); - else - throw std::domain_error("type must be number, but is " + type_name(j)); + if (j.is_number_unsigned()) + { + val = static_cast(*j.template get_ptr()); + } + else if (j.is_number_integer()) + { + val = static_cast(*j.template get_ptr()); + } + else if (j.is_number_float()) + { + val = static_cast(*j.template get_ptr()); + } + else if (j.is_boolean()) + { + val = static_cast(*j.template get_ptr()); + } + else + { + throw std::domain_error("type must be number, but is " + type_name(j)); + } } struct to_json_fn @@ -762,39 +798,39 @@ struct to_json_fn static_assert(sizeof(Json) == 0, "to_json method in T's namespace can not be called"); } -public: - template - void operator()(Json &j, T &&val) const - noexcept(noexcept(std::declval().call(j, std::forward(val), priority_tag<1>{}))) - { - return call(j, std::forward(val), priority_tag<1>{}); - } + public: + template + void operator()(Json& j, T&& val) const + noexcept(noexcept(std::declval().call(j, std::forward(val), priority_tag<1> {}))) + { + return call(j, std::forward(val), priority_tag<1> {}); + } }; struct from_json_fn { -private: - template - auto call(const Json &j, T &val, priority_tag<1>) const - noexcept(noexcept(from_json(j, val))) - -> decltype(from_json(j, val), void()) - { - return from_json(j, val); - } + private: + template + auto call(const Json& j, T& val, priority_tag<1>) const + noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val), void()) + { + return from_json(j, val); + } - template - void call(const Json &, T&, priority_tag<0>) const noexcept - { - static_assert(sizeof(Json) == 0, "from_json method in T's namespace can not be called"); - } + template + void call(const Json&, T&, priority_tag<0>) const noexcept + { + static_assert(sizeof(Json) == 0, "from_json method in T's namespace can not be called"); + } -public: - template - void operator()(const Json &j, T &val) const - noexcept(noexcept(std::declval().call(j, val, priority_tag<1>{}))) - { - return call(j, val, priority_tag<1>{}); - } + public: + template + void operator()(const Json& j, T& val) const + noexcept(noexcept(std::declval().call(j, val, priority_tag<1> {}))) + { + return call(j, val, priority_tag<1> {}); + } }; // taken from ranges-v3 @@ -830,8 +866,8 @@ struct DecimalSeparator : std::numpunct inline namespace { -constexpr const auto & to_json = detail::static_const::value; -constexpr const auto & from_json = detail::static_const::value; +constexpr const auto& to_json = detail::static_const::value; +constexpr const auto& from_json = detail::static_const::value; } // default JSONSerializer template argument, doesn't care about template argument @@ -846,10 +882,10 @@ struct adl_serializer } template - static void to_json(Json &j, T &&val) noexcept( + static void to_json(Json& j, T&& val) noexcept( noexcept(::nlohmann::to_json(j, std::forward(val)))) { - ::nlohmann::to_json(j, std::forward(val)); + ::nlohmann::to_json(j, std::forward(val)); } }; @@ -947,7 +983,7 @@ class basic_json { private: template <::nlohmann::value_t> friend struct detail::external_constructor; - template friend std::string detail::type_name(const Json &); + template friend std::string detail::type_name(const Json&); /// workaround type for MSVC using basic_json_t = basic_json, enable_if_t::value and - not std::is_same::value and - not detail::is_basic_json_nested_type< - basic_json_t, U>::value and - detail::has_to_json::value, + not std::is_same::value and + not detail::is_basic_json_nested_type< + basic_json_t, U>::value and + detail::has_to_json::value, int> = 0> - basic_json(T &&val) noexcept(noexcept(JSONSerializer::to_json( - std::declval(), std::forward(val)))) + basic_json(T && val) noexcept(noexcept(JSONSerializer::to_json( + std::declval(), std::forward(val)))) { - JSONSerializer::to_json(*this, std::forward(val)); + JSONSerializer::to_json(*this, std::forward(val)); } /*! @@ -2013,7 +2049,7 @@ class basic_json @since version 1.0.0 */ static basic_json array(std::initializer_list init = - std::initializer_list()) + std::initializer_list()) { return basic_json(init, false, value_t::array); } @@ -2053,7 +2089,7 @@ class basic_json @since version 1.0.0 */ static basic_json object(std::initializer_list init = - std::initializer_list()) + std::initializer_list()) { return basic_json(init, false, value_t::object); } @@ -2394,7 +2430,7 @@ class basic_json std::is_nothrow_move_assignable::value and std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value - ) + ) { // check that passed value is valid other.assert_invariant(); @@ -3020,7 +3056,7 @@ class basic_json enable_if_t::value, int> = 0> basic_json get() const { - return *this; + return *this; } /*! @@ -3032,7 +3068,7 @@ class basic_json - @ref json_serializer has a from_json method of the form: void from_json(const @ref basic_json&, U&) - @ref json_serializer does not have a from_json method of the form: U from_json(const @ref basic_json&); - @return a value of type U + @return a value of type U @throw what json_serializer from_json method throws @@ -3040,27 +3076,27 @@ class basic_json */ template < typename T, - typename U = uncvref_t, - enable_if_t< + typename U = uncvref_t, + enable_if_t < not std::is_same::value and - detail::has_from_json::value and - not detail::has_non_default_from_json::value, - int> = 0> + detail::has_from_json::value and + not detail::has_non_default_from_json::value, + int > = 0 > // do we really want the uncvref ? if a user call get, shouldn't we // static assert ? // i know there is a special behaviour for boolean_t* and such auto get() const noexcept(noexcept(JSONSerializer::from_json( - std::declval(), std::declval()))) - -> U + std::declval(), std::declval()))) + -> U { - static_assert(std::is_default_constructible::value and - std::is_copy_constructible::value, - "Types must be DefaultConstructible and " - "CopyConstructible when used with get"); - U ret; - JSONSerializer::from_json(*this, ret); - return ret; + static_assert(std::is_default_constructible::value and + std::is_copy_constructible::value, + "Types must be DefaultConstructible and " + "CopyConstructible when used with get"); + U ret; + JSONSerializer::from_json(*this, ret); + return ret; } /*! @@ -3072,7 +3108,7 @@ class basic_json - U is not @ref basic_json - @ref json_serializer has a from_json method of the form: U from_json(const @ref basic_json&); - @return a value of type U + @return a value of type U @throw what json_serializer from_json method throws @@ -3081,12 +3117,12 @@ class basic_json template < typename T, enable_if_t>::value and - detail::has_non_default_from_json>::value, - int> = 0> - uncvref_t get() const noexcept(noexcept(JSONSerializer::from_json(std::declval()))) + detail::has_non_default_from_json>::value, + int> = 0 > + uncvref_t get() const noexcept(noexcept(JSONSerializer::from_json(std::declval()))) { - return JSONSerializer::from_json(*this); + return JSONSerializer::from_json(*this); } /*! @@ -3168,8 +3204,8 @@ class basic_json { // get the type of the PointerType (remove pointer and const) using pointee_t = typename std::remove_const::type>::type>::type; + std::remove_pointer::type>::type>::type; // make sure the type matches the allowed types static_assert( std::is_same::value @@ -3196,8 +3232,8 @@ class basic_json { // get the type of the PointerType (remove pointer and const) using pointee_t = typename std::remove_const::type>::type>::type; + std::remove_pointer::type>::type>::type; // make sure the type matches the allowed types static_assert( std::is_same::value @@ -4250,14 +4286,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; } @@ -5474,9 +5510,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; } @@ -5546,7 +5582,7 @@ class basic_json std::is_nothrow_move_assignable::value and std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value - ) + ) { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); @@ -7871,7 +7907,10 @@ class basic_json @since version 1.0.0 */ - std::string type_name() const { return detail::type_name(*this); } + std::string type_name() const + { + return detail::type_name(*this); + } private: /*! @@ -8086,13 +8125,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"; @@ -8137,16 +8176,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; @@ -8220,126 +8259,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::denorm_min(); - }; + /// iterator as signed integer type + difference_type m_it = std::numeric_limits::denorm_min(); + }; /*! @brief an iterator value @@ -8359,104 +8398,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 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*() - { - 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: + /// dereference operator (needed for range-based for) + iteration_proxy_internal& operator*() { - return std::to_string(array_index); + return *this; } - // use key from the object - case value_t::object: + /// increment operator (needed for range-based for) + iteration_proxy_internal& operator++() { - return anchor.key(); + ++anchor; + ++array_index; + + return *this; } - // use an empty key for all primitive types - default: + /// inequality operator (needed for range-based for) + bool operator!= (const iteration_proxy_internal& o) const { - return ""; + return anchor != o.anchor; } - } - } - /// return value of the iterator - typename IteratorType::reference value() const - { - return anchor.value(); - } + /// 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(); + } }; - /// 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: @@ -8480,7 +8519,7 @@ class basic_json @since version 1.0.0, simplified in version 2.0.9 */ template - class iter_impl : public std::iterator + class iter_impl : public std::iterator { /// allow basic_json to access private members friend class basic_json; @@ -8490,19 +8529,19 @@ class basic_json or std::is_same::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::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::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; @@ -8516,566 +8555,566 @@ class basic_json @post The iterator is initialized; i.e. `m_object != nullptr`. */ explicit iter_impl(pointer object) noexcept - : m_object(object) - { - assert(m_object != nullptr); - - switch (m_object->m_type) + : m_object(object) { - case basic_json::value_t::object: + assert(m_object != nullptr); + + switch (m_object->m_type) { - 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; - } - - 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::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::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::object: - { - m_it.object_iterator = m_object->m_value.object->begin(); - 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; - } - } - } - - /*! - @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: - { - if (m_it.primitive_iterator.is_begin()) + case basic_json::value_t::object: { - return *m_object; + m_it.object_iterator = typename object_t::iterator(); + break; } - 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 - { - 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; - } - - default: - { - if (m_it.primitive_iterator.is_begin()) + case basic_json::value_t::array: { - return m_object; + 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; + } + + 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::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::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::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + 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; + } + } + } + + /*! + @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")); - } - } - } + } - /*! - @brief post-increment (it++) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator++(int) - { - auto result = *this; - ++(*this); - return result; - } + default: + { + if (m_it.primitive_iterator.is_begin()) + { + return *m_object; + } - /*! - @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; + JSON_THROW(std::out_of_range("cannot get value")); + } } } - 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) + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const { - case basic_json::value_t::object: - { - std::advance(m_it.object_iterator, -1); - break; - } + assert(m_object != nullptr); - case basic_json::value_t::array: + switch (m_object->m_type) { - std::advance(m_it.array_iterator, -1); - break; - } + case basic_json::value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } - default: - { - --m_it.primitive_iterator; - break; + case basic_json::value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + default: + { + if (m_it.primitive_iterator.is_begin()) + { + return m_object; + } + + JSON_THROW(std::out_of_range("cannot get value")); + } } } - 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) + /*! + @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; + } + } + + 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::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; + } + } + + 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) + { 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); - } + assert(m_object != nullptr); - default: + switch (m_object->m_type) { - return (m_it.primitive_iterator == other.m_it.primitive_iterator); + 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) + /*! + @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) - { - case basic_json::value_t::object: + switch (m_object->m_type) { + 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); - } - - 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 - { - 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: - { - 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::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; - } - - 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 - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - 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::null: - { - JSON_THROW(std::out_of_range("cannot get value")); - } - - default: - { - if (m_it.primitive_iterator.get_value() == -n) - { - return *m_object; } - JSON_THROW(std::out_of_range("cannot get value")); + 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 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()) + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const { - return m_it.object_iterator->first; + 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: + { + 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::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; + } + + 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 + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + 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::null: + { + JSON_THROW(std::out_of_range("cannot get value")); + } + + default: + { + if (m_it.primitive_iterator.get_value() == -n) + { + 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 + { + assert(m_object != nullptr); + + if (m_object->is_object()) + { + 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 @@ -9095,9 +9134,9 @@ class basic_json @since version 1.0.0 */ template - class json_reverse_iterator : public std::reverse_iterator + class json_reverse_iterator : public std::reverse_iterator { - public: + public: /// shortcut to the reverse iterator adaptor using base_iterator = std::reverse_iterator; /// the reference type for the pointed-to element @@ -9105,89 +9144,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: @@ -9204,1133 +9243,1611 @@ 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 - }; - - /// 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 an input stream - explicit lexer(std::istream& s) - : m_stream(&s), m_line_buffer() - { - // 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; - - /*! - @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 - */ - 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 - { - 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(codepoint)); - } - else if (codepoint <= 0x7ff) - { - // 2-byte characters: 110xxxxx 10xxxxxx - result.append(1, static_cast(0xC0 | ((codepoint >> 6) & 0x1F))); - result.append(1, static_cast(0x80 | (codepoint & 0x3F))); - } - else if (codepoint <= 0xffff) - { - // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - result.append(1, static_cast(0xE0 | ((codepoint >> 12) & 0x0F))); - result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - result.append(1, static_cast(0x80 | (codepoint & 0x3F))); - } - else if (codepoint <= 0x10ffff) - { - // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - result.append(1, static_cast(0xF0 | ((codepoint >> 18) & 0x07))); - result.append(1, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); - result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - result.append(1, static_cast(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) - { - switch (t) - { - case token_type::uninitialized: - return ""; - 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 ""; - 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[] = { - 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, + 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 }; - 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 <= 0x00) goto basic_json_parser_2; - if (yych <= '!') goto basic_json_parser_4; - goto basic_json_parser_9; - } else { - if (yych <= '+') goto basic_json_parser_4; - if (yych <= ',') goto basic_json_parser_10; - goto basic_json_parser_12; - } - } else { - if (yych <= '9') { - if (yych <= '/') goto basic_json_parser_4; - if (yych <= '0') goto basic_json_parser_13; - goto basic_json_parser_15; - } else { - if (yych <= ':') goto basic_json_parser_17; - if (yych <= 'Z') 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; } -basic_json_parser_4: - ++m_cursor; -basic_json_parser_5: - { 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; } -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; - goto basic_json_parser_5; -basic_json_parser_10: - ++m_cursor; - { last_token_type = token_type::value_separator; break; } -basic_json_parser_12: - 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 <= '/') { - if (yych == '"') goto basic_json_parser_30; - if (yych <= '.') goto basic_json_parser_32; - goto basic_json_parser_30; - } else { - if (yych <= '\\') { - 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; - } - } - } 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 <= 's') { - if (yych <= 'r') goto basic_json_parser_30; - goto basic_json_parser_32; - } else { - if (yych <= 't') goto basic_json_parser_30; - if (yych <= 'u') 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; - goto basic_json_parser_32; -basic_json_parser_37: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= 0x9F) goto basic_json_parser_32; - if (yych <= 0xBF) goto basic_json_parser_36; - goto basic_json_parser_32; -basic_json_parser_38: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= 0x7F) goto basic_json_parser_32; - if (yych <= 0xBF) goto basic_json_parser_36; - goto basic_json_parser_32; -basic_json_parser_39: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= 0x7F) goto basic_json_parser_32; - if (yych <= 0x9F) goto basic_json_parser_36; - goto basic_json_parser_32; -basic_json_parser_40: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= 0x8F) goto basic_json_parser_32; - if (yych <= 0xBF) goto basic_json_parser_38; - goto basic_json_parser_32; -basic_json_parser_41: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= 0x7F) goto basic_json_parser_32; - if (yych <= 0xBF) goto basic_json_parser_38; - goto basic_json_parser_32; -basic_json_parser_42: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= 0x7F) goto basic_json_parser_32; - if (yych <= 0x8F) goto basic_json_parser_38; - goto basic_json_parser_32; -basic_json_parser_43: - yych = *++m_cursor; - if (yych <= '/') goto basic_json_parser_32; - if (yych <= '9') goto basic_json_parser_49; - goto basic_json_parser_32; -basic_json_parser_44: - yych = *++m_cursor; - 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_32; -basic_json_parser_46: - yych = *++m_cursor; - if (yych == 'l') 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 <= '/') 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; - } - } - } + /// the char type to use in the lexer + using lexer_char_t = unsigned char; - 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(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(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()) + /// a lexer from a buffer with given length + lexer(const lexer_char_t* buff, const size_t len) noexcept + : m_content(buff) { - // 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); + assert(m_content != nullptr); + m_start = m_cursor = m_content; + m_limit = m_content + len; + } - // 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) + /// 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()) { - m_line_buffer.append(n - 1, '\x01'); + 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] = ' '; } } - else + + // 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 + */ + static string_t to_unicode(const std::size_t codepoint1, + const std::size_t codepoint2 = 0) { - // 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'); + // calculate the code point from the given code points + std::size_t codepoint = codepoint1; - // 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(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(m_start), - static_cast(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(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) + // check if codepoint1 is a high surrogate + if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF) { - // see https://github.com/nlohmann/json/issues/365#issuecomment-262874705 - for (auto k = i; k < e; k++) + // check if codepoint2 is a low surrogate + if (codepoint2 >= 0xDC00 and codepoint2 <= 0xDFFF) { - result.push_back(static_cast(*k)); - } - i = e - 1; // -1 because of ++i - } - else - { - // processing escaped character - // read next character - ++i; - - switch (*i) - { - // 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(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 - (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 - - 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(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(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(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(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(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(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((std::numeric_limits::max)()) + 1; - curptr++; - } - else - { - type = value_t::number_unsigned; - max = static_cast((std::numeric_limits::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(*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; + 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 { - // no overflow - value = value * 10 + digit; + 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(codepoint)); + } + else if (codepoint <= 0x7ff) + { + // 2-byte characters: 110xxxxx 10xxxxxx + result.append(1, static_cast(0xC0 | ((codepoint >> 6) & 0x1F))); + result.append(1, static_cast(0x80 | (codepoint & 0x3F))); + } + else if (codepoint <= 0xffff) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + result.append(1, static_cast(0xE0 | ((codepoint >> 12) & 0x0F))); + result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + result.append(1, static_cast(0x80 | (codepoint & 0x3F))); + } + else if (codepoint <= 0x10ffff) + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + result.append(1, static_cast(0xF0 | ((codepoint >> 18) & 0x07))); + result.append(1, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + result.append(1, static_cast(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) + { + switch (t) + { + case token_type::uninitialized: + return ""; + 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 ""; + case token_type::end_of_input: + return "end of input"; + default: + { + // catch non-enum values + return "unknown token"; // LCOV_EXCL_LINE } } } - // 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); + /*! + 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. - if (value == max) + @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) { - // we cannot simply negate value (== max == -INT64_MIN), - // see https://github.com/nlohmann/json/issues/389 - result.m_value.number_integer = static_cast(INT64_MIN); + // 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[] = + { + 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 <= 0x00) + { + goto basic_json_parser_2; + } + if (yych <= '!') + { + goto basic_json_parser_4; + } + goto basic_json_parser_9; + } + else + { + if (yych <= '+') + { + goto basic_json_parser_4; + } + if (yych <= ',') + { + goto basic_json_parser_10; + } + goto basic_json_parser_12; + } + } + else + { + if (yych <= '9') + { + if (yych <= '/') + { + goto basic_json_parser_4; + } + if (yych <= '0') + { + goto basic_json_parser_13; + } + goto basic_json_parser_15; + } + else + { + if (yych <= ':') + { + goto basic_json_parser_17; + } + if (yych <= 'Z') + { + 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; + } +basic_json_parser_4: + ++m_cursor; +basic_json_parser_5: + { + 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; + } +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; + } + goto basic_json_parser_5; +basic_json_parser_10: + ++m_cursor; + { + last_token_type = token_type::value_separator; + break; + } +basic_json_parser_12: + 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 <= '/') + { + if (yych == '"') + { + goto basic_json_parser_30; + } + if (yych <= '.') + { + goto basic_json_parser_32; + } + goto basic_json_parser_30; + } + else + { + if (yych <= '\\') + { + 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; + } + } + } + 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 <= 's') + { + if (yych <= 'r') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 't') + { + goto basic_json_parser_30; + } + if (yych <= 'u') + { + 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; + } + goto basic_json_parser_32; +basic_json_parser_37: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x9F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_38: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_39: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0x9F) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_40: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x8F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_41: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_42: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0x8F) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_43: + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_49; + } + goto basic_json_parser_32; +basic_json_parser_44: + yych = *++m_cursor; + 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_32; +basic_json_parser_46: + yych = *++m_cursor; + if (yych == 'l') + { + 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 <= '/') + { + 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(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(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 { - // all other values can be negated safely - result.m_value.number_integer = -static_cast(value); + // 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(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(); } - else + + /// return string representation of last read token + string_t get_token_string() const { - // parse with strtod + assert(m_start != nullptr); + return string_t(reinterpret_cast(m_start), + static_cast(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(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(*k)); + } + i = e - 1; // -1 because of ++i + } + else + { + // processing escaped character + // read next character + ++i; + + switch (*i) + { + // 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(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 + (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 + + 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(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(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(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(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(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(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((std::numeric_limits::max)()) + 1; + curptr++; + } + else + { + type = value_t::number_unsigned; + max = static_cast((std::numeric_limits::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(*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(INT64_MIN); + } + else + { + // all other values can be negated safely + result.m_value.number_integer = -static_cast(value); + } + } + else + { + // parse with strtod result.m_value.number_float = str_to_float_t(static_cast(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; } - // 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; - }; + 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 @@ -10339,282 +10856,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(buff), std::strlen(buff)) - {} + : callback(cb), + m_lexer(reinterpret_cast(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::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(&(*first)), - static_cast(std::distance(first, last))) - {} + /// a parser reading from an iterator range with contiguous storage + template::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(&(*first)), + static_cast(std::distance(first, last))) + {} - /// 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) + /// public parser interface + basic_json parse() { - case lexer::token_type::begin_object: + // 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) { - if (keep and (not callback - or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) + case lexer::token_type::begin_object: { - // explicitly set result to object to cope with {} - result.m_type = value_t::object; - result.m_value = value_t::object; - } + 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; + } - // read next token - get_token(); + // read next token + get_token(); - // closing } -> we are done - if (last_token == 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; + } + + // 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); 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 + case lexer::token_type::begin_array: { - // ugly, but could be fixed with loop reorganization - if (last_token == lexer::token_type::value_separator) + 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; } - // store key - expect(lexer::token_type::value_string); - const auto key = m_lexer.get_string(); + // no comma is expected here + unexpect(lexer::token_type::value_separator); - bool keep_tag = false; - if (keep) + // otherwise: parse values + do { - if (callback) + // ugly, but could be fixed with loop reorganization + if (last_token == lexer::token_type::value_separator) { - basic_json k(key); - keep_tag = callback(depth, parse_event_t::key, k); + get_token(); } - else + + // parse value + auto value = parse_internal(keep); + if (keep and not value.is_discarded()) { - keep_tag = true; + result.push_back(std::move(value)); } } + while (last_token == lexer::token_type::value_separator); - // parse separator (:) + // closing ] + expect(lexer::token_type::end_array); 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); - 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)) + if (keep and 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); - - // otherwise: parse values - do + case lexer::token_type::literal_null: { - // 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); + get_token(); + result.m_type = value_t::null; + break; } - return result; + 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); + } } - case lexer::token_type::literal_null: + if (keep and callback and not callback(depth, parse_event_t::value, result)) { - get_token(); - result.m_type = value_t::null; - break; - } - - 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); + result = basic_json(value_t::discarded); } + return result; } - if (keep and callback and not callback(depth, parse_event_t::value, result)) + /// get next token from lexer + typename lexer::token_type get_token() { - result = basic_json(value_t::discarded); + last_token = m_lexer.scan(); + return last_token; } - 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) + void expect(typename lexer::token_type t) const { - 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); + 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 - { - if (t == last_token) + void unexpect(typename lexer::token_type t) const { - 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)); + 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)); 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: /*! @@ -10633,7 +11150,7 @@ basic_json_parser_66: /// allow basic_json to access private members friend class basic_json; - public: + public: /*! @brief create JSON pointer @@ -10657,573 +11174,573 @@ 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 - { - return std::accumulate(reference_tokens.begin(), - reference_tokens.end(), std::string{}, - [](const std::string & a, const std::string & b) + @since version 2.0.0 + */ + std::string to_string() const noexcept { - return a + "/" + escape(b); - }); - } + return std::accumulate(reference_tokens.begin(), + reference_tokens.end(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + escape(b); + }); + } - /// @copydoc to_string() - operator std::string() const - { - return to_string(); - } - - private: - /// remove and return last reference pointer - std::string pop_back() - { - if (is_root()) + /// @copydoc to_string() + operator std::string() const { + return to_string(); + } + + private: + /// remove and return last reference pointer + std::string pop_back() + { + if (is_root()) + { JSON_THROW(std::domain_error("JSON pointer has no parent")); + } + + auto last = reference_tokens.back(); + reference_tokens.pop_back(); + return last; } - 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(); - } - - json_pointer top() const - { - if (is_root()) + /// return whether pointer points to the root document + bool is_root() const { + return reference_tokens.empty(); + } + + json_pointer top() const + { + 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 - - @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) - { - switch (result->m_type) - { - case value_t::null: - { - 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; - } - - case value_t::object: - { - // 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(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(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(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(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(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 split(const std::string& reference_string) - { - std::vector result; - - // special case: empty reference string -> no reference tokens - if (reference_string.empty()) - { + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; return result; } - // check if nonempty reference string begins with slash - if (reference_string[0] != '/') + /*! + @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) + { + switch (result->m_type) + { + case value_t::null: + { + 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; + } + + case value_t::object: + { + // 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(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(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(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(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(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 split(const std::string& reference_string) + { + std::vector 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 + // 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 + // 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); + { + // 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 + // check reference tokens are properly escaped for (size_t pos = reference_token.find_first_of('~'); - pos != std::string::npos; + 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')) { + 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; + + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); } - case value_t::object: + 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) { - if (value.m_value.object->empty()) + case value_t::array: { - // 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) + if (value.m_value.array->empty()) { - flatten(reference_string + "/" + escape(element.first), - element.second, result); + // 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; } - break; - } - default: - { - // add primitive value with its reference string - result[reference_string] = value; - 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 + /*! + @param[in] value flattened JSON - @return unflattened JSON - */ - static basic_json unflatten(const basic_json& value) - { - if (not value.is_object()) + @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; + 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; } - return result; - } + private: + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } - private: - friend bool operator==(json_pointer const &lhs, - json_pointer const &rhs) noexcept - { - return lhs.reference_tokens == rhs.reference_tokens; - } + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return !(lhs == rhs); + } - friend bool operator!=(json_pointer const &lhs, - json_pointer const &rhs) noexcept - { - return !(lhs == rhs); - } - - /// the reference tokens - std::vector reference_tokens {}; + /// the reference tokens + std::vector reference_tokens {}; }; ////////////////////////// @@ -11899,25 +12416,26 @@ Returns an ordering that is similar to Python: */ inline bool operator<(const value_t lhs, const value_t rhs) noexcept { - static constexpr std::array order = {{ - 0, // null - 3, // object - 4, // array - 5, // string - 1, // boolean - 2, // integer - 2, // unsigned - 2, // float - }}; + static constexpr std::array order = {{ + 0, // null + 3, // object + 4, // array + 5, // string + 1, // boolean + 2, // integer + 2, // unsigned + 2, // float + } + }; - // discarded values are not comparable - if (lhs == value_t::discarded or rhs == value_t::discarded) - { - return false; - } + // discarded values are not comparable + if (lhs == value_t::discarded or rhs == value_t::discarded) + { + return false; + } - return order[static_cast(lhs)] < - order[static_cast(rhs)]; + return order[static_cast(lhs)] < + order[static_cast(rhs)]; } ///////////// @@ -11942,38 +12460,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::value and + is_nothrow_move_assignable::value + ) +{ + j1.swap(j2); +} + +/// hash value for JSON objects +template<> +struct hash { /*! - @brief exchanges the values of two JSON objects + @brief return a hash value for a JSON object @since version 1.0.0 */ - template<> - inline void swap(nlohmann::json& j1, - nlohmann::json& j2) noexcept( - is_nothrow_move_constructible::value and - is_nothrow_move_assignable::value - ) - { - j1.swap(j2); - } - - /// hash value for JSON objects - template<> - struct hash - { - /*! - @brief return a hash value for a JSON object - - @since version 1.0.0 - */ - std::size_t operator()(const nlohmann::json& j) const + std::size_t operator()(const nlohmann::json& j) const { // a naive hashing via the string representation const auto& h = hash(); return h(j.dump()); } - }; +}; } // namespace std /*! diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 8fbff2ad..cbda7ee7 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -136,15 +136,15 @@ the default value for a given type */ enum class value_t : uint8_t { - null, ///< null value - object, ///< object (unordered set of name/value pairs) - array, ///< array (ordered collection of values) - string, ///< string value - boolean, ///< boolean value - number_integer, ///< number value (signed integer) - number_unsigned, ///< number value (unsigned integer) - number_float, ///< number value (floating-point) - discarded ///< discarded by the the parser callback function + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + discarded ///< discarded by the the parser callback function }; // alias templates to reduce boilerplate @@ -168,29 +168,29 @@ using is_unscoped_enum = namespace detail { -template std::string type_name(const Json &j) +template std::string type_name(const Json& j) { - switch (j.m_type) - { - case value_t::null: - return "null"; - case value_t::object: - return "object"; - case value_t::array: - return "array"; - case value_t::string: - return "string"; - case value_t::boolean: - return "boolean"; - case value_t::discarded: - return "discarded"; - default: - return "number"; - } + switch (j.m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::discarded: + return "discarded"; + default: + return "number"; + } } // dispatch utility (taken from ranges-v3) -template struct priority_tag : priority_tag {}; +template struct priority_tag : priority_tag < N - 1 > {}; template <> struct priority_tag<0> {}; @@ -201,120 +201,120 @@ template struct external_constructor; template <> struct external_constructor { - template - static void construct(Json &j, typename Json::boolean_t b) noexcept - { - j.m_type = value_t::boolean; - j.m_value = b; - j.assert_invariant(); - } + template + static void construct(Json& j, typename Json::boolean_t b) noexcept + { + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } }; template <> struct external_constructor { - template - static void construct(Json &j, const typename Json::string_t& s) - { - j.m_type = value_t::string; - j.m_value = s; - j.assert_invariant(); - } + template + static void construct(Json& j, const typename Json::string_t& s) + { + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } }; template <> struct external_constructor { - template - static void construct(Json &j, typename Json::number_float_t val) noexcept - { - // replace infinity and NAN by null - if (not std::isfinite(val)) - j = Json{}; - else + template + static void construct(Json& j, typename Json::number_float_t val) noexcept { - j.m_type = value_t::number_float; - j.m_value = val; + // replace infinity and NAN by null + if (not std::isfinite(val)) + j = Json{}; + else + { + j.m_type = value_t::number_float; + j.m_value = val; + } + j.assert_invariant(); } - j.assert_invariant(); - } }; template <> struct external_constructor { - template - static void construct(Json &j, typename Json::number_unsigned_t val) noexcept - { - j.m_type = value_t::number_unsigned; - j.m_value = val; - j.assert_invariant(); - } + template + static void construct(Json& j, typename Json::number_unsigned_t val) noexcept + { + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } }; template <> struct external_constructor { - template - static void construct(Json &j, typename Json::number_integer_t val) noexcept - { - j.m_type = value_t::number_integer; - j.m_value = val; - j.assert_invariant(); - } + template + static void construct(Json& j, typename Json::number_integer_t val) noexcept + { + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } }; template <> struct external_constructor { - template - static void construct(Json &j, const typename Json::array_t& arr) - { - j.m_type = value_t::array; - j.m_value = arr; - j.assert_invariant(); - } + template + static void construct(Json& j, const typename Json::array_t& arr) + { + j.m_type = value_t::array; + j.m_value = arr; + j.assert_invariant(); + } - template ::value, - int> = 0> - static void construct(Json &j, const CompatibleArrayType &arr) - { - using std::begin; - using std::end; - j.m_type = value_t::array; - j.m_value.array = - j.template create(begin(arr), end(arr)); - j.assert_invariant(); - } + template ::value, + int> = 0> + static void construct(Json& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + j.m_type = value_t::array; + j.m_value.array = + j.template create(begin(arr), end(arr)); + j.assert_invariant(); + } }; template <> struct external_constructor { - template - static void construct(Json &j, const typename Json::object_t& obj) - { - j.m_type = value_t::object; - j.m_value = obj; - j.assert_invariant(); - } + template + static void construct(Json& j, const typename Json::object_t& obj) + { + j.m_type = value_t::object; + j.m_value = obj; + j.assert_invariant(); + } - template ::value, - int> = 0> - static void construct(Json &j, const CompatibleObjectType &obj) - { - using std::begin; - using std::end; + template ::value, + int> = 0> + static void construct(Json& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; - j.m_type = value_t::object; - j.m_value.object = - j.template create(begin(obj), end(obj)); - j.assert_invariant(); - } + j.m_type = value_t::object; + j.m_value.object = + j.template create(begin(obj), end(obj)); + j.assert_invariant(); + } }; // Implementation of 2 C++17 constructs: conjunction, negation. @@ -380,11 +380,11 @@ struct is_compatible_object_type_impl template struct is_compatible_object_type { - static auto constexpr value = is_compatible_object_type_impl< - conjunction>, - has_mapped_type, - has_key_type>::value, - typename BasicJson::object_t, CompatibleObjectType>::value; + static auto constexpr value = is_compatible_object_type_impl < + conjunction>, + has_mapped_type, + has_key_type>::value, + typename BasicJson::object_t, CompatibleObjectType >::value; }; template @@ -400,17 +400,17 @@ struct is_basic_json_nested_type template struct is_compatible_array_type { - // TODO concept Container? - // this might not make VS happy - static auto constexpr value = + // TODO concept Container? + // this might not make VS happy + static auto constexpr value = conjunction>, - negation>, - negation>, - negation>, - has_value_type, - has_iterator>::value; + negation>, + negation>, + negation>, + has_value_type, + has_iterator>::value; }; template @@ -419,7 +419,7 @@ struct is_compatible_integer_type_impl : std::false_type {}; template struct is_compatible_integer_type_impl { - // is there an assert somewhere on overflows? + // is there an assert somewhere on overflows? using RealLimits = std::numeric_limits; using CompatibleLimits = std::numeric_limits; @@ -433,11 +433,11 @@ struct is_compatible_integer_type_impl struct is_compatible_integer_type { - static constexpr auto - value = is_compatible_integer_type_impl < - std::is_integral::value and - not std::is_same::value, - RealIntegerType, CompatibleNumberIntegerType > ::value; + static constexpr auto + value = is_compatible_integer_type_impl < + std::is_integral::value and + not std::is_same::value, + RealIntegerType, CompatibleNumberIntegerType > ::value; }; // This trait checks if JSONSerializer::from_json(json const&, udt&) exists @@ -461,261 +461,287 @@ struct has_from_json template struct has_non_default_from_json { -private: - template < - typename U, - typename = enable_if_t::from_json(std::declval()))>::value>> - static int detect(U &&); - static void detect(...); + private: + template < + typename U, + typename = enable_if_t::from_json(std::declval()))>::value >> + static int detect(U&&); + static void detect(...); -public: - static constexpr bool value = std::is_integral>()))>::value; + public: + static constexpr bool value = std::is_integral>()))>::value; }; // This trait checks if Json::json_serializer::to_json exists template struct has_to_json { -private: - template ::to_json( - std::declval(), std::declval()))> - static int detect(U &&); - static void detect(...); + private: + template ::to_json( + std::declval(), std::declval()))> + static int detect(U&&); + static void detect(...); -public: - static constexpr bool value = std::is_integral>()))>::value; + public: + static constexpr bool value = std::is_integral>()))>::value; }; // overloads for basic_json template parameters template ::value and - not std::is_same::value, + not std::is_same::value, int> = 0> -void get_arithmetic_value(const Json &j, ArithmeticType &val) +void get_arithmetic_value(const Json& j, ArithmeticType& val) { - // unsigned must be checked first, since is_number_integer() == true for unsigned - if (j.is_number_unsigned()) - val = static_cast(*j.template get_ptr()); - else if (j.is_number_integer()) - val = static_cast(*j.template get_ptr()); - else if (j.is_number_float()) - val = static_cast(*j.template get_ptr()); - else - throw std::domain_error("type must be number, but is " + type_name(j)); + // unsigned must be checked first, since is_number_integer() == true for unsigned + if (j.is_number_unsigned()) + { + val = static_cast(*j.template get_ptr()); + } + else if (j.is_number_integer()) + { + val = static_cast(*j.template get_ptr()); + } + else if (j.is_number_float()) + { + val = static_cast(*j.template get_ptr()); + } + else + { + throw std::domain_error("type must be number, but is " + type_name(j)); + } } template -void to_json(Json &j, typename Json::boolean_t b) noexcept +void to_json(Json& j, typename Json::boolean_t b) noexcept { - external_constructor::construct(j, b); + external_constructor::construct(j, b); } template ::value, + CompatibleString>::value, int> = 0> -void to_json(Json &j, const CompatibleString &s) +void to_json(Json& j, const CompatibleString& s) { - external_constructor::construct(j, s); + external_constructor::construct(j, s); } template ::value, int> = 0> -void to_json(Json &j, FloatType val) noexcept +void to_json(Json& j, FloatType val) noexcept { - external_constructor::construct(j, static_cast(val)); + external_constructor::construct(j, static_cast(val)); } template < typename Json, typename CompatibleNumberUnsignedType, - enable_if_t::value, - int> = 0> -void to_json(Json &j, CompatibleNumberUnsignedType val) noexcept + enable_if_t::value, + int> = 0 > +void to_json(Json& j, CompatibleNumberUnsignedType val) noexcept { - external_constructor::construct(j, static_cast(val)); + external_constructor::construct(j, static_cast(val)); } template < typename Json, typename CompatibleNumberIntegerType, - enable_if_t::value, - int> = 0> -void to_json(Json &j, CompatibleNumberIntegerType val) noexcept + enable_if_t::value, + int> = 0 > +void to_json(Json& j, CompatibleNumberIntegerType val) noexcept { - external_constructor::construct(j, static_cast(val)); + external_constructor::construct(j, static_cast(val)); } template ::value, int> = 0> -void to_json(Json &j, UnscopedEnumType e) noexcept +void to_json(Json& j, UnscopedEnumType e) noexcept { - external_constructor::construct(j, e); + external_constructor::construct(j, e); } template < typename Json, typename CompatibleArrayType, - enable_if_t< + enable_if_t < is_compatible_array_type::value or - std::is_same::value, - int> = 0> -void to_json(Json &j, const CompatibleArrayType &arr) + std::is_same::value, + int > = 0 > +void to_json(Json& j, const CompatibleArrayType& arr) { - external_constructor::construct(j, arr); + external_constructor::construct(j, arr); } template < typename Json, typename CompatibleObjectType, enable_if_t::value, - int> = 0> -void to_json(Json &j, const CompatibleObjectType &arr) + int> = 0 > +void to_json(Json& j, const CompatibleObjectType& arr) { - external_constructor::construct(j, arr); + external_constructor::construct(j, arr); } template -void from_json(const Json & j, typename Json::boolean_t& b) +void from_json(const Json& j, typename Json::boolean_t& b) { - if (!j.is_boolean()) - throw std::domain_error("type must be boolean, but is " + type_name(j)); - b = *j.template get_ptr(); + if (!j.is_boolean()) + { + throw std::domain_error("type must be boolean, but is " + type_name(j)); + } + b = *j.template get_ptr(); } template -void from_json(const Json & j, typename Json::string_t& s) +void from_json(const Json& j, typename Json::string_t& s) { - if (!j.is_string()) - throw std::domain_error("type must be string, but is " + type_name(j)); - s = *j.template get_ptr(); + if (!j.is_string()) + { + throw std::domain_error("type must be string, but is " + type_name(j)); + } + s = *j.template get_ptr(); } template -void from_json(const Json & j, typename Json::number_float_t& val) +void from_json(const Json& j, typename Json::number_float_t& val) { - get_arithmetic_value(j, val); + get_arithmetic_value(j, val); } template -void from_json(const Json & j, typename Json::number_unsigned_t& val) +void from_json(const Json& j, typename Json::number_unsigned_t& val) { - get_arithmetic_value(j, val); + get_arithmetic_value(j, val); } template -void from_json(const Json & j, typename Json::number_integer_t& val) +void from_json(const Json& j, typename Json::number_integer_t& val) { - get_arithmetic_value(j, val); + get_arithmetic_value(j, val); } template ::value, int> = 0> -void from_json(const Json &j, UnscopedEnumType& e) +void from_json(const Json& j, UnscopedEnumType& e) { - typename std::underlying_type::type val = e; - get_arithmetic_value(j, val); - e = static_cast(val); + typename std::underlying_type::type val = e; + get_arithmetic_value(j, val); + e = static_cast(val); } template -void from_json(const Json &j, typename Json::array_t &arr) +void from_json(const Json& j, typename Json::array_t& arr) { - if (!j.is_array()) - throw std::domain_error("type must be array, but is " + type_name(j)); - arr = *j.template get_ptr(); + if (!j.is_array()) + { + throw std::domain_error("type must be array, but is " + type_name(j)); + } + arr = *j.template get_ptr(); } // forward_list doesn't have an insert method, TODO find a way to avoid including forward_list template -void from_json(const Json &j, std::forward_list& l) +void from_json(const Json& j, std::forward_list& l) { - // do not perform the check when user wants to retrieve jsons - // (except when it's null.. ?) - if (j.is_null()) - throw std::domain_error("type must be array, but is " + type_name(j)); - if (not std::is_same::value) - { - if (!j.is_array()) - throw std::domain_error("type must be array, but is " + type_name(j)); - } - for (auto it = j.rbegin(), end = j.rend(); it != end; ++it) - l.push_front(it->template get()); + // do not perform the check when user wants to retrieve jsons + // (except when it's null.. ?) + if (j.is_null()) + { + throw std::domain_error("type must be array, but is " + type_name(j)); + } + if (not std::is_same::value) + { + if (!j.is_array()) + { + throw std::domain_error("type must be array, but is " + type_name(j)); + } + } + for (auto it = j.rbegin(), end = j.rend(); it != end; ++it) + { + l.push_front(it->template get()); + } } template -void from_json_array_impl(const Json &j, CompatibleArrayType &arr, priority_tag<0>) +void from_json_array_impl(const Json& j, CompatibleArrayType& arr, priority_tag<0>) { - using std::begin; - using std::end; + using std::begin; + using std::end; - std::transform( - j.begin(), j.end(), std::inserter(arr, end(arr)), [](const Json &i) - { + std::transform( + j.begin(), j.end(), std::inserter(arr, end(arr)), [](const Json & i) + { // get() returns *this, this won't call a from_json method when // value_type is Json return i.template get(); - }); + }); } template -auto from_json_array_impl(const Json &j, CompatibleArrayType &arr, priority_tag<1>) - -> decltype( - arr.reserve(std::declval()), - void()) +auto from_json_array_impl(const Json& j, CompatibleArrayType& arr, priority_tag<1>) +-> decltype( + arr.reserve(std::declval()), + void()) { - using std::begin; - using std::end; + using std::begin; + using std::end; - arr.reserve(j.size()); - std::transform( - j.begin(), j.end(), std::inserter(arr, end(arr)), [](const Json &i) - { + arr.reserve(j.size()); + std::transform( + j.begin(), j.end(), std::inserter(arr, end(arr)), [](const Json & i) + { // get() returns *this, this won't call a from_json method when // value_type is Json return i.template get(); - }); + }); } template < typename Json, typename CompatibleArrayType, enable_if_t::value and - not std::is_same::value, - int> = 0> -void from_json(const Json &j, CompatibleArrayType &arr) + not std::is_same::value, + int> = 0 > +void from_json(const Json& j, CompatibleArrayType& arr) { - if (j.is_null()) - throw std::domain_error("type must be array, but is " + type_name(j)); - // when T == Json, do not check if value_t is correct - if (not std::is_same::value) - { - if (!j.is_array()) - throw std::domain_error("type must be array, but is " + type_name(j)); - } - from_json_array_impl(j, arr, priority_tag<1>{}); + if (j.is_null()) + { + throw std::domain_error("type must be array, but is " + type_name(j)); + } + // when T == Json, do not check if value_t is correct + if (not std::is_same::value) + { + if (!j.is_array()) + { + throw std::domain_error("type must be array, but is " + type_name(j)); + } + } + from_json_array_impl(j, arr, priority_tag<1> {}); } template < typename Json, typename CompatibleObjectType, enable_if_t::value, - int> = 0> -void from_json(const Json &j, CompatibleObjectType &obj) + int> = 0 > +void from_json(const Json& j, CompatibleObjectType& obj) { - if (!j.is_object()) - throw std::domain_error("type must be object, but is " + type_name(j)); + if (!j.is_object()) + { + throw std::domain_error("type must be object, but is " + type_name(j)); + } - auto inner_object = j.template get_ptr(); - using std::begin; - using std::end; - // we could avoid the assignment, but this might require a for loop, which - // might be less efficient than the container constructor for some containers (would it?) - obj = CompatibleObjectType(begin(*inner_object), end(*inner_object)); + auto inner_object = j.template get_ptr(); + using std::begin; + using std::end; + // we could avoid the assignment, but this might require a for loop, which + // might be less efficient than the container constructor for some containers (would it?) + obj = CompatibleObjectType(begin(*inner_object), end(*inner_object)); } // overload for arithmetic types, not chosen for basic_json template arguments (BooleanType, etc..) @@ -724,25 +750,35 @@ void from_json(const Json &j, CompatibleObjectType &obj) // in case of a custom BooleanType which is not an arithmetic type? template < typename Json, typename ArithmeticType, - enable_if_t< + enable_if_t < std::is_arithmetic::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value, - int> = 0> -void from_json(const Json &j, ArithmeticType &val) + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value, + int > = 0 > +void from_json(const Json& j, ArithmeticType& val) { - if (j.is_number_unsigned()) - val = static_cast(*j.template get_ptr()); - else if (j.is_number_integer()) - val = static_cast(*j.template get_ptr()); - else if (j.is_number_float()) - val = static_cast(*j.template get_ptr()); - else if (j.is_boolean()) - val = static_cast(*j.template get_ptr()); - else - throw std::domain_error("type must be number, but is " + type_name(j)); + if (j.is_number_unsigned()) + { + val = static_cast(*j.template get_ptr()); + } + else if (j.is_number_integer()) + { + val = static_cast(*j.template get_ptr()); + } + else if (j.is_number_float()) + { + val = static_cast(*j.template get_ptr()); + } + else if (j.is_boolean()) + { + val = static_cast(*j.template get_ptr()); + } + else + { + throw std::domain_error("type must be number, but is " + type_name(j)); + } } struct to_json_fn @@ -762,39 +798,39 @@ struct to_json_fn static_assert(sizeof(Json) == 0, "to_json method in T's namespace can not be called"); } -public: - template - void operator()(Json &j, T &&val) const - noexcept(noexcept(std::declval().call(j, std::forward(val), priority_tag<1>{}))) - { - return call(j, std::forward(val), priority_tag<1>{}); - } + public: + template + void operator()(Json& j, T&& val) const + noexcept(noexcept(std::declval().call(j, std::forward(val), priority_tag<1> {}))) + { + return call(j, std::forward(val), priority_tag<1> {}); + } }; struct from_json_fn { -private: - template - auto call(const Json &j, T &val, priority_tag<1>) const - noexcept(noexcept(from_json(j, val))) - -> decltype(from_json(j, val), void()) - { - return from_json(j, val); - } + private: + template + auto call(const Json& j, T& val, priority_tag<1>) const + noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val), void()) + { + return from_json(j, val); + } - template - void call(const Json &, T&, priority_tag<0>) const noexcept - { - static_assert(sizeof(Json) == 0, "from_json method in T's namespace can not be called"); - } + template + void call(const Json&, T&, priority_tag<0>) const noexcept + { + static_assert(sizeof(Json) == 0, "from_json method in T's namespace can not be called"); + } -public: - template - void operator()(const Json &j, T &val) const - noexcept(noexcept(std::declval().call(j, val, priority_tag<1>{}))) - { - return call(j, val, priority_tag<1>{}); - } + public: + template + void operator()(const Json& j, T& val) const + noexcept(noexcept(std::declval().call(j, val, priority_tag<1> {}))) + { + return call(j, val, priority_tag<1> {}); + } }; // taken from ranges-v3 @@ -830,8 +866,8 @@ struct DecimalSeparator : std::numpunct inline namespace { -constexpr const auto & to_json = detail::static_const::value; -constexpr const auto & from_json = detail::static_const::value; +constexpr const auto& to_json = detail::static_const::value; +constexpr const auto& from_json = detail::static_const::value; } // default JSONSerializer template argument, doesn't care about template argument @@ -846,10 +882,10 @@ struct adl_serializer } template - static void to_json(Json &j, T &&val) noexcept( + static void to_json(Json& j, T&& val) noexcept( noexcept(::nlohmann::to_json(j, std::forward(val)))) { - ::nlohmann::to_json(j, std::forward(val)); + ::nlohmann::to_json(j, std::forward(val)); } }; @@ -947,7 +983,7 @@ class basic_json { private: template <::nlohmann::value_t> friend struct detail::external_constructor; - template friend std::string detail::type_name(const Json &); + template friend std::string detail::type_name(const Json&); /// workaround type for MSVC using basic_json_t = basic_json, enable_if_t::value and - not std::is_same::value and - not detail::is_basic_json_nested_type< - basic_json_t, U>::value and - detail::has_to_json::value, + not std::is_same::value and + not detail::is_basic_json_nested_type< + basic_json_t, U>::value and + detail::has_to_json::value, int> = 0> - basic_json(T &&val) noexcept(noexcept(JSONSerializer::to_json( - std::declval(), std::forward(val)))) + basic_json(T && val) noexcept(noexcept(JSONSerializer::to_json( + std::declval(), std::forward(val)))) { - JSONSerializer::to_json(*this, std::forward(val)); + JSONSerializer::to_json(*this, std::forward(val)); } /*! @@ -2013,7 +2049,7 @@ class basic_json @since version 1.0.0 */ static basic_json array(std::initializer_list init = - std::initializer_list()) + std::initializer_list()) { return basic_json(init, false, value_t::array); } @@ -2053,7 +2089,7 @@ class basic_json @since version 1.0.0 */ static basic_json object(std::initializer_list init = - std::initializer_list()) + std::initializer_list()) { return basic_json(init, false, value_t::object); } @@ -2394,7 +2430,7 @@ class basic_json std::is_nothrow_move_assignable::value and std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value - ) + ) { // check that passed value is valid other.assert_invariant(); @@ -3020,7 +3056,7 @@ class basic_json enable_if_t::value, int> = 0> basic_json get() const { - return *this; + return *this; } /*! @@ -3032,7 +3068,7 @@ class basic_json - @ref json_serializer has a from_json method of the form: void from_json(const @ref basic_json&, U&) - @ref json_serializer does not have a from_json method of the form: U from_json(const @ref basic_json&); - @return a value of type U + @return a value of type U @throw what json_serializer from_json method throws @@ -3040,27 +3076,27 @@ class basic_json */ template < typename T, - typename U = uncvref_t, - enable_if_t< + typename U = uncvref_t, + enable_if_t < not std::is_same::value and - detail::has_from_json::value and - not detail::has_non_default_from_json::value, - int> = 0> + detail::has_from_json::value and + not detail::has_non_default_from_json::value, + int > = 0 > // do we really want the uncvref ? if a user call get, shouldn't we // static assert ? // i know there is a special behaviour for boolean_t* and such auto get() const noexcept(noexcept(JSONSerializer::from_json( - std::declval(), std::declval()))) - -> U + std::declval(), std::declval()))) + -> U { - static_assert(std::is_default_constructible::value and - std::is_copy_constructible::value, - "Types must be DefaultConstructible and " - "CopyConstructible when used with get"); - U ret; - JSONSerializer::from_json(*this, ret); - return ret; + static_assert(std::is_default_constructible::value and + std::is_copy_constructible::value, + "Types must be DefaultConstructible and " + "CopyConstructible when used with get"); + U ret; + JSONSerializer::from_json(*this, ret); + return ret; } /*! @@ -3072,7 +3108,7 @@ class basic_json - U is not @ref basic_json - @ref json_serializer has a from_json method of the form: U from_json(const @ref basic_json&); - @return a value of type U + @return a value of type U @throw what json_serializer from_json method throws @@ -3081,12 +3117,12 @@ class basic_json template < typename T, enable_if_t>::value and - detail::has_non_default_from_json>::value, - int> = 0> - uncvref_t get() const noexcept(noexcept(JSONSerializer::from_json(std::declval()))) + detail::has_non_default_from_json>::value, + int> = 0 > + uncvref_t get() const noexcept(noexcept(JSONSerializer::from_json(std::declval()))) { - return JSONSerializer::from_json(*this); + return JSONSerializer::from_json(*this); } /*! @@ -3168,8 +3204,8 @@ class basic_json { // get the type of the PointerType (remove pointer and const) using pointee_t = typename std::remove_const::type>::type>::type; + std::remove_pointer::type>::type>::type; // make sure the type matches the allowed types static_assert( std::is_same::value @@ -3196,8 +3232,8 @@ class basic_json { // get the type of the PointerType (remove pointer and const) using pointee_t = typename std::remove_const::type>::type>::type; + std::remove_pointer::type>::type>::type; // make sure the type matches the allowed types static_assert( std::is_same::value @@ -4250,14 +4286,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; } @@ -5474,9 +5510,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; } @@ -5546,7 +5582,7 @@ class basic_json std::is_nothrow_move_assignable::value and std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value - ) + ) { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); @@ -7871,7 +7907,10 @@ class basic_json @since version 1.0.0 */ - std::string type_name() const { return detail::type_name(*this); } + std::string type_name() const + { + return detail::type_name(*this); + } private: /*! @@ -8086,13 +8125,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"; @@ -8137,16 +8176,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; @@ -8220,126 +8259,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::denorm_min(); - }; + /// iterator as signed integer type + difference_type m_it = std::numeric_limits::denorm_min(); + }; /*! @brief an iterator value @@ -8359,104 +8398,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 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*() - { - 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: + /// dereference operator (needed for range-based for) + iteration_proxy_internal& operator*() { - return std::to_string(array_index); + return *this; } - // use key from the object - case value_t::object: + /// increment operator (needed for range-based for) + iteration_proxy_internal& operator++() { - return anchor.key(); + ++anchor; + ++array_index; + + return *this; } - // use an empty key for all primitive types - default: + /// inequality operator (needed for range-based for) + bool operator!= (const iteration_proxy_internal& o) const { - return ""; + return anchor != o.anchor; } - } - } - /// return value of the iterator - typename IteratorType::reference value() const - { - return anchor.value(); - } + /// 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(); + } }; - /// 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: @@ -8480,7 +8519,7 @@ class basic_json @since version 1.0.0, simplified in version 2.0.9 */ template - class iter_impl : public std::iterator + class iter_impl : public std::iterator { /// allow basic_json to access private members friend class basic_json; @@ -8490,19 +8529,19 @@ class basic_json or std::is_same::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::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::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; @@ -8516,566 +8555,566 @@ class basic_json @post The iterator is initialized; i.e. `m_object != nullptr`. */ explicit iter_impl(pointer object) noexcept - : m_object(object) - { - assert(m_object != nullptr); - - switch (m_object->m_type) + : m_object(object) { - case basic_json::value_t::object: + assert(m_object != nullptr); + + switch (m_object->m_type) { - 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; - } - - 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::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::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::object: - { - m_it.object_iterator = m_object->m_value.object->begin(); - 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; - } - } - } - - /*! - @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: - { - if (m_it.primitive_iterator.is_begin()) + case basic_json::value_t::object: { - return *m_object; + m_it.object_iterator = typename object_t::iterator(); + break; } - 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 - { - 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; - } - - default: - { - if (m_it.primitive_iterator.is_begin()) + case basic_json::value_t::array: { - return m_object; + 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; + } + + 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::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::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::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + 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; + } + } + } + + /*! + @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")); - } - } - } + } - /*! - @brief post-increment (it++) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator++(int) - { - auto result = *this; - ++(*this); - return result; - } + default: + { + if (m_it.primitive_iterator.is_begin()) + { + return *m_object; + } - /*! - @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; + JSON_THROW(std::out_of_range("cannot get value")); + } } } - 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) + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const { - case basic_json::value_t::object: - { - std::advance(m_it.object_iterator, -1); - break; - } + assert(m_object != nullptr); - case basic_json::value_t::array: + switch (m_object->m_type) { - std::advance(m_it.array_iterator, -1); - break; - } + case basic_json::value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } - default: - { - --m_it.primitive_iterator; - break; + case basic_json::value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + default: + { + if (m_it.primitive_iterator.is_begin()) + { + return m_object; + } + + JSON_THROW(std::out_of_range("cannot get value")); + } } } - 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) + /*! + @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; + } + } + + 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::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; + } + } + + 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) + { 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); - } + assert(m_object != nullptr); - default: + switch (m_object->m_type) { - return (m_it.primitive_iterator == other.m_it.primitive_iterator); + 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) + /*! + @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) - { - case basic_json::value_t::object: + switch (m_object->m_type) { + 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); - } - - 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 - { - 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: - { - 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::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; - } - - 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 - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - 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::null: - { - JSON_THROW(std::out_of_range("cannot get value")); - } - - default: - { - if (m_it.primitive_iterator.get_value() == -n) - { - return *m_object; } - JSON_THROW(std::out_of_range("cannot get value")); + 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 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()) + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const { - return m_it.object_iterator->first; + 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: + { + 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::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; + } + + 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 + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + 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::null: + { + JSON_THROW(std::out_of_range("cannot get value")); + } + + default: + { + if (m_it.primitive_iterator.get_value() == -n) + { + 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 + { + assert(m_object != nullptr); + + if (m_object->is_object()) + { + 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 @@ -9095,9 +9134,9 @@ class basic_json @since version 1.0.0 */ template - class json_reverse_iterator : public std::reverse_iterator + class json_reverse_iterator : public std::reverse_iterator { - public: + public: /// shortcut to the reverse iterator adaptor using base_iterator = std::reverse_iterator; /// the reference type for the pointed-to element @@ -9105,89 +9144,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: @@ -9204,761 +9243,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 - }; - - /// 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 an input stream - explicit lexer(std::istream& s) - : m_stream(&s), m_line_buffer() - { - // immediately abort if stream is erroneous - if (s.fail()) { + 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; + + /// 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() + { + // 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; - - /*! - @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 - */ - 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 + + // 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") { - JSON_THROW(std::invalid_argument("missing or wrong low surrogate")); + m_line_buffer[0] = ' '; + m_line_buffer[1] = ' '; + m_line_buffer[2] = ' '; } } - string_t result; + // switch off unwanted functions (due to pointer members) + lexer() = delete; + lexer(const lexer&) = delete; + lexer operator=(const lexer&) = delete; - if (codepoint < 0x80) - { - // 1-byte characters: 0xxxxxxx (ASCII) - result.append(1, static_cast(codepoint)); - } - else if (codepoint <= 0x7ff) - { - // 2-byte characters: 110xxxxx 10xxxxxx - result.append(1, static_cast(0xC0 | ((codepoint >> 6) & 0x1F))); - result.append(1, static_cast(0x80 | (codepoint & 0x3F))); - } - else if (codepoint <= 0xffff) - { - // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - result.append(1, static_cast(0xE0 | ((codepoint >> 12) & 0x0F))); - result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - result.append(1, static_cast(0x80 | (codepoint & 0x3F))); - } - else if (codepoint <= 0x10ffff) - { - // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - result.append(1, static_cast(0xF0 | ((codepoint >> 18) & 0x07))); - result.append(1, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); - result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - result.append(1, static_cast(0x80 | (codepoint & 0x3F))); - } - else - { - JSON_THROW(std::out_of_range("code points above 0x10FFFF are invalid")); - } + /*! + @brief create a string from one or two Unicode code points - return result; - } + 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. - /// return name of values of type token_type (only used for errors) - static std::string token_type_name(const token_type t) - { - switch (t) + @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 + */ + static string_t to_unicode(const std::size_t codepoint1, + const std::size_t codepoint2 = 0) { - case token_type::uninitialized: - return ""; - 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 ""; - case token_type::end_of_input: - return "end of input"; - default: + // 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) { - // 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); - - /*!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; } - - // 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; } - - // 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; } - - // 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; - } - - /*! - @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(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(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(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(m_start), - static_cast(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(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++) + // check if codepoint2 is a low surrogate + if (codepoint2 >= 0xDC00 and codepoint2 <= 0xDFFF) { - result.push_back(static_cast(*k)); - } - i = e - 1; // -1 because of ++i - } - else - { - // processing escaped character - // read next character - ++i; - - switch (*i) - { - // 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(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 - (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 - - 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(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(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(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(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(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(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((std::numeric_limits::max)()) + 1; - curptr++; - } - else - { - type = value_t::number_unsigned; - max = static_cast((std::numeric_limits::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(*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; + 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 { - // no overflow - value = value * 10 + digit; + 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(codepoint)); + } + else if (codepoint <= 0x7ff) + { + // 2-byte characters: 110xxxxx 10xxxxxx + result.append(1, static_cast(0xC0 | ((codepoint >> 6) & 0x1F))); + result.append(1, static_cast(0x80 | (codepoint & 0x3F))); + } + else if (codepoint <= 0xffff) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + result.append(1, static_cast(0xE0 | ((codepoint >> 12) & 0x0F))); + result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + result.append(1, static_cast(0x80 | (codepoint & 0x3F))); + } + else if (codepoint <= 0x10ffff) + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + result.append(1, static_cast(0xF0 | ((codepoint >> 18) & 0x07))); + result.append(1, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + result.append(1, static_cast(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) + { + switch (t) + { + case token_type::uninitialized: + return ""; + 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 ""; + case token_type::end_of_input: + return "end of input"; + default: + { + // catch non-enum values + return "unknown token"; // LCOV_EXCL_LINE } } } - // 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); + /*! + 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. - if (value == max) + @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) { - // we cannot simply negate value (== max == -INT64_MIN), - // see https://github.com/nlohmann/json/issues/389 - result.m_value.number_integer = static_cast(INT64_MIN); + // pointer for backtracking information + m_marker = 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_"; + + // 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; } + + // 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; } + + // 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; } + + // anything else is an error + * { last_token_type = token_type::parse_error; break; } + */ + } + + 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(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(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 { - // all other values can be negated safely - result.m_value.number_integer = -static_cast(value); + // 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(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(); } - else + + /// return string representation of last read token + string_t get_token_string() const { - // parse with strtod + assert(m_start != nullptr); + return string_t(reinterpret_cast(m_start), + static_cast(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(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(*k)); + } + i = e - 1; // -1 because of ++i + } + else + { + // processing escaped character + // read next character + ++i; + + switch (*i) + { + // 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(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 + (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 + + 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(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(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(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(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(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(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((std::numeric_limits::max)()) + 1; + curptr++; + } + else + { + type = value_t::number_unsigned; + max = static_cast((std::numeric_limits::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(*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(INT64_MIN); + } + else + { + // all other values can be negated safely + result.m_value.number_integer = -static_cast(value); + } + } + else + { + // parse with strtod result.m_value.number_float = str_to_float_t(static_cast(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; } - // 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; - }; + 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 @@ -9967,282 +10006,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(buff), std::strlen(buff)) - {} + : callback(cb), + m_lexer(reinterpret_cast(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::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(&(*first)), - static_cast(std::distance(first, last))) - {} + /// a parser reading from an iterator range with contiguous storage + template::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(&(*first)), + static_cast(std::distance(first, last))) + {} - /// 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) + /// public parser interface + basic_json parse() { - case lexer::token_type::begin_object: + // 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) { - if (keep and (not callback - or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) + case lexer::token_type::begin_object: { - // explicitly set result to object to cope with {} - result.m_type = value_t::object; - result.m_value = value_t::object; - } + 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; + } - // read next token - get_token(); + // read next token + get_token(); - // closing } -> we are done - if (last_token == 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; + } + + // 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); 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 + case lexer::token_type::begin_array: { - // ugly, but could be fixed with loop reorganization - if (last_token == lexer::token_type::value_separator) + 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; } - // store key - expect(lexer::token_type::value_string); - const auto key = m_lexer.get_string(); + // no comma is expected here + unexpect(lexer::token_type::value_separator); - bool keep_tag = false; - if (keep) + // otherwise: parse values + do { - if (callback) + // ugly, but could be fixed with loop reorganization + if (last_token == lexer::token_type::value_separator) { - basic_json k(key); - keep_tag = callback(depth, parse_event_t::key, k); + get_token(); } - else + + // parse value + auto value = parse_internal(keep); + if (keep and not value.is_discarded()) { - keep_tag = true; + result.push_back(std::move(value)); } } + while (last_token == lexer::token_type::value_separator); - // parse separator (:) + // closing ] + expect(lexer::token_type::end_array); 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); - 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)) + if (keep and 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); - - // otherwise: parse values - do + case lexer::token_type::literal_null: { - // 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); + get_token(); + result.m_type = value_t::null; + break; } - return result; + 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); + } } - case lexer::token_type::literal_null: + if (keep and callback and not callback(depth, parse_event_t::value, result)) { - get_token(); - result.m_type = value_t::null; - break; - } - - 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); + result = basic_json(value_t::discarded); } + return result; } - if (keep and callback and not callback(depth, parse_event_t::value, result)) + /// get next token from lexer + typename lexer::token_type get_token() { - result = basic_json(value_t::discarded); + last_token = m_lexer.scan(); + return last_token; } - 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) + void expect(typename lexer::token_type t) const { - 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); + 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 - { - if (t == last_token) + void unexpect(typename lexer::token_type t) const { - 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)); + 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)); 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: /*! @@ -10261,7 +10300,7 @@ class basic_json /// allow basic_json to access private members friend class basic_json; - public: + public: /*! @brief create JSON pointer @@ -10285,573 +10324,573 @@ 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 - { - return std::accumulate(reference_tokens.begin(), - reference_tokens.end(), std::string{}, - [](const std::string & a, const std::string & b) + @since version 2.0.0 + */ + std::string to_string() const noexcept { - return a + "/" + escape(b); - }); - } + return std::accumulate(reference_tokens.begin(), + reference_tokens.end(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + escape(b); + }); + } - /// @copydoc to_string() - operator std::string() const - { - return to_string(); - } - - private: - /// remove and return last reference pointer - std::string pop_back() - { - if (is_root()) + /// @copydoc to_string() + operator std::string() const { + return to_string(); + } + + private: + /// remove and return last reference pointer + std::string pop_back() + { + if (is_root()) + { JSON_THROW(std::domain_error("JSON pointer has no parent")); + } + + auto last = reference_tokens.back(); + reference_tokens.pop_back(); + return last; } - 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(); - } - - json_pointer top() const - { - if (is_root()) + /// return whether pointer points to the root document + bool is_root() const { + return reference_tokens.empty(); + } + + json_pointer top() const + { + 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 - - @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) - { - switch (result->m_type) - { - case value_t::null: - { - 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; - } - - case value_t::object: - { - // 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(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(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(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(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(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 split(const std::string& reference_string) - { - std::vector result; - - // special case: empty reference string -> no reference tokens - if (reference_string.empty()) - { + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; return result; } - // check if nonempty reference string begins with slash - if (reference_string[0] != '/') + /*! + @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) + { + switch (result->m_type) + { + case value_t::null: + { + 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; + } + + case value_t::object: + { + // 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(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(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(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(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(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 split(const std::string& reference_string) + { + std::vector 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 + // 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 + // 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); + { + // 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 + // check reference tokens are properly escaped for (size_t pos = reference_token.find_first_of('~'); - pos != std::string::npos; + 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')) { + 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; + + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); } - case value_t::object: + 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) { - if (value.m_value.object->empty()) + case value_t::array: { - // 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) + if (value.m_value.array->empty()) { - flatten(reference_string + "/" + escape(element.first), - element.second, result); + // 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; } - break; - } - default: - { - // add primitive value with its reference string - result[reference_string] = value; - 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 + /*! + @param[in] value flattened JSON - @return unflattened JSON - */ - static basic_json unflatten(const basic_json& value) - { - if (not value.is_object()) + @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; + 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; } - return result; - } + private: + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } - private: - friend bool operator==(json_pointer const &lhs, - json_pointer const &rhs) noexcept - { - return lhs.reference_tokens == rhs.reference_tokens; - } + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return !(lhs == rhs); + } - friend bool operator!=(json_pointer const &lhs, - json_pointer const &rhs) noexcept - { - return !(lhs == rhs); - } - - /// the reference tokens - std::vector reference_tokens {}; + /// the reference tokens + std::vector reference_tokens {}; }; ////////////////////////// @@ -11527,25 +11566,26 @@ Returns an ordering that is similar to Python: */ inline bool operator<(const value_t lhs, const value_t rhs) noexcept { - static constexpr std::array order = {{ - 0, // null - 3, // object - 4, // array - 5, // string - 1, // boolean - 2, // integer - 2, // unsigned - 2, // float - }}; + static constexpr std::array order = {{ + 0, // null + 3, // object + 4, // array + 5, // string + 1, // boolean + 2, // integer + 2, // unsigned + 2, // float + } + }; - // discarded values are not comparable - if (lhs == value_t::discarded or rhs == value_t::discarded) - { - return false; - } + // discarded values are not comparable + if (lhs == value_t::discarded or rhs == value_t::discarded) + { + return false; + } - return order[static_cast(lhs)] < - order[static_cast(rhs)]; + return order[static_cast(lhs)] < + order[static_cast(rhs)]; } ///////////// @@ -11570,38 +11610,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::value and + is_nothrow_move_assignable::value + ) +{ + j1.swap(j2); +} + +/// hash value for JSON objects +template<> +struct hash { /*! - @brief exchanges the values of two JSON objects + @brief return a hash value for a JSON object @since version 1.0.0 */ - template<> - inline void swap(nlohmann::json& j1, - nlohmann::json& j2) noexcept( - is_nothrow_move_constructible::value and - is_nothrow_move_assignable::value - ) - { - j1.swap(j2); - } - - /// hash value for JSON objects - template<> - struct hash - { - /*! - @brief return a hash value for a JSON object - - @since version 1.0.0 - */ - std::size_t operator()(const nlohmann::json& j) const + std::size_t operator()(const nlohmann::json& j) const { // a naive hashing via the string representation const auto& h = hash(); return h(j.dump()); } - }; +}; } // namespace std /*! diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index 73b686d8..3c4d8bef 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -177,10 +177,10 @@ TEST_CASE("value conversion") SECTION("reserve is called on containers that supports it") { - // making the call to from_json throw in order to check capacity - std::vector v; - CHECK_THROWS_AS(nlohmann::from_json(j, v), std::logic_error); - CHECK(v.capacity() == j.size()); + // making the call to from_json throw in order to check capacity + std::vector v; + CHECK_THROWS_AS(nlohmann::from_json(j, v), std::logic_error); + CHECK(v.capacity() == j.size()); } } diff --git a/test/src/unit-noexcept.cpp b/test/src/unit-noexcept.cpp index de13e057..b939db4e 100644 --- a/test/src/unit-noexcept.cpp +++ b/test/src/unit-noexcept.cpp @@ -11,8 +11,8 @@ enum test struct pod {}; struct pod_bis {}; -void to_json(json &, pod) noexcept; -void to_json(json &, pod_bis); +void to_json(json&, pod) noexcept; +void to_json(json&, pod_bis); void from_json(const json&, pod) noexcept; void from_json(const json&, pod_bis); static json j; diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 07055bc1..6a5e0c64 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -63,7 +63,7 @@ TEST_CASE("regression tests") SECTION("pull request #71 - handle enum type") { - enum { t = 0 , u = 1}; + enum { t = 0, u = 1}; json j = json::array(); j.push_back(t); diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index 0590a8e5..35f86440 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -114,22 +114,22 @@ void to_json(Json& j, country c) } template -void to_json(Json& j, const person & p) +void to_json(Json& j, const person& p) { j = Json{{"age", p.m_age}, {"name", p.m_name}, {"country", p.m_country}}; } -void to_json(nlohmann::json& j, const address & a) +void to_json(nlohmann::json& j, const address& a) { j = a.m_val; } -void to_json(nlohmann::json& j, const contact & c) +void to_json(nlohmann::json& j, const contact& c) { j = json{{"person", c.m_person}, {"address", c.m_address}}; } -void to_json(nlohmann::json& j, const contact_book & cb) +void to_json(nlohmann::json& j, const contact_book& cb) { j = json{{"name", cb.m_book_name}, {"contacts", cb.m_contacts}}; } @@ -140,28 +140,28 @@ bool operator==(age lhs, age rhs) return lhs.m_val == rhs.m_val; } -bool operator==(const address & lhs, const address & rhs) +bool operator==(const address& lhs, const address& rhs) { return lhs.m_val == rhs.m_val; } -bool operator==(const name & lhs, const name & rhs) +bool operator==(const name& lhs, const name& rhs) { return lhs.m_val == rhs.m_val; } -bool operator==(const person & lhs, const person & rhs) +bool operator==(const person& lhs, const person& rhs) { return std::tie(lhs.m_name, lhs.m_age) == std::tie(rhs.m_name, rhs.m_age); } -bool operator==(const contact & lhs, const contact & rhs) +bool operator==(const contact& lhs, const contact& rhs) { return std::tie(lhs.m_person, lhs.m_address) == std::tie(rhs.m_person, rhs.m_address); } -bool operator==(const contact_book & lhs, const contact_book & rhs) +bool operator==(const contact_book& lhs, const contact_book& rhs) { return std::tie(lhs.m_book_name, lhs.m_contacts) == std::tie(rhs.m_book_name, rhs.m_contacts); @@ -172,19 +172,19 @@ bool operator==(const contact_book & lhs, const contact_book & rhs) namespace udt { template -void from_json(const Json & j, age& a) +void from_json(const Json& j, age& a) { a.m_val = j.template get(); } template -void from_json(const Json & j, name& n) +void from_json(const Json& j, name& n) { n.m_val = j.template get(); } template -void from_json(const Json & j, country& c) +void from_json(const Json& j, country& c) { const auto str = j.template get(); static const std::map m = @@ -200,25 +200,25 @@ void from_json(const Json & j, country& c) } template -void from_json(const Json & j, person& p) +void from_json(const Json& j, person& p) { p.m_age = j["age"].template get(); p.m_name = j["name"].template get(); p.m_country = j["country"].template get(); } -void from_json(const nlohmann::json & j, address& a) +void from_json(const nlohmann::json& j, address& a) { a.m_val = j.get(); } -void from_json(const nlohmann::json & j, contact& c) +void from_json(const nlohmann::json& j, contact& c) { c.m_person = j["person"].get(); c.m_address = j["address"].get
(); } -void from_json(const nlohmann::json & j, contact_book& cb) +void from_json(const nlohmann::json& j, contact_book& cb) { cb.m_book_name = j["name"].get(); cb.m_contacts = j["contacts"].get>(); @@ -297,7 +297,7 @@ namespace nlohmann template struct adl_serializer> { - static void to_json(json& j, const std::shared_ptr & opt) + static void to_json(json& j, const std::shared_ptr& opt) { if (opt) { @@ -309,7 +309,7 @@ struct adl_serializer> } } - static void from_json(const json & j, std::shared_ptr& opt) + static void from_json(const json& j, std::shared_ptr& opt) { if (j.is_null()) { @@ -325,12 +325,12 @@ struct adl_serializer> template <> struct adl_serializer { - static void to_json(json& j, const udt::legacy_type & l) + static void to_json(json& j, const udt::legacy_type& l) { j = std::stoi(l.number); } - static void from_json(const json & j, udt::legacy_type& l) + static void from_json(const json& j, udt::legacy_type& l) { l.number = std::to_string(j.get()); } @@ -394,21 +394,21 @@ namespace nlohmann template <> struct adl_serializer> { - using type = std::vector; - static void to_json(json& j, const type &) + using type = std::vector; + static void to_json(json& j, const type&) { - j = "hijacked!"; + j = "hijacked!"; } - static void from_json(const json &, type& opt) + static void from_json(const json&, type& opt) { - opt = {42.0, 42.0, 42.0}; + opt = {42.0, 42.0, 42.0}; } // preferred version - static type from_json(const json &) + static type from_json(const json&) { - return {4.0, 5.0, 6.0}; + return {4.0, 5.0, 6.0}; } }; } @@ -419,7 +419,7 @@ TEST_CASE("even supported types can be specialized", "[udt]") CHECK(j.dump() == R"("hijacked!")"); auto f = j.get>(); // the single argument from_json method is preferred - CHECK((f == std::vector{4.0, 5.0, 6.0})); + CHECK((f == std::vector {4.0, 5.0, 6.0})); } namespace nlohmann @@ -427,7 +427,7 @@ namespace nlohmann template struct adl_serializer> { - static void to_json(json& j, const std::unique_ptr & opt) + static void to_json(json& j, const std::unique_ptr& opt) { if (opt) { @@ -440,7 +440,7 @@ struct adl_serializer> } // this is the overload needed for non-copyable types, - static std::unique_ptr from_json(const json & j) + static std::unique_ptr from_json(const json& j) { if (j.is_null()) { @@ -491,64 +491,64 @@ TEST_CASE("Non-copyable types", "[udt]") template struct pod_serializer { - // use adl for non-pods, or scalar types - template < - typename Json, typename U = T, - typename std::enable_if< - not(std::is_pod::value and std::is_class::value), int>::type = 0> - static void from_json(const Json &j, U &t) - { - using nlohmann::from_json; - from_json(j, t); - } + // use adl for non-pods, or scalar types + template < + typename Json, typename U = T, + typename std::enable_if < + not(std::is_pod::value and std::is_class::value), int >::type = 0 > + static void from_json(const Json& j, U& t) + { + using nlohmann::from_json; + from_json(j, t); + } - // special behaviour for pods - template ::value and std::is_class::value, int>::type = 0> - static void from_json(const Json &j, U &t) - { - std::uint64_t value; - // TODO The following block is no longer relevant in this serializer, make another one that shows the issue - // the problem arises only when one from_json method is defined without any constraint - // - // Why cannot we simply use: j.get() ? - // Well, with the current experiment, the get method looks for a from_json - // function, which we are currently defining! - // This would end up in a stack overflow. Calling nlohmann::from_json is a - // workaround (is it?). - // I shall find a good way to avoid this once all constructors are converted - // to free methods - // - // In short, constructing a json by constructor calls to_json - // calling get calls from_json, for now, we cannot do this in custom - // serializers - nlohmann::from_json(j, value); - auto bytes = static_cast(static_cast(&value)); - std::memcpy(&t, bytes, sizeof(value)); - } + // special behaviour for pods + template ::value and std::is_class::value, int>::type = 0> + static void from_json(const Json& j, U& t) + { + std::uint64_t value; + // TODO The following block is no longer relevant in this serializer, make another one that shows the issue + // the problem arises only when one from_json method is defined without any constraint + // + // Why cannot we simply use: j.get() ? + // Well, with the current experiment, the get method looks for a from_json + // function, which we are currently defining! + // This would end up in a stack overflow. Calling nlohmann::from_json is a + // workaround (is it?). + // I shall find a good way to avoid this once all constructors are converted + // to free methods + // + // In short, constructing a json by constructor calls to_json + // calling get calls from_json, for now, we cannot do this in custom + // serializers + nlohmann::from_json(j, value); + auto bytes = static_cast(static_cast(&value)); + std::memcpy(&t, bytes, sizeof(value)); + } - template < - typename Json, typename U = T, - typename std::enable_if< - not(std::is_pod::value and std::is_class::value), int>::type = 0> - static void to_json(Json &j, const T &t) - { - using nlohmann::to_json; - to_json(j, t); - } + template < + typename Json, typename U = T, + typename std::enable_if < + not(std::is_pod::value and std::is_class::value), int >::type = 0 > + static void to_json(Json& j, const T& t) + { + using nlohmann::to_json; + to_json(j, t); + } - template ::value and std::is_class::value, int>::type = 0> - static void to_json(Json &j, const T &t) noexcept - { - auto bytes = static_cast< const unsigned char*>(static_cast(&t)); - std::uint64_t value = bytes[0]; - for (auto i = 1; i < 8; ++i) - value |= std::uint64_t{bytes[i]} << 8 * i; - nlohmann::to_json(j, value); - } + template ::value and std::is_class::value, int>::type = 0> + static void to_json(Json& j, const T& t) noexcept + { + auto bytes = static_cast< const unsigned char*>(static_cast(&t)); + std::uint64_t value = bytes[0]; + for (auto i = 1; i < 8; ++i) + value |= std::uint64_t{bytes[i]} << 8 * i; + nlohmann::to_json(j, value); + } }; namespace udt @@ -562,19 +562,19 @@ struct small_pod struct non_pod { - std::string s; + std::string s; }; template -void to_json(Json& j, const non_pod & np) +void to_json(Json& j, const non_pod& np) { - j = np.s; + j = np.s; } template -void from_json(const Json & j, non_pod& np) +void from_json(const Json& j, non_pod& np) { - np.s = j.template get(); + np.s = j.template get(); } bool operator==(small_pod lhs, small_pod rhs) noexcept @@ -583,35 +583,35 @@ bool operator==(small_pod lhs, small_pod rhs) noexcept std::tie(rhs.begin, rhs.middle, rhs.end); } -bool operator==(const non_pod &lhs, const non_pod &rhs) noexcept +bool operator==(const non_pod& lhs, const non_pod& rhs) noexcept { - return lhs.s == rhs.s; + return lhs.s == rhs.s; } std::ostream& operator<<(std::ostream& os, small_pod l) { - return os << "begin: " << l.begin << ", middle: " << l.middle << ", end: " << l.end; + return os << "begin: " << l.begin << ", middle: " << l.middle << ", end: " << l.end; } } TEST_CASE("custom serializer for pods", "[udt]") { - using custom_json = - nlohmann::basic_json; + using custom_json = + nlohmann::basic_json; - 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(); + auto p2 = j.get(); - CHECK(p == p2); + CHECK(p == p2); - auto np = udt::non_pod{{"non-pod"}}; - custom_json j2 = np; - auto np2 = j2.get(); - CHECK(np == np2); + auto np = udt::non_pod{{"non-pod"}}; + custom_json j2 = np; + auto np2 = j2.get(); + CHECK(np == np2); } template @@ -622,13 +622,13 @@ using custom_json = nlohmann::basic_json struct another_adl_serializer { - static void from_json(const custom_json & j , T& t) + static void from_json(const custom_json& j, T& t) { using nlohmann::from_json; from_json(j, t); } - static void to_json(custom_json& j , const T & t) + static void to_json(custom_json& j, const T& t) { using nlohmann::to_json; to_json(j, t);