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:
Niels Lohmann 2020-06-23 09:16:01 +02:00 committed by GitHub
commit 4bfe4add20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 34 deletions

View File

@ -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

View File

@ -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));
}
};

View File

@ -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));
}
};

View File

@ -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,