Merge pull request #2203 from t-b/use-unsigned-indizies-for-array-index-in-json-pointer
Use unsigned indizies for array index in json pointer
This commit is contained in:
		
						commit
						4bfe4add20
					
				
					 4 changed files with 73 additions and 34 deletions
				
			
		| 
						 | 
				
			
			@ -3,6 +3,7 @@
 | 
			
		|||
#include <algorithm> // all_of
 | 
			
		||||
#include <cassert> // assert
 | 
			
		||||
#include <cctype> // isdigit
 | 
			
		||||
#include <limits> // max
 | 
			
		||||
#include <numeric> // accumulate
 | 
			
		||||
#include <string> // string
 | 
			
		||||
#include <utility> // move
 | 
			
		||||
| 
						 | 
				
			
			@ -325,10 +326,15 @@ class json_pointer
 | 
			
		|||
 | 
			
		||||
    @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
 | 
			
		||||
    */
 | 
			
		||||
    static int array_index(const std::string& s)
 | 
			
		||||
    static typename BasicJsonType::size_type array_index(const std::string& s)
 | 
			
		||||
    {
 | 
			
		||||
        using size_type = typename BasicJsonType::size_type;
 | 
			
		||||
 | 
			
		||||
        // error condition (cf. RFC 6901, Sect. 4)
 | 
			
		||||
        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and s[0] == '0'))
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -344,10 +350,10 @@ class json_pointer
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        std::size_t processed_chars = 0;
 | 
			
		||||
        int res = 0;
 | 
			
		||||
        unsigned long long res = 0;
 | 
			
		||||
        JSON_TRY
 | 
			
		||||
        {
 | 
			
		||||
            res = std::stoi(s, &processed_chars);
 | 
			
		||||
            res = std::stoull(s, &processed_chars);
 | 
			
		||||
        }
 | 
			
		||||
        JSON_CATCH(std::out_of_range&)
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -360,7 +366,14 @@ class json_pointer
 | 
			
		|||
            JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return res;
 | 
			
		||||
        // 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)()))
 | 
			
		||||
        {
 | 
			
		||||
            JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type")); // LCOV_EXCL_LINE
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return static_cast<size_type>(res);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    json_pointer top() const
 | 
			
		||||
| 
						 | 
				
			
			@ -419,7 +432,7 @@ class json_pointer
 | 
			
		|||
                case detail::value_t::array:
 | 
			
		||||
                {
 | 
			
		||||
                    // create an entry in the array
 | 
			
		||||
                    result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
 | 
			
		||||
                    result = &result->operator[](array_index(reference_token));
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -497,8 +510,7 @@ class json_pointer
 | 
			
		|||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        // convert array index to number; unchecked access
 | 
			
		||||
                        ptr = &ptr->operator[](
 | 
			
		||||
                                  static_cast<size_type>(array_index(reference_token)));
 | 
			
		||||
                        ptr = &ptr->operator[](array_index(reference_token));
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -542,7 +554,7 @@ class json_pointer
 | 
			
		|||
                    }
 | 
			
		||||
 | 
			
		||||
                    // note: at performs range check
 | 
			
		||||
                    ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
 | 
			
		||||
                    ptr = &ptr->at(array_index(reference_token));
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -592,8 +604,7 @@ class json_pointer
 | 
			
		|||
                    }
 | 
			
		||||
 | 
			
		||||
                    // use unchecked array access
 | 
			
		||||
                    ptr = &ptr->operator[](
 | 
			
		||||
                              static_cast<size_type>(array_index(reference_token)));
 | 
			
		||||
                    ptr = &ptr->operator[](array_index(reference_token));
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -636,7 +647,7 @@ class json_pointer
 | 
			
		|||
                    }
 | 
			
		||||
 | 
			
		||||
                    // note: at performs range check
 | 
			
		||||
                    ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
 | 
			
		||||
                    ptr = &ptr->at(array_index(reference_token));
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -700,7 +711,7 @@ class json_pointer
 | 
			
		|||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    const auto idx = static_cast<size_type>(array_index(reference_token));
 | 
			
		||||
                    const auto idx = array_index(reference_token);
 | 
			
		||||
                    if (idx >= ptr->size())
 | 
			
		||||
                    {
 | 
			
		||||
                        // index out of range
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8207,7 +8207,7 @@ class basic_json
 | 
			
		|||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        const auto idx = json_pointer::array_index(last_path);
 | 
			
		||||
                        if (JSON_HEDLEY_UNLIKELY(static_cast<size_type>(idx) > parent.size()))
 | 
			
		||||
                        if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
 | 
			
		||||
                        {
 | 
			
		||||
                            // avoid undefined behavior
 | 
			
		||||
                            JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
 | 
			
		||||
| 
						 | 
				
			
			@ -8250,7 +8250,7 @@ class basic_json
 | 
			
		|||
            else if (parent.is_array())
 | 
			
		||||
            {
 | 
			
		||||
                // note erase performs range check
 | 
			
		||||
                parent.erase(static_cast<size_type>(json_pointer::array_index(last_path)));
 | 
			
		||||
                parent.erase(json_pointer::array_index(last_path));
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11036,6 +11036,7 @@ class json_reverse_iterator : public std::reverse_iterator<Base>
 | 
			
		|||
#include <algorithm> // all_of
 | 
			
		||||
#include <cassert> // assert
 | 
			
		||||
#include <cctype> // isdigit
 | 
			
		||||
#include <limits> // max
 | 
			
		||||
#include <numeric> // accumulate
 | 
			
		||||
#include <string> // string
 | 
			
		||||
#include <utility> // move
 | 
			
		||||
| 
						 | 
				
			
			@ -11361,10 +11362,15 @@ class json_pointer
 | 
			
		|||
 | 
			
		||||
    @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
 | 
			
		||||
    */
 | 
			
		||||
    static int array_index(const std::string& s)
 | 
			
		||||
    static typename BasicJsonType::size_type array_index(const std::string& s)
 | 
			
		||||
    {
 | 
			
		||||
        using size_type = typename BasicJsonType::size_type;
 | 
			
		||||
 | 
			
		||||
        // error condition (cf. RFC 6901, Sect. 4)
 | 
			
		||||
        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and s[0] == '0'))
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -11380,10 +11386,10 @@ class json_pointer
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        std::size_t processed_chars = 0;
 | 
			
		||||
        int res = 0;
 | 
			
		||||
        unsigned long long res = 0;
 | 
			
		||||
        JSON_TRY
 | 
			
		||||
        {
 | 
			
		||||
            res = std::stoi(s, &processed_chars);
 | 
			
		||||
            res = std::stoull(s, &processed_chars);
 | 
			
		||||
        }
 | 
			
		||||
        JSON_CATCH(std::out_of_range&)
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -11396,7 +11402,14 @@ class json_pointer
 | 
			
		|||
            JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return res;
 | 
			
		||||
        // 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)()))
 | 
			
		||||
        {
 | 
			
		||||
            JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type")); // LCOV_EXCL_LINE
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return static_cast<size_type>(res);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    json_pointer top() const
 | 
			
		||||
| 
						 | 
				
			
			@ -11455,7 +11468,7 @@ class json_pointer
 | 
			
		|||
                case detail::value_t::array:
 | 
			
		||||
                {
 | 
			
		||||
                    // create an entry in the array
 | 
			
		||||
                    result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
 | 
			
		||||
                    result = &result->operator[](array_index(reference_token));
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -11533,8 +11546,7 @@ class json_pointer
 | 
			
		|||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        // convert array index to number; unchecked access
 | 
			
		||||
                        ptr = &ptr->operator[](
 | 
			
		||||
                                  static_cast<size_type>(array_index(reference_token)));
 | 
			
		||||
                        ptr = &ptr->operator[](array_index(reference_token));
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -11578,7 +11590,7 @@ class json_pointer
 | 
			
		|||
                    }
 | 
			
		||||
 | 
			
		||||
                    // note: at performs range check
 | 
			
		||||
                    ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
 | 
			
		||||
                    ptr = &ptr->at(array_index(reference_token));
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -11628,8 +11640,7 @@ class json_pointer
 | 
			
		|||
                    }
 | 
			
		||||
 | 
			
		||||
                    // use unchecked array access
 | 
			
		||||
                    ptr = &ptr->operator[](
 | 
			
		||||
                              static_cast<size_type>(array_index(reference_token)));
 | 
			
		||||
                    ptr = &ptr->operator[](array_index(reference_token));
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -11672,7 +11683,7 @@ class json_pointer
 | 
			
		|||
                    }
 | 
			
		||||
 | 
			
		||||
                    // note: at performs range check
 | 
			
		||||
                    ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
 | 
			
		||||
                    ptr = &ptr->at(array_index(reference_token));
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -11736,7 +11747,7 @@ class json_pointer
 | 
			
		|||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    const auto idx = static_cast<size_type>(array_index(reference_token));
 | 
			
		||||
                    const auto idx = array_index(reference_token);
 | 
			
		||||
                    if (idx >= ptr->size())
 | 
			
		||||
                    {
 | 
			
		||||
                        // index out of range
 | 
			
		||||
| 
						 | 
				
			
			@ -24001,7 +24012,7 @@ class basic_json
 | 
			
		|||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        const auto idx = json_pointer::array_index(last_path);
 | 
			
		||||
                        if (JSON_HEDLEY_UNLIKELY(static_cast<size_type>(idx) > parent.size()))
 | 
			
		||||
                        if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
 | 
			
		||||
                        {
 | 
			
		||||
                            // avoid undefined behavior
 | 
			
		||||
                            JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
 | 
			
		||||
| 
						 | 
				
			
			@ -24044,7 +24055,7 @@ class basic_json
 | 
			
		|||
            else if (parent.is_array())
 | 
			
		||||
            {
 | 
			
		||||
                // note erase performs range check
 | 
			
		||||
                parent.erase(static_cast<size_type>(json_pointer::array_index(last_path)));
 | 
			
		||||
                parent.erase(json_pointer::array_index(last_path));
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -348,12 +348,29 @@ TEST_CASE("JSON pointers")
 | 
			
		|||
            CHECK_THROWS_WITH(j_const["/1+1"_json_pointer] == 1,
 | 
			
		||||
                              "[json.exception.out_of_range.404] unresolved reference token '1+1'");
 | 
			
		||||
 | 
			
		||||
            CHECK_THROWS_AS(j["/111111111111111111111111"_json_pointer] = 1, json::out_of_range&);
 | 
			
		||||
            CHECK_THROWS_WITH(j["/111111111111111111111111"_json_pointer] = 1,
 | 
			
		||||
                              "[json.exception.out_of_range.404] unresolved reference token '111111111111111111111111'");
 | 
			
		||||
            CHECK_THROWS_AS(j_const["/111111111111111111111111"_json_pointer] == 1, json::out_of_range&);
 | 
			
		||||
            CHECK_THROWS_WITH(j_const["/111111111111111111111111"_json_pointer] == 1,
 | 
			
		||||
                              "[json.exception.out_of_range.404] unresolved reference token '111111111111111111111111'");
 | 
			
		||||
            {
 | 
			
		||||
                auto too_large_index = std::to_string((std::numeric_limits<unsigned long long>::max)()) + "1";
 | 
			
		||||
                json::json_pointer jp(std::string("/") + too_large_index);
 | 
			
		||||
                std::string throw_msg = std::string("[json.exception.out_of_range.404] unresolved reference token '") + too_large_index + "'";
 | 
			
		||||
 | 
			
		||||
                CHECK_THROWS_AS(j[jp] = 1, json::out_of_range&);
 | 
			
		||||
                CHECK_THROWS_WITH(j[jp] = 1, throw_msg.c_str());
 | 
			
		||||
                CHECK_THROWS_AS(j_const[jp] == 1, json::out_of_range&);
 | 
			
		||||
                CHECK_THROWS_WITH(j_const[jp] == 1, throw_msg.c_str());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (sizeof(typename json::size_type) < sizeof(unsigned long long))
 | 
			
		||||
            {
 | 
			
		||||
                auto size_type_max_uul = static_cast<unsigned long long>((std::numeric_limits<json::size_type>::max)());
 | 
			
		||||
                auto too_large_index = std::to_string(size_type_max_uul);
 | 
			
		||||
                json::json_pointer jp(std::string("/") + too_large_index);
 | 
			
		||||
                std::string throw_msg = std::string("[json.exception.out_of_range.410] array index ") + too_large_index + " exceeds size_type";
 | 
			
		||||
 | 
			
		||||
                CHECK_THROWS_AS(j[jp] = 1, json::out_of_range&);
 | 
			
		||||
                CHECK_THROWS_WITH(j[jp] = 1, throw_msg.c_str());
 | 
			
		||||
                CHECK_THROWS_AS(j_const[jp] == 1, json::out_of_range&);
 | 
			
		||||
                CHECK_THROWS_WITH(j_const[jp] == 1, throw_msg.c_str());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            CHECK_THROWS_AS(j.at("/one"_json_pointer) = 1, json::parse_error&);
 | 
			
		||||
            CHECK_THROWS_WITH(j.at("/one"_json_pointer) = 1,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue