Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions include/nlohmann/detail/conversions/from_json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
n = nullptr;
}

template <typename BasicJsonType, typename ArithmeticTypeTarget, typename ArithmeticTypeSource>
ArithmeticTypeTarget static_cast_check_range(const BasicJsonType& j)
{
const auto val = *j.template get_ptr<ArithmeticTypeSource*>();
const auto min = std::numeric_limits<ArithmeticTypeTarget>::min();
const auto max = std::numeric_limits<ArithmeticTypeTarget>::max();
if (val < min && val > max)
Copy link
Contributor

@gregmarr gregmarr Apr 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if will never be true, unless you get some weird results from val and min/max having different types. You want || not &&. You probably also want min and max to have the same type as val.

You would also only need to do this test if ArithmeticTypeTarget and ArithmeticTypeSource are different types, and then only if Target is smaller than Source, or if they have different signed-ness.

It might also be better to do this as a pre-test that just throws on out of range, and leave the current lines alone for the actual assignments.

{
JSON_THROW(
out_of_range::create(
406,
"value " + std::to_string(val) + " is out of target integer range [" + std::to_string(min) + ", " +
std::to_string(max) + "]", &j));
}
return static_cast<ArithmeticTypeTarget>(val);
}


// overloads for basic_json template parameters
template < typename BasicJsonType, typename ArithmeticType,
enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
Expand All @@ -54,17 +72,17 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
{
case value_t::number_unsigned:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
val = static_cast_check_range<BasicJsonType, ArithmeticType, const typename BasicJsonType::number_unsigned_t>(j);
break;
}
case value_t::number_integer:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
val = static_cast_check_range<BasicJsonType, ArithmeticType, const typename BasicJsonType::number_integer_t>(j);
break;
}
case value_t::number_float:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
val = static_cast_check_range<BasicJsonType, ArithmeticType, const typename BasicJsonType::number_float_t>(j);
break;
}

Expand Down Expand Up @@ -343,17 +361,17 @@ inline void from_json(const BasicJsonType& j, ArithmeticType& val)
{
case value_t::number_unsigned:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
val = static_cast_check_range<BasicJsonType, ArithmeticType, const typename BasicJsonType::number_unsigned_t>(j);
break;
}
case value_t::number_integer:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
val = static_cast_check_range<BasicJsonType, ArithmeticType, const typename BasicJsonType::number_integer_t>(j);
break;
}
case value_t::number_float:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
val = static_cast_check_range<BasicJsonType, ArithmeticType, const typename BasicJsonType::number_float_t>(j);
break;
}
case value_t::boolean:
Expand Down
6 changes: 6 additions & 0 deletions tests/src/unit-class_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,12 @@ TEST_CASE("parser class")
CHECK_THROWS_WITH_AS(parser_helper("1.18973e+4932").empty(), "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'", json::out_of_range&);
}

SECTION("out-of-range-conversion")
{
// overflows during parsing with casting yield an exception
CHECK_THROWS_WITH_AS(parser_helper("123456").get<int16_t>(), "[json.exception.out_of_range.406] value 123456 is out of target integer range [-32768, 32767]", json::out_of_range&);
}

SECTION("invalid numbers")
{
// numbers must not begin with "+"
Expand Down