diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 73db0745..5e4c8a1f 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -329,8 +329,30 @@ class json_pointer */ static int array_index(const std::string& s) { + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and s[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + s + + "' must not begin with '0'")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and not (s[0] >= '1' and s[0] <= '9'))) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number")); + } + std::size_t processed_chars = 0; - const int res = std::stoi(s, &processed_chars); + int res = 0; + JSON_TRY + { + res = std::stoi(s, &processed_chars); + } + JSON_CATCH(std::out_of_range&) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + } // check if the string was completely read if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) @@ -397,14 +419,7 @@ class json_pointer case detail::value_t::array: { // create an entry in the array - JSON_TRY - { - result = &result->operator[](static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + result = &result->operator[](static_cast(array_index(reference_token))); break; } @@ -474,14 +489,6 @@ class json_pointer case detail::value_t::array: { - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - if (reference_token == "-") { // explicitly treat "-" as index beyond the end @@ -490,15 +497,8 @@ class json_pointer else { // convert array index to number; unchecked access - JSON_TRY - { - ptr = &ptr->operator[]( - static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); } break; } @@ -541,23 +541,8 @@ class json_pointer ") is out of range")); } - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - // note: at performs range check - JSON_TRY - { - ptr = &ptr->at(static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + ptr = &ptr->at(static_cast(array_index(reference_token))); break; } @@ -606,24 +591,9 @@ class json_pointer ") is out of range")); } - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - // use unchecked array access - JSON_TRY - { - ptr = &ptr->operator[]( - static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); break; } @@ -665,23 +635,8 @@ class json_pointer ") is out of range")); } - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - // note: at performs range check - JSON_TRY - { - ptr = &ptr->at(static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + ptr = &ptr->at(static_cast(array_index(reference_token))); break; } @@ -724,30 +679,14 @@ class json_pointer return false; } - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + const auto idx = static_cast(array_index(reference_token)); + if (idx >= ptr->size()) { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); + // index out of range + return false; } - JSON_TRY - { - const auto idx = static_cast(array_index(reference_token)); - if (idx >= ptr->size()) - { - // index out of range - return false; - } - - ptr = &ptr->operator[](idx); - break; - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + ptr = &ptr->operator[](idx); break; } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 7749797d..15d069fc 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -10732,8 +10732,30 @@ class json_pointer */ static int array_index(const std::string& s) { + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and s[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + s + + "' must not begin with '0'")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and not (s[0] >= '1' and s[0] <= '9'))) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number")); + } + std::size_t processed_chars = 0; - const int res = std::stoi(s, &processed_chars); + int res = 0; + JSON_TRY + { + res = std::stoi(s, &processed_chars); + } + JSON_CATCH(std::out_of_range&) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + } // check if the string was completely read if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) @@ -10800,14 +10822,7 @@ class json_pointer case detail::value_t::array: { // create an entry in the array - JSON_TRY - { - result = &result->operator[](static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + result = &result->operator[](static_cast(array_index(reference_token))); break; } @@ -10877,14 +10892,6 @@ class json_pointer case detail::value_t::array: { - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - if (reference_token == "-") { // explicitly treat "-" as index beyond the end @@ -10893,15 +10900,8 @@ class json_pointer else { // convert array index to number; unchecked access - JSON_TRY - { - ptr = &ptr->operator[]( - static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); } break; } @@ -10944,23 +10944,8 @@ class json_pointer ") is out of range")); } - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - // note: at performs range check - JSON_TRY - { - ptr = &ptr->at(static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + ptr = &ptr->at(static_cast(array_index(reference_token))); break; } @@ -11009,24 +10994,9 @@ class json_pointer ") is out of range")); } - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - // use unchecked array access - JSON_TRY - { - ptr = &ptr->operator[]( - static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); break; } @@ -11068,23 +11038,8 @@ class json_pointer ") is out of range")); } - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - // note: at performs range check - JSON_TRY - { - ptr = &ptr->at(static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + ptr = &ptr->at(static_cast(array_index(reference_token))); break; } @@ -11127,30 +11082,14 @@ class json_pointer return false; } - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + const auto idx = static_cast(array_index(reference_token)); + if (idx >= ptr->size()) { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); + // index out of range + return false; } - JSON_TRY - { - const auto idx = static_cast(array_index(reference_token)); - if (idx >= ptr->size()) - { - // index out of range - return false; - } - - ptr = &ptr->operator[](idx); - break; - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } + ptr = &ptr->operator[](idx); break; } diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp index 3cfd8dad..1655a458 100644 --- a/test/src/unit-json_pointer.cpp +++ b/test/src/unit-json_pointer.cpp @@ -332,6 +332,34 @@ TEST_CASE("JSON pointers") CHECK_THROWS_WITH(j_const.at("/one"_json_pointer) == 1, "[json.exception.parse_error.109] parse error: array index 'one' is not a number"); + CHECK_THROWS_AS(j["/+1"_json_pointer] = 1, json::parse_error&); + CHECK_THROWS_WITH(j["/+1"_json_pointer] = 1, + "[json.exception.parse_error.109] parse error: array index '+1' is not a number"); + CHECK_THROWS_AS(j_const["/+1"_json_pointer] == 1, json::parse_error&); + CHECK_THROWS_WITH(j_const["/+1"_json_pointer] == 1, + "[json.exception.parse_error.109] parse error: array index '+1' is not a number"); + + CHECK_THROWS_AS(j["/1+1"_json_pointer] = 1, json::out_of_range&); + CHECK_THROWS_WITH(j["/1+1"_json_pointer] = 1, + "[json.exception.out_of_range.404] unresolved reference token '1+1'"); + CHECK_THROWS_AS(j_const["/1+1"_json_pointer] == 1, json::out_of_range&); + 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'"); + + CHECK_THROWS_AS(j.at("/one"_json_pointer) = 1, json::parse_error&); + CHECK_THROWS_WITH(j.at("/one"_json_pointer) = 1, + "[json.exception.parse_error.109] parse error: array index 'one' is not a number"); + CHECK_THROWS_AS(j_const.at("/one"_json_pointer) == 1, json::parse_error&); + CHECK_THROWS_WITH(j_const.at("/one"_json_pointer) == 1, + "[json.exception.parse_error.109] parse error: array index 'one' is not a number"); + CHECK_THROWS_AS(j.contains("/one"_json_pointer), json::parse_error&); CHECK_THROWS_WITH(j.contains("/one"_json_pointer), "[json.exception.parse_error.109] parse error: array index 'one' is not a number");