989 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
		
		
			
		
	
	
			989 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
|  | //     __ _____ _____ _____
 | ||
|  | //  __|  |   __|     |   | |  JSON for Modern C++
 | ||
|  | // |  |  |__   |  |  | | | |  version 3.11.3
 | ||
|  | // |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 | ||
|  | //
 | ||
|  | // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
 | ||
|  | // SPDX-License-Identifier: MIT
 | ||
|  | 
 | ||
|  | #pragma once
 | ||
|  | 
 | ||
|  | #include <algorithm> // all_of
 | ||
|  | #include <cctype> // isdigit
 | ||
|  | #include <cerrno> // errno, ERANGE
 | ||
|  | #include <cstdlib> // strtoull
 | ||
|  | #ifndef JSON_NO_IO
 | ||
|  |     #include <iosfwd> // ostream
 | ||
|  | #endif  // JSON_NO_IO
 | ||
|  | #include <limits> // max
 | ||
|  | #include <numeric> // accumulate
 | ||
|  | #include <string> // string
 | ||
|  | #include <utility> // move
 | ||
|  | #include <vector> // vector
 | ||
|  | 
 | ||
|  | #include <nlohmann/detail/exceptions.hpp>
 | ||
|  | #include <nlohmann/detail/macro_scope.hpp>
 | ||
|  | #include <nlohmann/detail/string_concat.hpp>
 | ||
|  | #include <nlohmann/detail/string_escape.hpp>
 | ||
|  | #include <nlohmann/detail/value_t.hpp>
 | ||
|  | 
 | ||
|  | NLOHMANN_JSON_NAMESPACE_BEGIN | ||
|  | 
 | ||
|  | /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
 | ||
|  | /// @sa https://json.nlohmann.me/api/json_pointer/
 | ||
|  | template<typename RefStringType> | ||
|  | class json_pointer | ||
|  | { | ||
|  |     // allow basic_json to access private members
 | ||
|  |     NLOHMANN_BASIC_JSON_TPL_DECLARATION | ||
|  |     friend class basic_json; | ||
|  | 
 | ||
|  |     template<typename> | ||
|  |     friend class json_pointer; | ||
|  | 
 | ||
|  |     template<typename T> | ||
|  |     struct string_t_helper | ||
|  |     { | ||
|  |         using type = T; | ||
|  |     }; | ||
|  | 
 | ||
|  |     NLOHMANN_BASIC_JSON_TPL_DECLARATION | ||
|  |     struct string_t_helper<NLOHMANN_BASIC_JSON_TPL> | ||
|  |     { | ||
|  |         using type = StringType; | ||
|  |     }; | ||
|  | 
 | ||
|  |   public: | ||
|  |     // for backwards compatibility accept BasicJsonType
 | ||
|  |     using string_t = typename string_t_helper<RefStringType>::type; | ||
|  | 
 | ||
|  |     /// @brief create JSON pointer
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/
 | ||
|  |     explicit json_pointer(const string_t& s = "") | ||
|  |         : reference_tokens(split(s)) | ||
|  |     {} | ||
|  | 
 | ||
|  |     /// @brief return a string representation of the JSON pointer
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/to_string/
 | ||
|  |     string_t to_string() const | ||
|  |     { | ||
|  |         return std::accumulate(reference_tokens.begin(), reference_tokens.end(), | ||
|  |                                string_t{}, | ||
|  |                                [](const string_t& a, const string_t& b) | ||
|  |         { | ||
|  |             return detail::concat(a, '/', detail::escape(b)); | ||
|  |         }); | ||
|  |     } | ||
|  | 
 | ||
|  |     /// @brief return a string representation of the JSON pointer
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/
 | ||
|  |     JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string()) | ||
|  |     operator string_t() const | ||
|  |     { | ||
|  |         return to_string(); | ||
|  |     } | ||
|  | 
 | ||
|  | #ifndef JSON_NO_IO
 | ||
|  |     /// @brief write string representation of the JSON pointer to stream
 | ||
|  |     /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
 | ||
|  |     friend std::ostream& operator<<(std::ostream& o, const json_pointer& ptr) | ||
|  |     { | ||
|  |         o << ptr.to_string(); | ||
|  |         return o; | ||
|  |     } | ||
|  | #endif
 | ||
|  | 
 | ||
|  |     /// @brief append another JSON pointer at the end of this JSON pointer
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
 | ||
|  |     json_pointer& operator/=(const json_pointer& ptr) | ||
|  |     { | ||
|  |         reference_tokens.insert(reference_tokens.end(), | ||
|  |                                 ptr.reference_tokens.begin(), | ||
|  |                                 ptr.reference_tokens.end()); | ||
|  |         return *this; | ||
|  |     } | ||
|  | 
 | ||
