improved RFC compliance and code coverage
This commit is contained in:
parent
f883a04c87
commit
0835eb293f
3 changed files with 112 additions and 8 deletions
46
src/json.hpp
46
src/json.hpp
|
@ -8993,6 +8993,8 @@ basic_json_parser_63:
|
||||||
@complexity Linear in the length of 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::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
|
reference get_unchecked(pointer ptr) const
|
||||||
{
|
{
|
||||||
|
@ -9002,18 +9004,27 @@ basic_json_parser_63:
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
{
|
{
|
||||||
|
// use unchecked object access
|
||||||
ptr = &ptr->operator[](reference_token);
|
ptr = &ptr->operator[](reference_token);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case value_t::array:
|
case value_t::array:
|
||||||
{
|
{
|
||||||
|
// error condition (cf. RFC 6901, Sect. 4)
|
||||||
|
if (reference_token.size() > 1 and reference_token[0] == '0')
|
||||||
|
{
|
||||||
|
throw std::domain_error("array index must not begin with '0'");
|
||||||
|
}
|
||||||
|
|
||||||
if (reference_token == "-")
|
if (reference_token == "-")
|
||||||
{
|
{
|
||||||
|
// explicityly treat "-" as index beyond the end
|
||||||
ptr = &ptr->operator[](ptr->m_value.array->size());
|
ptr = &ptr->operator[](ptr->m_value.array->size());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// convert array index to number; unchecked access
|
||||||
ptr = &ptr->operator[](static_cast<size_t>(std::stoi(reference_token)));
|
ptr = &ptr->operator[](static_cast<size_t>(std::stoi(reference_token)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -9037,6 +9048,7 @@ basic_json_parser_63:
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
{
|
{
|
||||||
|
// note: at performs range check
|
||||||
ptr = &ptr->at(reference_token);
|
ptr = &ptr->at(reference_token);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -9045,12 +9057,20 @@ basic_json_parser_63:
|
||||||
{
|
{
|
||||||
if (reference_token == "-")
|
if (reference_token == "-")
|
||||||
{
|
{
|
||||||
throw std::out_of_range("cannot resolve 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");
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
// error condition (cf. RFC 6901, Sect. 4)
|
||||||
|
if (reference_token.size() > 1 and reference_token[0] == '0')
|
||||||
{
|
{
|
||||||
ptr = &ptr->at(static_cast<size_t>(std::stoi(reference_token)));
|
throw std::domain_error("array index must not begin with '0'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// note: at performs range check
|
||||||
|
ptr = &ptr->at(static_cast<size_t>(std::stoi(reference_token)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9080,6 +9100,7 @@ basic_json_parser_63:
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
{
|
{
|
||||||
|
// use unchecked object access
|
||||||
ptr = &ptr->operator[](reference_token);
|
ptr = &ptr->operator[](reference_token);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -9088,10 +9109,19 @@ basic_json_parser_63:
|
||||||
{
|
{
|
||||||
if (reference_token == "-")
|
if (reference_token == "-")
|
||||||
{
|
{
|
||||||
|
// "-" cannot be used for const access
|
||||||
throw std::out_of_range("array index '-' (" +
|
throw std::out_of_range("array index '-' (" +
|
||||||
std::to_string(ptr->m_value.array->size()) +
|
std::to_string(ptr->m_value.array->size()) +
|
||||||
") is out of range");
|
") is out of range");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// error condition (cf. RFC 6901, Sect. 4)
|
||||||
|
if (reference_token.size() > 1 and reference_token[0] == '0')
|
||||||
|
{
|
||||||
|
throw std::domain_error("array index must not begin with '0'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// use unchecked array access
|
||||||
ptr = &ptr->operator[](static_cast<size_t>(std::stoi(reference_token)));
|
ptr = &ptr->operator[](static_cast<size_t>(std::stoi(reference_token)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -9114,6 +9144,7 @@ basic_json_parser_63:
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
{
|
{
|
||||||
|
// note: at performs range check
|
||||||
ptr = &ptr->at(reference_token);
|
ptr = &ptr->at(reference_token);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -9122,10 +9153,19 @@ basic_json_parser_63:
|
||||||
{
|
{
|
||||||
if (reference_token == "-")
|
if (reference_token == "-")
|
||||||
{
|
{
|
||||||
|
// "-" always fails the range check
|
||||||
throw std::out_of_range("array index '-' (" +
|
throw std::out_of_range("array index '-' (" +
|
||||||
std::to_string(ptr->m_value.array->size()) +
|
std::to_string(ptr->m_value.array->size()) +
|
||||||
") is out of range");
|
") is out of range");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// error condition (cf. RFC 6901, Sect. 4)
|
||||||
|
if (reference_token.size() > 1 and reference_token[0] == '0')
|
||||||
|
{
|
||||||
|
throw std::domain_error("array index must not begin with '0'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: at performs range check
|
||||||
ptr = &ptr->at(static_cast<size_t>(std::stoi(reference_token)));
|
ptr = &ptr->at(static_cast<size_t>(std::stoi(reference_token)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8303,6 +8303,8 @@ class basic_json
|
||||||
@complexity Linear in the length of 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::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
|
reference get_unchecked(pointer ptr) const
|
||||||
{
|
{
|
||||||
|
@ -8312,18 +8314,27 @@ class basic_json
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
{
|
{
|
||||||
|
// use unchecked object access
|
||||||
ptr = &ptr->operator[](reference_token);
|
ptr = &ptr->operator[](reference_token);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case value_t::array:
|
case value_t::array:
|
||||||
{
|
{
|
||||||
|
// error condition (cf. RFC 6901, Sect. 4)
|
||||||
|
if (reference_token.size() > 1 and reference_token[0] == '0')
|
||||||
|
{
|
||||||
|
throw std::domain_error("array index must not begin with '0'");
|
||||||
|
}
|
||||||
|
|
||||||
if (reference_token == "-")
|
if (reference_token == "-")
|
||||||
{
|
{
|
||||||
|
// explicityly treat "-" as index beyond the end
|
||||||
ptr = &ptr->operator[](ptr->m_value.array->size());
|
ptr = &ptr->operator[](ptr->m_value.array->size());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// convert array index to number; unchecked access
|
||||||
ptr = &ptr->operator[](static_cast<size_t>(std::stoi(reference_token)));
|
ptr = &ptr->operator[](static_cast<size_t>(std::stoi(reference_token)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -8347,6 +8358,7 @@ class basic_json
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
{
|
{
|
||||||
|
// note: at performs range check
|
||||||
ptr = &ptr->at(reference_token);
|
ptr = &ptr->at(reference_token);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -8355,12 +8367,20 @@ class basic_json
|
||||||
{
|
{
|
||||||
if (reference_token == "-")
|
if (reference_token == "-")
|
||||||
{
|
{
|
||||||
throw std::out_of_range("cannot resolve 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");
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
// error condition (cf. RFC 6901, Sect. 4)
|
||||||
|
if (reference_token.size() > 1 and reference_token[0] == '0')
|
||||||
{
|
{
|
||||||
ptr = &ptr->at(static_cast<size_t>(std::stoi(reference_token)));
|
throw std::domain_error("array index must not begin with '0'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// note: at performs range check
|
||||||
|
ptr = &ptr->at(static_cast<size_t>(std::stoi(reference_token)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8390,6 +8410,7 @@ class basic_json
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
{
|
{
|
||||||
|
// use unchecked object access
|
||||||
ptr = &ptr->operator[](reference_token);
|
ptr = &ptr->operator[](reference_token);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -8398,10 +8419,19 @@ class basic_json
|
||||||
{
|
{
|
||||||
if (reference_token == "-")
|
if (reference_token == "-")
|
||||||
{
|
{
|
||||||
|
// "-" cannot be used for const access
|
||||||
throw std::out_of_range("array index '-' (" +
|
throw std::out_of_range("array index '-' (" +
|
||||||
std::to_string(ptr->m_value.array->size()) +
|
std::to_string(ptr->m_value.array->size()) +
|
||||||
") is out of range");
|
") is out of range");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// error condition (cf. RFC 6901, Sect. 4)
|
||||||
|
if (reference_token.size() > 1 and reference_token[0] == '0')
|
||||||
|
{
|
||||||
|
throw std::domain_error("array index must not begin with '0'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// use unchecked array access
|
||||||
ptr = &ptr->operator[](static_cast<size_t>(std::stoi(reference_token)));
|
ptr = &ptr->operator[](static_cast<size_t>(std::stoi(reference_token)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -8424,6 +8454,7 @@ class basic_json
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
{
|
{
|
||||||
|
// note: at performs range check
|
||||||
ptr = &ptr->at(reference_token);
|
ptr = &ptr->at(reference_token);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -8432,10 +8463,19 @@ class basic_json
|
||||||
{
|
{
|
||||||
if (reference_token == "-")
|
if (reference_token == "-")
|
||||||
{
|
{
|
||||||
|
// "-" always fails the range check
|
||||||
throw std::out_of_range("array index '-' (" +
|
throw std::out_of_range("array index '-' (" +
|
||||||
std::to_string(ptr->m_value.array->size()) +
|
std::to_string(ptr->m_value.array->size()) +
|
||||||
") is out of range");
|
") is out of range");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// error condition (cf. RFC 6901, Sect. 4)
|
||||||
|
if (reference_token.size() > 1 and reference_token[0] == '0')
|
||||||
|
{
|
||||||
|
throw std::domain_error("array index must not begin with '0'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: at performs range check
|
||||||
ptr = &ptr->at(static_cast<size_t>(std::stoi(reference_token)));
|
ptr = &ptr->at(static_cast<size_t>(std::stoi(reference_token)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12196,6 +12196,7 @@ TEST_CASE("JSON pointers")
|
||||||
SECTION("nonconst access")
|
SECTION("nonconst access")
|
||||||
{
|
{
|
||||||
json j = {1, 2, 3};
|
json j = {1, 2, 3};
|
||||||
|
const json j_const = j;
|
||||||
|
|
||||||
// check reading access
|
// check reading access
|
||||||
CHECK(j["/0"_json_pointer] == j[0]);
|
CHECK(j["/0"_json_pointer] == j[0]);
|
||||||
|
@ -12214,9 +12215,32 @@ TEST_CASE("JSON pointers")
|
||||||
j["/5"_json_pointer] = 55;
|
j["/5"_json_pointer] = 55;
|
||||||
CHECK(j == json({1, 13, 3, 33, nullptr, 55}));
|
CHECK(j == json({1, 13, 3, 33, nullptr, 55}));
|
||||||
|
|
||||||
|
// error with leading 0
|
||||||
|
CHECK_THROWS_AS(j["/01"_json_pointer], std::domain_error);
|
||||||
|
CHECK_THROWS_WITH(j["/01"_json_pointer], "array index must not begin with '0'");
|
||||||
|
CHECK_THROWS_AS(j_const["/01"_json_pointer], std::domain_error);
|
||||||
|
CHECK_THROWS_WITH(j_const["/01"_json_pointer], "array index must not begin with '0'");
|
||||||
|
CHECK_THROWS_AS(j.at("/01"_json_pointer), std::domain_error);
|
||||||
|
CHECK_THROWS_WITH(j.at("/01"_json_pointer), "array index must not begin with '0'");
|
||||||
|
CHECK_THROWS_AS(j_const.at("/01"_json_pointer), std::domain_error);
|
||||||
|
CHECK_THROWS_WITH(j_const.at("/01"_json_pointer), "array index must not begin with '0'");
|
||||||
|
|
||||||
|
// error with incorrect numbers
|
||||||
|
CHECK_THROWS_AS(j["/one"_json_pointer] = 1, std::invalid_argument);
|
||||||
|
|
||||||
// assign to "-"
|
// assign to "-"
|
||||||
j["/-"_json_pointer] = 99;
|
j["/-"_json_pointer] = 99;
|
||||||
CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99}));
|
CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99}));
|
||||||
|
|
||||||
|
// error when using "-" in const object
|
||||||
|
CHECK_THROWS_AS(j_const["/-"_json_pointer], std::out_of_range);
|
||||||
|
CHECK_THROWS_WITH(j_const["/-"_json_pointer], "array index '-' (3) is out of range");
|
||||||
|
|
||||||
|
// error when using "-" with at
|
||||||
|
CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range);
|
||||||
|
CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (7) is out of range");
|
||||||
|
CHECK_THROWS_AS(j_const.at("/-"_json_pointer), std::out_of_range);
|
||||||
|
CHECK_THROWS_WITH(j_const.at("/-"_json_pointer), "array index '-' (3) is out of range");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("const access")
|
SECTION("const access")
|
||||||
|
|
Loading…
Reference in a new issue