|  |     /// @brief append an unescaped reference token at the end of this JSON pointer
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
 | ||
|  |     json_pointer& operator/=(string_t token) | ||
|  |     { | ||
|  |         push_back(std::move(token)); | ||
|  |         return *this; | ||
|  |     } | ||
|  | 
 | ||
|  |     /// @brief append an array index at the end of this JSON pointer
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
 | ||
|  |     json_pointer& operator/=(std::size_t array_idx) | ||
|  |     { | ||
|  |         return *this /= std::to_string(array_idx); | ||
|  |     } | ||
|  | 
 | ||
|  |     /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
 | ||
|  |     friend json_pointer operator/(const json_pointer& lhs, | ||
|  |                                   const json_pointer& rhs) | ||
|  |     { | ||
|  |         return json_pointer(lhs) /= rhs; | ||
|  |     } | ||
|  | 
 | ||
|  |     /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
 | ||
|  |     friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param)
 | ||
|  |     { | ||
|  |         return json_pointer(lhs) /= std::move(token); | ||
|  |     } | ||
|  | 
 | ||
|  |     /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
 | ||
|  |     friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx) | ||
|  |     { | ||
|  |         return json_pointer(lhs) /= array_idx; | ||
|  |     } | ||
|  | 
 | ||
|  |     /// @brief returns the parent of this JSON pointer
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/
 | ||
|  |     json_pointer parent_pointer() const | ||
|  |     { | ||
|  |         if (empty()) | ||
|  |         { | ||
|  |             return *this; | ||
|  |         } | ||
|  | 
 | ||
|  |         json_pointer res = *this; | ||
|  |         res.pop_back(); | ||
|  |         return res; | ||
|  |     } | ||
|  | 
 | ||
|  |     /// @brief remove last reference token
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/
 | ||
|  |     void pop_back() | ||
|  |     { | ||
|  |         if (JSON_HEDLEY_UNLIKELY(empty())) | ||
|  |         { | ||
|  |             JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); | ||
|  |         } | ||
|  | 
 | ||
|  |         reference_tokens.pop_back(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /// @brief return last reference token
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/back/
 | ||
|  |     const string_t& back() const | ||
|  |     { | ||
|  |         if (JSON_HEDLEY_UNLIKELY(empty())) | ||
|  |         { | ||
|  |             JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); | ||
|  |         } | ||
|  | 
 | ||
|  |         return reference_tokens.back(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /// @brief append an unescaped token at the end of the reference pointer
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/push_back/
 | ||
|  |     void push_back(const string_t& token) | ||
|  |     { | ||
|  |         reference_tokens.push_back(token); | ||
|  |     } | ||
|  | 
 | ||
|  |     /// @brief append an unescaped token at the end of the reference pointer
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/push_back/
 | ||
|  |     void push_back(string_t&& token) | ||
|  |     { | ||
|  |         reference_tokens.push_back(std::move(token)); | ||
|  |     } | ||
|  | 
 | ||
|  |     /// @brief return whether pointer points to the root document
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/empty/
 | ||
|  |     bool empty() const noexcept | ||
|  |     { | ||
|  |         return reference_tokens.empty(); | ||
|  |     } | ||
|  | 
 | ||
|  |   private: | ||
|  |     /*!
 | ||
|  |     @param[in] s  reference token to be converted into an array index | ||
|  | 
 | ||
|  |     @return integer representation of @a s | ||
|  | 
 | ||
|  |     @throw parse_error.106  if an array index begins with '0' | ||
|  |     @throw parse_error.109  if an array index begins not with a digit | ||
|  |     @throw out_of_range.404 if string @a s could not be converted to an integer | ||
|  |     @throw out_of_range.410 if an array index exceeds size_type | ||
|  |     */ | ||
|  |     template<typename BasicJsonType> | ||
|  |     static typename BasicJsonType::size_type array_index(const string_t& s) | ||
|  |     { | ||
|  |         using size_type = typename BasicJsonType::size_type; | ||
|  | 
 | ||
|  |         // error condition (cf. RFC 6901, Sect. 4)
 | ||
|  |         if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) | ||
|  |         { | ||
|  |             JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr)); | ||
|  |         } | ||
|  | 
 | ||
|  |         // error condition (cf. RFC 6901, Sect. 4)
 | ||
|  |         if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) | ||
|  |         { | ||
|  |             JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr)); | ||
|  |         } | ||
|  | 
 | ||
|  |         const char* p = s.c_str(); | ||
|  |         char* p_end = nullptr; | ||
|  |         errno = 0; // strtoull doesn't reset errno
 | ||
|  |         const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)
 | ||
|  |         if (p == p_end // invalid input or empty string
 | ||
|  |                 || errno == ERANGE // out of range
 | ||
|  |                 || JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read
 | ||
|  |         { | ||
|  |             JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr)); | ||
|  |         } | ||
|  | 
 | ||
|  |         // only triggered on special platforms (like 32bit), see also
 | ||
|  |         // https://github.com/nlohmann/json/pull/2203
 | ||
|  |         if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)()))  // NOLINT(runtime/int)
 | ||
|  |         { | ||
|  |             JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr));   // LCOV_EXCL_LINE
 | ||
|  |         } | ||
|  | 
 | ||
|  |         return static_cast<size_type>(res); | ||
|  |     } | ||
|  | 
 | ||
|  |   JSON_PRIVATE_UNLESS_TESTED: | ||
|  |     json_pointer top() const | ||
|  |     { | ||
|  |         if (JSON_HEDLEY_UNLIKELY(empty())) | ||
|  |         { | ||
|  |             JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); | ||
|  |         } | ||
|  | 
 | ||
|  |         json_pointer result = *this; | ||
|  |         result.reference_tokens = {reference_tokens[0]}; | ||
|  |         return result; | ||
|  |     } | ||
|  | 
 | ||
|  |   private: | ||
|  |     /*!
 | ||
|  |     @brief create and return a reference to the pointed to value | ||
|  | 
 | ||
|  |     @complexity Linear in the number of reference tokens. | ||
|  | 
 | ||
|  |     @throw parse_error.109 if array index is not a number | ||
|  |     @throw type_error.313 if value cannot be unflattened | ||
|  |     */ | ||
|  |     template<typename BasicJsonType> | ||
|  |     BasicJsonType& get_and_create(BasicJsonType& j) const | ||
|  |     { | ||
|  |         auto* result = &j; | ||
|  | 
 | ||
|  |         // in case no reference tokens exist, return a reference to the JSON value
 | ||
|  |         // j which will be overwritten by a primitive value
 | ||
|  |         for (const auto& reference_token : reference_tokens) | ||
|  |         { | ||
|  |             switch (result->type()) | ||
|  |             { | ||
|  |                 case detail::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 detail::value_t::object: | ||
|  |                 { | ||
|  |                     // create an entry in the object
 | ||
|  |                     result = &result->operator[](reference_token); | ||
|  |                     break; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 case detail::value_t::array: | ||
|  |                 { | ||
|  |                     // create an entry in the array
 | ||
|  |                     result = &result->operator[](array_index<BasicJsonType>(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. | ||
|  |                 */ | ||
|  |                 case detail::value_t::string: | ||
|  |                 case detail::value_t::boolean: | ||
|  |                 case detail::value_t::number_integer: | ||
|  |                 case detail::value_t::number_unsigned: | ||
|  |                 case detail::value_t::number_float: | ||
|  |                 case detail::value_t::binary: | ||
|  |                 case detail::value_t::discarded: | ||
|  |                 default: | ||
|  |                     JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j)); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         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 parse_error.106   if an array index begins with '0' | ||
|  |     @throw parse_error.109   if an array index was not a number | ||
|  |     @throw out_of_range.404  if the JSON pointer can not be resolved | ||
|  |     */ | ||
|  |     template<typename BasicJsonType> | ||
|  |     BasicJsonType& get_unchecked(BasicJsonType* ptr) const | ||
|  |     { | ||
|  |         for (const auto& reference_token : reference_tokens) | ||
|  |         { | ||
|  |             // convert null values to arrays or objects before continuing
 | ||
|  |             if (ptr->is_null()) | ||
|  |             { | ||
|  |                 // check if reference token is a number
 | ||
|  |                 const bool nums = | ||
|  |                     std::all_of(reference_token.begin(), reference_token.end(), | ||
|  |                                 [](const unsigned char x) | ||
|  |                 { | ||
|  |                     return std::isdigit(x); | ||
|  |                 }); | ||
|  | 
 | ||
|  |                 // change value to array for numbers or "-" or to object otherwise
 | ||
|  |                 *ptr = (nums || reference_token == "-") | ||
|  |                        ? detail::value_t::array | ||
|  |                        : detail::value_t::object; | ||
|  |             } | ||
|  | 
 | ||
|  |             switch (ptr->type()) | ||
|  |             { | ||
|  |                 case detail::value_t::object: | ||
|  |                 { | ||
|  |                     // use unchecked object access
 | ||
|  |                     ptr = &ptr->operator[](reference_token); | ||
|  |                     break; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 case detail::value_t::array: | ||
|  |                 { | ||
|  |                     if (reference_token == "-") | ||
|  |                     { | ||
|  |                         // explicitly treat "-" as index beyond the end
 | ||
|  |                         ptr = &ptr->operator[](ptr->m_data.m_value.array->size()); | ||
|  |                     } | ||
|  |                     else | ||
|  |                     { | ||
|  |                         // convert array index to number; unchecked access
 | ||
|  |                         ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token)); | ||
|  |                     } | ||
|  |                     break; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 case detail::value_t::null: | ||
|  |                 case detail::value_t::string: | ||
|  |                 case detail::value_t::boolean: | ||
|  |                 case detail::value_t::number_integer: | ||
|  |                 case detail::value_t::number_unsigned: | ||
|  |                 case detail::value_t::number_float: | ||
|  |                 case detail::value_t::binary: | ||
|  |                 case detail::value_t::discarded: | ||
|  |                 default: | ||
|  |                     JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         return *ptr; | ||
|  |     } | ||
|  | 
 | ||
|  |     /*!
 | ||
|  |     @throw parse_error.106   if an array index begins with '0' | ||
|  |     @throw parse_error.109   if an array index was not a number | ||
|  |     @throw out_of_range.402  if the array index '-' is used | ||
|  |     @throw out_of_range.404  if the JSON pointer can not be resolved | ||
|  |     */ | ||
|  |     template<typename BasicJsonType> | ||
|  |     BasicJsonType& get_checked(BasicJsonType* ptr) const | ||
|  |     { | ||
|  |         for (const auto& reference_token : reference_tokens) | ||
|  |         { | ||
|  |             switch (ptr->type()) | ||
|  |             { | ||
|  |                 case detail::value_t::object: | ||
|  |                 { | ||
|  |                     // note: at performs range check
 | ||
|  |                     ptr = &ptr->at(reference_token); | ||
|  |                     break; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 case detail::value_t::array: | ||
|  |                 { | ||
|  |                     if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) | ||
|  |                     { | ||
|  |                         // "-" always fails the range check
 | ||
|  |                         JSON_THROW(detail::out_of_range::create(402, detail::concat( | ||
|  |                                 "array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), | ||
|  |                                 ") is out of range"), ptr)); | ||
|  |                     } | ||
|  | 
 | ||
|  |                     // note: at performs range check
 | ||
|  |                     ptr = &ptr->at(array_index<BasicJsonType>(reference_token)); | ||
|  |                     break; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 case detail::value_t::null: | ||
|  |                 case detail::value_t::string: | ||
|  |                 case detail::value_t::boolean: | ||
|  |                 case detail::value_t::number_integer: | ||
|  |                 case detail::value_t::number_unsigned: | ||
|  |                 case detail::value_t::number_float: | ||
|  |                 case detail::value_t::binary: | ||
|  |                 case detail::value_t::discarded: | ||
|  |                 default: | ||
|  |                     JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         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 | ||
|  | 
 | ||
|  |     @throw parse_error.106   if an array index begins with '0' | ||
|  |     @throw parse_error.109   if an array index was not a number | ||
|  |     @throw out_of_range.402  if the array index '-' is used | ||
|  |     @throw out_of_range.404  if the JSON pointer can not be resolved | ||
|  |     */ | ||
|  |     template<typename BasicJsonType> | ||
|  |     const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const | ||
|  |     { | ||
|  |         for (const auto& reference_token : reference_tokens) | ||
|  |         { | ||
|  |             switch (ptr->type()) | ||
|  |             { | ||
|  |                 case detail::value_t::object: | ||
|  |                 { | ||
|  |                     // use unchecked object access
 | ||
|  |                     ptr = &ptr->operator[](reference_token); | ||
|  |                     break; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 case detail::value_t::array: | ||
|  |                 { | ||
|  |                     if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) | ||
|  |                     { | ||
|  |                         // "-" cannot be used for const access
 | ||
|  |                         JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr)); | ||
|  |                     } | ||
|  | 
 | ||
|  |                     // use unchecked array access
 | ||
|  |                     ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token)); | ||
|  |                     break; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 case detail::value_t::null: | ||
|  |                 case detail::value_t::string: | ||
|  |                 case detail::value_t::boolean: | ||
|  |                 case detail::value_t::number_integer: | ||
|  |                 case detail::value_t::number_unsigned: | ||
|  |                 case detail::value_t::number_float: | ||
|  |                 case detail::value_t::binary: | ||
|  |                 case detail::value_t::discarded: | ||
|  |                 default: | ||
|  |                     JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         return *ptr; | ||
|  |     } | ||
|  | 
 | ||
|  |     /*!
 | ||
|  |     @throw parse_error.106   if an array index begins with '0' | ||
|  |     @throw parse_error.109   if an array index was not a number | ||
|  |     @throw out_of_range.402  if the array index '-' is used | ||
|  |     @throw out_of_range.404  if the JSON pointer can not be resolved | ||
|  |     */ | ||
|  |     template<typename BasicJsonType> | ||
|  |     const BasicJsonType& get_checked(const BasicJsonType* ptr) const | ||
|  |     { | ||
|  |         for (const auto& reference_token : reference_tokens) | ||
|  |         { | ||
|  |             switch (ptr->type()) | ||
|  |             { | ||
|  |                 case detail::value_t::object: | ||
|  |                 { | ||
|  |                     // note: at performs range check
 | ||
|  |                     ptr = &ptr->at(reference_token); | ||
|  |                     break; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 case detail::value_t::array: | ||
|  |                 { | ||
|  |                     if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) | ||
|  |                     { | ||
|  |                         // "-" always fails the range check
 | ||
|  |                         JSON_THROW(detail::out_of_range::create(402, detail::concat( | ||
|  |                                 "array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), | ||
|  |                                 ") is out of range"), ptr)); | ||
|  |                     } | ||
|  | 
 | ||
|  |                     // note: at performs range check
 | ||
|  |                     ptr = &ptr->at(array_index<BasicJsonType>(reference_token)); | ||
|  |                     break; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 case detail::value_t::null: | ||
|  |                 case detail::value_t::string: | ||
|  |                 case detail::value_t::boolean: | ||
|  |                 case detail::value_t::number_integer: | ||
|  |                 case detail::value_t::number_unsigned: | ||
|  |                 case detail::value_t::number_float: | ||
|  |                 case detail::value_t::binary: | ||
|  |                 case detail::value_t::discarded: | ||
|  |                 default: | ||
|  |                     JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         return *ptr; | ||
|  |     } | ||
|  | 
 | ||
|  |     /*!
 | ||
|  |     @throw parse_error.106   if an array index begins with '0' | ||
|  |     @throw parse_error.109   if an array index was not a number | ||
|  |     */ | ||
|  |     template<typename BasicJsonType> | ||
|  |     bool contains(const BasicJsonType* ptr) const | ||
|  |     { | ||
|  |         for (const auto& reference_token : reference_tokens) | ||
|  |         { | ||
|  |             switch (ptr->type()) | ||
|  |             { | ||
|  |                 case detail::value_t::object: | ||
|  |                 { | ||
|  |                     if (!ptr->contains(reference_token)) | ||
|  |                     { | ||
|  |                         // we did not find the key in the object
 | ||
|  |                         return false; | ||
|  |                     } | ||
|  | 
 | ||
|  |                     ptr = &ptr->operator[](reference_token); | ||
|  |                     break; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 case detail::value_t::array: | ||
|  |                 { | ||
|  |                     if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) | ||
|  |                     { | ||
|  |                         // "-" always fails the range check
 | ||
|  |                         return false; | ||
|  |                     } | ||
|  |                     if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9"))) | ||
|  |                     { | ||
|  |                         // invalid char
 | ||
|  |                         return false; | ||
|  |                     } | ||
|  |                     if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) | ||
|  |                     { | ||
|  |                         if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) | ||
|  |                         { | ||
|  |                             // first char should be between '1' and '9'
 | ||
|  |                             return false; | ||
|  |                         } | ||
|  |                         for (std::size_t i = 1; i < reference_token.size(); i++) | ||
|  |                         { | ||
|  |                             if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9'))) | ||
|  |                             { | ||
|  |                                 // other char should be between '0' and '9'
 | ||
|  |                                 return false; | ||
|  |                             } | ||
|  |                         } | ||
|  |                     } | ||
|  | 
 | ||
|  |                     const auto idx = array_index<BasicJsonType>(reference_token); | ||
|  |                     if (idx >= ptr->size()) | ||
|  |                     { | ||
|  |                         // index out of range
 | ||
|  |                         return false; | ||
|  |                     } | ||
|  | 
 | ||
|  |                     ptr = &ptr->operator[](idx); | ||
|  |                     break; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 case detail::value_t::null: | ||
|  |                 case detail::value_t::string: | ||
|  |                 case detail::value_t::boolean: | ||
|  |                 case detail::value_t::number_integer: | ||
|  |                 case detail::value_t::number_unsigned: | ||
|  |                 case detail::value_t::number_float: | ||
|  |                 case detail::value_t::binary: | ||
|  |                 case detail::value_t::discarded: | ||
|  |                 default: | ||
|  |                 { | ||
|  |                     // we do not expect primitive values if there is still a
 | ||
|  |                     // reference token to process
 | ||
|  |                     return false; | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         // no reference token left means we found a primitive value
 | ||
|  |         return true; | ||
|  |     } | ||
|  | 
 | ||
|  |     /*!
 | ||
|  |     @brief split the string input to reference tokens | ||
|  | 
 | ||
|  |     @note This function is only called by the json_pointer constructor. | ||
|  |           All exceptions below are documented there. | ||
|  | 
 | ||
|  |     @throw parse_error.107  if the pointer is not empty or begins with '/' | ||
|  |     @throw parse_error.108  if character '~' is not followed by '0' or '1' | ||
|  |     */ | ||
|  |     static std::vector<string_t> split(const string_t& reference_string) | ||
|  |     { | ||
|  |         std::vector<string_t> result; | ||
|  | 
 | ||
|  |         // special case: empty reference string -> no reference tokens
 | ||
|  |         if (reference_string.empty()) | ||
|  |         { | ||
|  |             return result; | ||
|  |         } | ||
|  | 
 | ||
|  |         // check if nonempty reference string begins with slash
 | ||
|  |         if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) | ||
|  |         { | ||
|  |             JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr)); | ||
|  |         } | ||
|  | 
 | ||
|  |         // 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
 | ||
|  |             std::size_t slash = reference_string.find_first_of('/', 1), | ||
|  |             // set the beginning of the first reference token
 | ||
|  |             start = 1; | ||
|  |             // we can stop if start == 0 (if slash == string_t::npos)
 | ||
|  |             start != 0; | ||
|  |             // set the beginning of the next reference token
 | ||
|  |             // (will eventually be 0 if slash == string_t::npos)
 | ||
|  |             start = (slash == string_t::npos) ? 0 : slash + 1, | ||
|  |             // find next slash
 | ||
|  |             slash = reference_string.find_first_of('/', start)) | ||
|  |         { | ||
|  |             // use the text between the beginning of the reference token
 | ||
|  |             // (start) and the last slash (slash).
 | ||
|  |             auto reference_token = reference_string.substr(start, slash - start); | ||
|  | 
 | ||
|  |             // check reference tokens are properly escaped
 | ||
|  |             for (std::size_t pos = reference_token.find_first_of('~'); | ||
|  |                     pos != string_t::npos; | ||
|  |                     pos = reference_token.find_first_of('~', pos + 1)) | ||
|  |             { | ||
|  |                 JSON_ASSERT(reference_token[pos] == '~'); | ||
|  | 
 | ||
|  |                 // ~ must be followed by 0 or 1
 | ||
|  |                 if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || | ||
|  |                                          (reference_token[pos + 1] != '0' && | ||
|  |                                           reference_token[pos + 1] != '1'))) | ||
|  |                 { | ||
|  |                     JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             // finally, store the reference token
 | ||
|  |             detail::unescape(reference_token); | ||
|  |             result.push_back(reference_token); | ||
|  |         } | ||
|  | 
 | ||
|  |         return result; | ||
|  |     } | ||
|  | 
 | ||
|  |   private: | ||
|  |     /*!
 | ||
|  |     @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`. | ||
|  |     */ | ||
|  |     template<typename BasicJsonType> | ||
|  |     static void flatten(const string_t& reference_string, | ||
|  |                         const BasicJsonType& value, | ||
|  |                         BasicJsonType& result) | ||
|  |     { | ||
|  |         switch (value.type()) | ||
|  |         { | ||
|  |             case detail::value_t::array: | ||
|  |             { | ||
|  |                 if (value.m_data.m_value.array->empty()) | ||
|  |                 { | ||
|  |                     // flatten empty array as null
 | ||
|  |                     result[reference_string] = nullptr; | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     // iterate array and use index as reference string
 | ||
|  |                     for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i) | ||
|  |                     { | ||
|  |                         flatten(detail::concat(reference_string, '/', std::to_string(i)), | ||
|  |                                 value.m_data.m_value.array->operator[](i), result); | ||
|  |                     } | ||
|  |                 } | ||
|  |                 break; | ||
|  |             } | ||
|  | 
 | ||
|  |             case detail::value_t::object: | ||
|  |             { | ||
|  |                 if (value.m_data.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_data.m_value.object) | ||
|  |                     { | ||
|  |                         flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result); | ||
|  |                     } | ||
|  |                 } | ||
|  |                 break; | ||
|  |             } | ||
|  | 
 | ||
|  |             case detail::value_t::null: | ||
|  |             case detail::value_t::string: | ||
|  |             case detail::value_t::boolean: | ||
|  |             case detail::value_t::number_integer: | ||
|  |             case detail::value_t::number_unsigned: | ||
|  |             case detail::value_t::number_float: | ||
|  |             case detail::value_t::binary: | ||
|  |             case detail::value_t::discarded: | ||
|  |             default: | ||
|  |             { | ||
|  |                 // add primitive value with its reference string
 | ||
|  |                 result[reference_string] = value; | ||
|  |                 break; | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     /*!
 | ||
|  |     @param[in] value  flattened JSON | ||
|  | 
 | ||
|  |     @return unflattened JSON | ||
|  | 
 | ||
|  |     @throw parse_error.109 if array index is not a number | ||
|  |     @throw type_error.314  if value is not an object | ||
|  |     @throw type_error.315  if object values are not primitive | ||
|  |     @throw type_error.313  if value cannot be unflattened | ||
|  |     */ | ||
|  |     template<typename BasicJsonType> | ||
|  |     static BasicJsonType | ||
|  |     unflatten(const BasicJsonType& value) | ||
|  |     { | ||
|  |         if (JSON_HEDLEY_UNLIKELY(!value.is_object())) | ||
|  |         { | ||
|  |             JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value)); | ||
|  |         } | ||
|  | 
 | ||
|  |         BasicJsonType result; | ||
|  | 
 | ||
|  |         // iterate the JSON object values
 | ||
|  |         for (const auto& element : *value.m_data.m_value.object) | ||
|  |         { | ||
|  |             if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) | ||
|  |             { | ||
|  |                 JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second)); | ||
|  |             } | ||
|  | 
 | ||
|  |             // 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; | ||
|  |     } | ||
|  | 
 | ||
|  |     // can't use conversion operator because of ambiguity
 | ||
|  |     json_pointer<string_t> convert() const& | ||
|  |     { | ||
|  |         json_pointer<string_t> result; | ||
|  |         result.reference_tokens = reference_tokens; | ||
|  |         return result; | ||
|  |     } | ||
|  | 
 | ||
|  |     json_pointer<string_t> convert()&& | ||
|  |     { | ||
|  |         json_pointer<string_t> result; | ||
|  |         result.reference_tokens = std::move(reference_tokens); | ||
|  |         return result; | ||
|  |     } | ||
|  | 
 | ||
|  |   public: | ||
|  | #if JSON_HAS_THREE_WAY_COMPARISON
 | ||
|  |     /// @brief compares two JSON pointers for equality
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
 | ||
|  |     template<typename RefStringTypeRhs> | ||
|  |     bool operator==(const json_pointer<RefStringTypeRhs>& rhs) const noexcept | ||
|  |     { | ||
|  |         return reference_tokens == rhs.reference_tokens; | ||
|  |     } | ||
|  | 
 | ||
|  |     /// @brief compares JSON pointer and string for equality
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
 | ||
|  |     JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer)) | ||
|  |     bool operator==(const string_t& rhs) const | ||
|  |     { | ||
|  |         return *this == json_pointer(rhs); | ||
|  |     } | ||
|  | 
 | ||
|  |     /// @brief 3-way compares two JSON pointers
 | ||
|  |     template<typename RefStringTypeRhs> | ||
|  |     std::strong_ordering operator<=>(const json_pointer<RefStringTypeRhs>& rhs) const noexcept // *NOPAD*
 | ||
|  |     { | ||
|  |         return  reference_tokens <=> rhs.reference_tokens; // *NOPAD*
 | ||
|  |     } | ||
|  | #else
 | ||
|  |     /// @brief compares two JSON pointers for equality
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
 | ||
|  |     template<typename RefStringTypeLhs, typename RefStringTypeRhs> | ||
|  |     // NOLINTNEXTLINE(readability-redundant-declaration)
 | ||
|  |     friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs, | ||
|  |                            const json_pointer<RefStringTypeRhs>& rhs) noexcept; | ||
|  | 
 | ||
|  |     /// @brief compares JSON pointer and string for equality
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
 | ||
|  |     template<typename RefStringTypeLhs, typename StringType> | ||
|  |     // NOLINTNEXTLINE(readability-redundant-declaration)
 | ||
|  |     friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs, | ||
|  |                            const StringType& rhs); | ||
|  | 
 | ||
|  |     /// @brief compares string and JSON pointer for equality
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
 | ||
|  |     template<typename RefStringTypeRhs, typename StringType> | ||
|  |     // NOLINTNEXTLINE(readability-redundant-declaration)
 | ||
|  |     friend bool operator==(const StringType& lhs, | ||
|  |                            const json_pointer<RefStringTypeRhs>& rhs); | ||
|  | 
 | ||
|  |     /// @brief compares two JSON pointers for inequality
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
 | ||
|  |     template<typename RefStringTypeLhs, typename RefStringTypeRhs> | ||
|  |     // NOLINTNEXTLINE(readability-redundant-declaration)
 | ||
|  |     friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs, | ||
|  |                            const json_pointer<RefStringTypeRhs>& rhs) noexcept; | ||
|  | 
 | ||
|  |     /// @brief compares JSON pointer and string for inequality
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
 | ||
|  |     template<typename RefStringTypeLhs, typename StringType> | ||
|  |     // NOLINTNEXTLINE(readability-redundant-declaration)
 | ||
|  |     friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs, | ||
|  |                            const StringType& rhs); | ||
|  | 
 | ||
|  |     /// @brief compares string and JSON pointer for inequality
 | ||
|  |     /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
 | ||
|  |     template<typename RefStringTypeRhs, typename StringType> | ||
|  |     // NOLINTNEXTLINE(readability-redundant-declaration)
 | ||
|  |     friend bool operator!=(const StringType& lhs, | ||
|  |                            const json_pointer<RefStringTypeRhs>& rhs); | ||
|  | 
 | ||
|  |     /// @brief compares two JSON pointer for less-than
 | ||
|  |     template<typename RefStringTypeLhs, typename RefStringTypeRhs> | ||
|  |     // NOLINTNEXTLINE(readability-redundant-declaration)
 | ||
|  |     friend bool operator<(const json_pointer<RefStringTypeLhs>& lhs, | ||
|  |                           const json_pointer<RefStringTypeRhs>& rhs) noexcept; | ||
|  | #endif
 | ||
|  | 
 | ||
|  |   private: | ||
|  |     /// the reference tokens
 | ||
|  |     std::vector<string_t> reference_tokens; | ||
|  | }; | ||
|  | 
 | ||
|  | #if !JSON_HAS_THREE_WAY_COMPARISON
 | ||
|  | // functions cannot be defined inside class due to ODR violations
 | ||
|  | template<typename RefStringTypeLhs, typename RefStringTypeRhs> | ||
|  | inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs, | ||
|  |                        const json_pointer<RefStringTypeRhs>& rhs) noexcept | ||
|  | { | ||
|  |     return lhs.reference_tokens == rhs.reference_tokens; | ||
|  | } | ||
|  | 
 | ||
|  | template<typename RefStringTypeLhs, | ||
|  |          typename StringType = typename json_pointer<RefStringTypeLhs>::string_t> | ||
|  | JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer)) | ||
|  | inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs, | ||
|  |                        const StringType& rhs) | ||
|  | { | ||
|  |     return lhs == json_pointer<RefStringTypeLhs>(rhs); | ||
|  | } | ||
|  | 
 | ||
|  | template<typename RefStringTypeRhs, | ||
|  |          typename StringType = typename json_pointer<RefStringTypeRhs>::string_t> | ||
|  | JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer)) | ||
|  | inline bool operator==(const StringType& lhs, | ||
|  |                        const json_pointer<RefStringTypeRhs>& rhs) | ||
|  | { | ||
|  |     return json_pointer<RefStringTypeRhs>(lhs) == rhs; | ||
|  | } | ||
|  | 
 | ||
|  | template<typename RefStringTypeLhs, typename RefStringTypeRhs> | ||
|  | inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs, | ||
|  |                        const json_pointer<RefStringTypeRhs>& rhs) noexcept | ||
|  | { | ||
|  |     return !(lhs == rhs); | ||
|  | } | ||
|  | 
 | ||
|  | template<typename RefStringTypeLhs, | ||
|  |          typename StringType = typename json_pointer<RefStringTypeLhs>::string_t> | ||
|  | JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer)) | ||
|  | inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs, | ||
|  |                        const StringType& rhs) | ||
|  | { | ||
|  |     return !(lhs == rhs); | ||
|  | } | ||
|  | 
 | ||
|  | template<typename RefStringTypeRhs, | ||
|  |          typename StringType = typename json_pointer<RefStringTypeRhs>::string_t> | ||
|  | JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer)) | ||
|  | inline bool operator!=(const StringType& lhs, | ||
|  |                        const json_pointer<RefStringTypeRhs>& rhs) | ||
|  | { | ||
|  |     return !(lhs == rhs); | ||
|  | } | ||
|  | 
 | ||
|  | template<typename RefStringTypeLhs, typename RefStringTypeRhs> | ||
|  | inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs, | ||
|  |                       const json_pointer<RefStringTypeRhs>& rhs) noexcept | ||
|  | { | ||
|  |     return lhs.reference_tokens < rhs.reference_tokens; | ||
|  | } | ||
|  | #endif
 | ||
|  | 
 | ||
|  | NLOHMANN_JSON_NAMESPACE_END |