mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +00:00
fix: JSON parsing of negative STNumber and STAmount (#5990)
This change fixes JSON parsing of negative `int` input in `STNumber` and `STAmount`. The conversion of JSON to `STNumber` or `STAmount` may trigger a condition where we negate smallest possible `int` value, which is undefined behaviour. We use a temporary storage as `int64_t` to avoid this bug. Note that this only affects RPC, because we do not parse JSON in the protocol layer, and hence no amendment is needed.
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
#include <xrpl/json/json_forwards.h>
|
#include <xrpl/json/json_forwards.h>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <limits>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -139,9 +140,9 @@ public:
|
|||||||
using ArrayIndex = UInt;
|
using ArrayIndex = UInt;
|
||||||
|
|
||||||
static Value const null;
|
static Value const null;
|
||||||
static Int const minInt;
|
static constexpr Int minInt = std::numeric_limits<Int>::min();
|
||||||
static Int const maxInt;
|
static constexpr Int maxInt = std::numeric_limits<Int>::max();
|
||||||
static UInt const maxUInt;
|
static constexpr UInt maxUInt = std::numeric_limits<UInt>::max();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class CZString
|
class CZString
|
||||||
@@ -244,6 +245,10 @@ public:
|
|||||||
bool
|
bool
|
||||||
asBool() const;
|
asBool() const;
|
||||||
|
|
||||||
|
/** Correct absolute value from int or unsigned int */
|
||||||
|
UInt
|
||||||
|
asAbsUInt() const;
|
||||||
|
|
||||||
// TODO: What is the "empty()" method this docstring mentions?
|
// TODO: What is the "empty()" method this docstring mentions?
|
||||||
/** isNull() tests to see if this field is null. Don't use this method to
|
/** isNull() tests to see if this field is null. Don't use this method to
|
||||||
test for emptiness: use empty(). */
|
test for emptiness: use empty(). */
|
||||||
|
|||||||
@@ -14,9 +14,6 @@
|
|||||||
namespace Json {
|
namespace Json {
|
||||||
|
|
||||||
Value const Value::null;
|
Value const Value::null;
|
||||||
Int const Value::minInt = Int(~(UInt(-1) / 2));
|
|
||||||
Int const Value::maxInt = Int(UInt(-1) / 2);
|
|
||||||
UInt const Value::maxUInt = UInt(-1);
|
|
||||||
|
|
||||||
class DefaultValueAllocator : public ValueAllocator
|
class DefaultValueAllocator : public ValueAllocator
|
||||||
{
|
{
|
||||||
@@ -550,6 +547,69 @@ Value::asInt() const
|
|||||||
return 0; // unreachable;
|
return 0; // unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UInt
|
||||||
|
Value::asAbsUInt() const
|
||||||
|
{
|
||||||
|
switch (type_)
|
||||||
|
{
|
||||||
|
case nullValue:
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case intValue: {
|
||||||
|
// Doing this conversion through int64 avoids overflow error for
|
||||||
|
// value_.int_ = -1 * 2^31 i.e. numeric_limits<int>::min().
|
||||||
|
if (value_.int_ < 0)
|
||||||
|
return static_cast<std::int64_t>(value_.int_) * -1;
|
||||||
|
return value_.int_;
|
||||||
|
}
|
||||||
|
|
||||||
|
case uintValue:
|
||||||
|
return value_.uint_;
|
||||||
|
|
||||||
|
case realValue: {
|
||||||
|
if (value_.real_ < 0)
|
||||||
|
{
|
||||||
|
JSON_ASSERT_MESSAGE(
|
||||||
|
-1 * value_.real_ <= maxUInt,
|
||||||
|
"Real out of unsigned integer range");
|
||||||
|
return UInt(-1 * value_.real_);
|
||||||
|
}
|
||||||
|
JSON_ASSERT_MESSAGE(
|
||||||
|
value_.real_ <= maxUInt, "Real out of unsigned integer range");
|
||||||
|
return UInt(value_.real_);
|
||||||
|
}
|
||||||
|
|
||||||
|
case booleanValue:
|
||||||
|
return value_.bool_ ? 1 : 0;
|
||||||
|
|
||||||
|
case stringValue: {
|
||||||
|
char const* const str{value_.string_ ? value_.string_ : ""};
|
||||||
|
auto const temp = beast::lexicalCastThrow<std::int64_t>(str);
|
||||||
|
if (temp < 0)
|
||||||
|
{
|
||||||
|
JSON_ASSERT_MESSAGE(
|
||||||
|
-1 * temp <= maxUInt,
|
||||||
|
"String out of unsigned integer range");
|
||||||
|
return -1 * temp;
|
||||||
|
}
|
||||||
|
JSON_ASSERT_MESSAGE(
|
||||||
|
temp <= maxUInt, "String out of unsigned integer range");
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
case arrayValue:
|
||||||
|
case objectValue:
|
||||||
|
JSON_ASSERT_MESSAGE(false, "Type is not convertible to int");
|
||||||
|
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
default:
|
||||||
|
UNREACHABLE("Json::Value::asAbsInt : invalid type");
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
Value::UInt
|
Value::UInt
|
||||||
Value::asUInt() const
|
Value::asUInt() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1087,7 +1087,7 @@ amountFromJson(SField const& name, Json::Value const& v)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parts.mantissa = -value.asInt();
|
parts.mantissa = value.asAbsUInt();
|
||||||
parts.negative = true;
|
parts.negative = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ numberFromJson(SField const& field, Json::Value const& value)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parts.mantissa = -value.asInt();
|
parts.mantissa = value.asAbsUInt();
|
||||||
parts.negative = true;
|
parts.negative = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -572,7 +572,7 @@ public:
|
|||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
1,
|
1,
|
||||||
std::chrono::seconds{Json::Value::maxInt + 1}}});
|
std::chrono::seconds{Json::Value::minInt}}});
|
||||||
// force an out-of-range validUntil value on the future list
|
// force an out-of-range validUntil value on the future list
|
||||||
// The first list is accepted. The second fails. The parser
|
// The first list is accepted. The second fails. The parser
|
||||||
// returns the "best" result, so this looks like a success.
|
// returns the "best" result, so this looks like a success.
|
||||||
@@ -608,7 +608,7 @@ public:
|
|||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
1,
|
1,
|
||||||
std::chrono::seconds{Json::Value::maxInt + 1},
|
std::chrono::seconds{Json::Value::minInt},
|
||||||
std::chrono::seconds{Json::Value::maxInt - 6000}}});
|
std::chrono::seconds{Json::Value::maxInt - 6000}}});
|
||||||
// verify refresh intervals are properly clamped
|
// verify refresh intervals are properly clamped
|
||||||
testFetchList(
|
testFetchList(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <xrpl/basics/random.h>
|
#include <xrpl/basics/random.h>
|
||||||
#include <xrpl/beast/unit_test.h>
|
#include <xrpl/beast/unit_test.h>
|
||||||
#include <xrpl/protocol/STAmount.h>
|
#include <xrpl/protocol/STAmount.h>
|
||||||
|
#include <xrpl/protocol/XRPAmount.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
@@ -585,6 +586,216 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testParseJson()
|
||||||
|
{
|
||||||
|
static_assert(!std::is_convertible_v<STAmount*, Number*>);
|
||||||
|
|
||||||
|
{
|
||||||
|
STAmount const stnum{sfNumber};
|
||||||
|
BEAST_EXPECT(stnum.getSType() == STI_AMOUNT);
|
||||||
|
BEAST_EXPECT(stnum.getText() == "0");
|
||||||
|
BEAST_EXPECT(stnum.isDefault() == true);
|
||||||
|
BEAST_EXPECT(stnum.value() == Number{0});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(
|
||||||
|
amountFromJson(sfNumber, Json::Value(42)) == XRPAmount(42));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
amountFromJson(sfNumber, Json::Value(-42)) == XRPAmount(-42));
|
||||||
|
|
||||||
|
BEAST_EXPECT(
|
||||||
|
amountFromJson(sfNumber, Json::UInt(42)) == XRPAmount(42));
|
||||||
|
|
||||||
|
BEAST_EXPECT(amountFromJson(sfNumber, "-123") == XRPAmount(-123));
|
||||||
|
|
||||||
|
BEAST_EXPECT(amountFromJson(sfNumber, "123") == XRPAmount(123));
|
||||||
|
BEAST_EXPECT(amountFromJson(sfNumber, "-123") == XRPAmount(-123));
|
||||||
|
|
||||||
|
BEAST_EXPECT(amountFromJson(sfNumber, "3.14e2") == XRPAmount(314));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
amountFromJson(sfNumber, "-3.14e2") == XRPAmount(-314));
|
||||||
|
|
||||||
|
BEAST_EXPECT(amountFromJson(sfNumber, "0") == XRPAmount(0));
|
||||||
|
BEAST_EXPECT(amountFromJson(sfNumber, "-0") == XRPAmount(0));
|
||||||
|
|
||||||
|
constexpr auto imin = std::numeric_limits<int>::min();
|
||||||
|
BEAST_EXPECT(amountFromJson(sfNumber, imin) == XRPAmount(imin));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
amountFromJson(sfNumber, std::to_string(imin)) ==
|
||||||
|
XRPAmount(imin));
|
||||||
|
|
||||||
|
constexpr auto imax = std::numeric_limits<int>::max();
|
||||||
|
BEAST_EXPECT(amountFromJson(sfNumber, imax) == XRPAmount(imax));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
amountFromJson(sfNumber, std::to_string(imax)) ==
|
||||||
|
XRPAmount(imax));
|
||||||
|
|
||||||
|
constexpr auto umax = std::numeric_limits<unsigned int>::max();
|
||||||
|
BEAST_EXPECT(amountFromJson(sfNumber, umax) == XRPAmount(umax));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
amountFromJson(sfNumber, std::to_string(umax)) ==
|
||||||
|
XRPAmount(umax));
|
||||||
|
|
||||||
|
// XRP does not handle fractional part
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto _ = amountFromJson(sfNumber, "0.0");
|
||||||
|
BEAST_EXPECT(false);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error const& e)
|
||||||
|
{
|
||||||
|
std::string const expected =
|
||||||
|
"XRP and MPT must be specified as integral amount.";
|
||||||
|
BEAST_EXPECT(e.what() == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// XRP does not handle fractional part
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto _ = amountFromJson(sfNumber, "1000e-2");
|
||||||
|
BEAST_EXPECT(false);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error const& e)
|
||||||
|
{
|
||||||
|
std::string const expected =
|
||||||
|
"XRP and MPT must be specified as integral amount.";
|
||||||
|
BEAST_EXPECT(e.what() == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obvious non-numbers tested here
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto _ = amountFromJson(sfNumber, "");
|
||||||
|
BEAST_EXPECT(false);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error const& e)
|
||||||
|
{
|
||||||
|
std::string const expected = "'' is not a number";
|
||||||
|
BEAST_EXPECT(e.what() == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto _ = amountFromJson(sfNumber, "e");
|
||||||
|
BEAST_EXPECT(false);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error const& e)
|
||||||
|
{
|
||||||
|
std::string const expected = "'e' is not a number";
|
||||||
|
BEAST_EXPECT(e.what() == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto _ = amountFromJson(sfNumber, "1e");
|
||||||
|
BEAST_EXPECT(false);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error const& e)
|
||||||
|
{
|
||||||
|
std::string const expected = "'1e' is not a number";
|
||||||
|
BEAST_EXPECT(e.what() == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto _ = amountFromJson(sfNumber, "e2");
|
||||||
|
BEAST_EXPECT(false);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error const& e)
|
||||||
|
{
|
||||||
|
std::string const expected = "'e2' is not a number";
|
||||||
|
BEAST_EXPECT(e.what() == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto _ = amountFromJson(sfNumber, Json::Value());
|
||||||
|
BEAST_EXPECT(false);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error const& e)
|
||||||
|
{
|
||||||
|
std::string const expected =
|
||||||
|
"XRP may not be specified with a null Json value";
|
||||||
|
BEAST_EXPECT(e.what() == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto _ = amountFromJson(
|
||||||
|
sfNumber,
|
||||||
|
"123456789012345678901234567890123456789012345678901234"
|
||||||
|
"5678"
|
||||||
|
"901234567890123456789012345678901234567890123456789012"
|
||||||
|
"3456"
|
||||||
|
"78901234567890123456789012345678901234567890");
|
||||||
|
BEAST_EXPECT(false);
|
||||||
|
}
|
||||||
|
catch (std::bad_cast const& e)
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do not handle leading zeros
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto _ = amountFromJson(sfNumber, "001");
|
||||||
|
BEAST_EXPECT(false);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error const& e)
|
||||||
|
{
|
||||||
|
std::string const expected = "'001' is not a number";
|
||||||
|
BEAST_EXPECT(e.what() == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto _ = amountFromJson(sfNumber, "000.0");
|
||||||
|
BEAST_EXPECT(false);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error const& e)
|
||||||
|
{
|
||||||
|
std::string const expected = "'000.0' is not a number";
|
||||||
|
BEAST_EXPECT(e.what() == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do not handle dangling dot
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto _ = amountFromJson(sfNumber, ".1");
|
||||||
|
BEAST_EXPECT(false);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error const& e)
|
||||||
|
{
|
||||||
|
std::string const expected = "'.1' is not a number";
|
||||||
|
BEAST_EXPECT(e.what() == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto _ = amountFromJson(sfNumber, "1.");
|
||||||
|
BEAST_EXPECT(false);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error const& e)
|
||||||
|
{
|
||||||
|
std::string const expected = "'1.' is not a number";
|
||||||
|
BEAST_EXPECT(e.what() == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto _ = amountFromJson(sfNumber, "1.e3");
|
||||||
|
BEAST_EXPECT(false);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error const& e)
|
||||||
|
{
|
||||||
|
std::string const expected = "'1.e3' is not a number";
|
||||||
|
BEAST_EXPECT(e.what() == expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
testConvertXRP()
|
testConvertXRP()
|
||||||
{
|
{
|
||||||
@@ -1022,6 +1233,7 @@ public:
|
|||||||
testArithmetic();
|
testArithmetic();
|
||||||
testUnderflow();
|
testUnderflow();
|
||||||
testRounding();
|
testRounding();
|
||||||
|
testParseJson();
|
||||||
testConvertXRP();
|
testConvertXRP();
|
||||||
testConvertIOU();
|
testConvertIOU();
|
||||||
testCanAddXRP();
|
testCanAddXRP();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <xrpl/beast/unit_test/suite.h>
|
#include <xrpl/beast/unit_test/suite.h>
|
||||||
#include <xrpl/json/json_forwards.h>
|
#include <xrpl/json/json_forwards.h>
|
||||||
#include <xrpl/protocol/Issue.h>
|
#include <xrpl/protocol/Issue.h>
|
||||||
|
#include <xrpl/protocol/SField.h>
|
||||||
#include <xrpl/protocol/STAmount.h>
|
#include <xrpl/protocol/STAmount.h>
|
||||||
#include <xrpl/protocol/STNumber.h>
|
#include <xrpl/protocol/STNumber.h>
|
||||||
|
|
||||||
@@ -126,6 +127,30 @@ struct STNumber_test : public beast::unit_test::suite
|
|||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
numberFromJson(sfNumber, "-0.000e6") == STNumber(sfNumber, 0));
|
numberFromJson(sfNumber, "-0.000e6") == STNumber(sfNumber, 0));
|
||||||
|
|
||||||
|
constexpr auto imin = std::numeric_limits<int>::min();
|
||||||
|
BEAST_EXPECT(
|
||||||
|
numberFromJson(sfNumber, imin) ==
|
||||||
|
STNumber(sfNumber, Number(imin, 0)));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
numberFromJson(sfNumber, std::to_string(imin)) ==
|
||||||
|
STNumber(sfNumber, Number(imin, 0)));
|
||||||
|
|
||||||
|
constexpr auto imax = std::numeric_limits<int>::max();
|
||||||
|
BEAST_EXPECT(
|
||||||
|
numberFromJson(sfNumber, imax) ==
|
||||||
|
STNumber(sfNumber, Number(imax, 0)));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
numberFromJson(sfNumber, std::to_string(imax)) ==
|
||||||
|
STNumber(sfNumber, Number(imax, 0)));
|
||||||
|
|
||||||
|
constexpr auto umax = std::numeric_limits<unsigned int>::max();
|
||||||
|
BEAST_EXPECT(
|
||||||
|
numberFromJson(sfNumber, umax) ==
|
||||||
|
STNumber(sfNumber, Number(umax, 0)));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
numberFromJson(sfNumber, std::to_string(umax)) ==
|
||||||
|
STNumber(sfNumber, Number(umax, 0)));
|
||||||
|
|
||||||
// Obvious non-numbers tested here
|
// Obvious non-numbers tested here
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <doctest/doctest.h>
|
#include <doctest/doctest.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -15,6 +16,14 @@ namespace ripple {
|
|||||||
|
|
||||||
TEST_SUITE_BEGIN("json_value");
|
TEST_SUITE_BEGIN("json_value");
|
||||||
|
|
||||||
|
TEST_CASE("limits")
|
||||||
|
{
|
||||||
|
using namespace Json;
|
||||||
|
static_assert(Value::minInt == Int(~(UInt(-1) / 2)));
|
||||||
|
static_assert(Value::maxInt == Int(UInt(-1) / 2));
|
||||||
|
static_assert(Value::maxUInt == UInt(-1));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("construct and compare Json::StaticString")
|
TEST_CASE("construct and compare Json::StaticString")
|
||||||
{
|
{
|
||||||
static constexpr char sample[]{"Contents of a Json::StaticString"};
|
static constexpr char sample[]{"Contents of a Json::StaticString"};
|
||||||
@@ -582,8 +591,6 @@ TEST_CASE("bad json")
|
|||||||
|
|
||||||
TEST_CASE("edge cases")
|
TEST_CASE("edge cases")
|
||||||
{
|
{
|
||||||
std::string json;
|
|
||||||
|
|
||||||
std::uint32_t max_uint = std::numeric_limits<std::uint32_t>::max();
|
std::uint32_t max_uint = std::numeric_limits<std::uint32_t>::max();
|
||||||
std::int32_t max_int = std::numeric_limits<std::int32_t>::max();
|
std::int32_t max_int = std::numeric_limits<std::int32_t>::max();
|
||||||
std::int32_t min_int = std::numeric_limits<std::int32_t>::min();
|
std::int32_t min_int = std::numeric_limits<std::int32_t>::min();
|
||||||
@@ -592,71 +599,145 @@ TEST_CASE("edge cases")
|
|||||||
std::int32_t a_large_int = max_int - 1978;
|
std::int32_t a_large_int = max_int - 1978;
|
||||||
std::int32_t a_small_int = min_int + 1978;
|
std::int32_t a_small_int = min_int + 1978;
|
||||||
|
|
||||||
json = "{\"max_uint\":" + std::to_string(max_uint);
|
{
|
||||||
json += ",\"max_int\":" + std::to_string(max_int);
|
std::string json = "{\"max_uint\":" + std::to_string(max_uint);
|
||||||
json += ",\"min_int\":" + std::to_string(min_int);
|
json += ",\"max_int\":" + std::to_string(max_int);
|
||||||
json += ",\"a_uint\":" + std::to_string(a_uint);
|
json += ",\"min_int\":" + std::to_string(min_int);
|
||||||
json += ",\"a_large_int\":" + std::to_string(a_large_int);
|
json += ",\"a_uint\":" + std::to_string(a_uint);
|
||||||
json += ",\"a_small_int\":" + std::to_string(a_small_int);
|
json += ",\"a_large_int\":" + std::to_string(a_large_int);
|
||||||
json += "}";
|
json += ",\"a_small_int\":" + std::to_string(a_small_int);
|
||||||
|
json += "}";
|
||||||
|
|
||||||
Json::Value j1;
|
Json::Value j1;
|
||||||
Json::Reader r1;
|
Json::Reader r1;
|
||||||
|
|
||||||
CHECK(r1.parse(json, j1));
|
CHECK(r1.parse(json, j1));
|
||||||
CHECK(j1["max_uint"].asUInt() == max_uint);
|
CHECK(j1["max_uint"].asUInt() == max_uint);
|
||||||
CHECK(j1["max_int"].asInt() == max_int);
|
CHECK(j1["max_uint"].asAbsUInt() == max_uint);
|
||||||
CHECK(j1["min_int"].asInt() == min_int);
|
CHECK(j1["max_int"].asInt() == max_int);
|
||||||
CHECK(j1["a_uint"].asUInt() == a_uint);
|
CHECK(j1["max_int"].asAbsUInt() == max_int);
|
||||||
CHECK(j1["a_uint"] > a_large_int);
|
CHECK(j1["min_int"].asInt() == min_int);
|
||||||
CHECK(j1["a_uint"] > a_small_int);
|
CHECK(
|
||||||
CHECK(j1["a_large_int"].asInt() == a_large_int);
|
j1["min_int"].asAbsUInt() ==
|
||||||
CHECK(j1["a_large_int"].asUInt() == a_large_int);
|
static_cast<std::int64_t>(min_int) * -1);
|
||||||
CHECK(j1["a_large_int"] < a_uint);
|
CHECK(j1["a_uint"].asUInt() == a_uint);
|
||||||
CHECK(j1["a_small_int"].asInt() == a_small_int);
|
CHECK(j1["a_uint"].asAbsUInt() == a_uint);
|
||||||
CHECK(j1["a_small_int"] < a_uint);
|
CHECK(j1["a_uint"] > a_large_int);
|
||||||
|
CHECK(j1["a_uint"] > a_small_int);
|
||||||
|
CHECK(j1["a_large_int"].asInt() == a_large_int);
|
||||||
|
CHECK(j1["a_large_int"].asAbsUInt() == a_large_int);
|
||||||
|
CHECK(j1["a_large_int"].asUInt() == a_large_int);
|
||||||
|
CHECK(j1["a_large_int"] < a_uint);
|
||||||
|
CHECK(j1["a_small_int"].asInt() == a_small_int);
|
||||||
|
CHECK(
|
||||||
|
j1["a_small_int"].asAbsUInt() ==
|
||||||
|
static_cast<std::int64_t>(a_small_int) * -1);
|
||||||
|
CHECK(j1["a_small_int"] < a_uint);
|
||||||
|
}
|
||||||
|
|
||||||
json = "{\"overflow\":";
|
std::uint64_t overflow = std::uint64_t(max_uint) + 1;
|
||||||
json += std::to_string(std::uint64_t(max_uint) + 1);
|
{
|
||||||
json += "}";
|
std::string json = "{\"overflow\":";
|
||||||
|
json += std::to_string(overflow);
|
||||||
|
json += "}";
|
||||||
|
|
||||||
Json::Value j2;
|
Json::Value j2;
|
||||||
Json::Reader r2;
|
Json::Reader r2;
|
||||||
|
|
||||||
CHECK(!r2.parse(json, j2));
|
CHECK(!r2.parse(json, j2));
|
||||||
|
}
|
||||||
|
|
||||||
json = "{\"underflow\":";
|
std::int64_t underflow = std::int64_t(min_int) - 1;
|
||||||
json += std::to_string(std::int64_t(min_int) - 1);
|
{
|
||||||
json += "}";
|
std::string json = "{\"underflow\":";
|
||||||
|
json += std::to_string(underflow);
|
||||||
|
json += "}";
|
||||||
|
|
||||||
Json::Value j3;
|
Json::Value j3;
|
||||||
Json::Reader r3;
|
Json::Reader r3;
|
||||||
|
|
||||||
CHECK(!r3.parse(json, j3));
|
CHECK(!r3.parse(json, j3));
|
||||||
|
}
|
||||||
|
|
||||||
Json::Value intString{"4294967296"};
|
{
|
||||||
CHECK_THROWS_AS(intString.asUInt(), beast::BadLexicalCast);
|
Json::Value intString{std::to_string(overflow)};
|
||||||
|
CHECK_THROWS_AS(intString.asUInt(), beast::BadLexicalCast);
|
||||||
|
CHECK_THROWS_AS(intString.asAbsUInt(), Json::error);
|
||||||
|
|
||||||
intString = "4294967295";
|
intString = "4294967295";
|
||||||
CHECK(intString.asUInt() == 4294967295u);
|
CHECK(intString.asUInt() == 4294967295u);
|
||||||
|
CHECK(intString.asAbsUInt() == 4294967295u);
|
||||||
|
|
||||||
intString = "0";
|
intString = "0";
|
||||||
CHECK(intString.asUInt() == 0);
|
CHECK(intString.asUInt() == 0);
|
||||||
|
CHECK(intString.asAbsUInt() == 0);
|
||||||
|
|
||||||
intString = "-1";
|
intString = "-1";
|
||||||
CHECK_THROWS_AS(intString.asUInt(), beast::BadLexicalCast);
|
CHECK_THROWS_AS(intString.asUInt(), beast::BadLexicalCast);
|
||||||
|
CHECK(intString.asAbsUInt() == 1);
|
||||||
|
|
||||||
intString = "2147483648";
|
intString = "-4294967295";
|
||||||
CHECK_THROWS_AS(intString.asInt(), beast::BadLexicalCast);
|
CHECK(intString.asAbsUInt() == 4294967295);
|
||||||
|
|
||||||
intString = "2147483647";
|
intString = "-4294967296";
|
||||||
CHECK(intString.asInt() == 2147483647);
|
CHECK_THROWS_AS(intString.asAbsUInt(), Json::error);
|
||||||
|
|
||||||
intString = "-2147483648";
|
intString = "2147483648";
|
||||||
CHECK(intString.asInt() == -2147483648LL); // MSVC wants the LL
|
CHECK_THROWS_AS(intString.asInt(), beast::BadLexicalCast);
|
||||||
|
CHECK(intString.asAbsUInt() == 2147483648);
|
||||||
|
|
||||||
intString = "-2147483649";
|
intString = "2147483647";
|
||||||
CHECK_THROWS_AS(intString.asInt(), beast::BadLexicalCast);
|
CHECK(intString.asInt() == 2147483647);
|
||||||
|
CHECK(intString.asAbsUInt() == 2147483647);
|
||||||
|
|
||||||
|
intString = "-2147483648";
|
||||||
|
CHECK(intString.asInt() == -2147483648LL); // MSVC wants the LL
|
||||||
|
CHECK(intString.asAbsUInt() == 2147483648LL);
|
||||||
|
|
||||||
|
intString = "-2147483649";
|
||||||
|
CHECK_THROWS_AS(intString.asInt(), beast::BadLexicalCast);
|
||||||
|
CHECK(intString.asAbsUInt() == 2147483649);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Json::Value intReal{4294967297.0};
|
||||||
|
CHECK_THROWS_AS(intReal.asUInt(), Json::error);
|
||||||
|
CHECK_THROWS_AS(intReal.asAbsUInt(), Json::error);
|
||||||
|
|
||||||
|
intReal = 4294967295.0;
|
||||||
|
CHECK(intReal.asUInt() == 4294967295u);
|
||||||
|
CHECK(intReal.asAbsUInt() == 4294967295u);
|
||||||
|
|
||||||
|
intReal = 0.0;
|
||||||
|
CHECK(intReal.asUInt() == 0);
|
||||||
|
CHECK(intReal.asAbsUInt() == 0);
|
||||||
|
|
||||||
|
intReal = -1.0;
|
||||||
|
CHECK_THROWS_AS(intReal.asUInt(), Json::error);
|
||||||
|
CHECK(intReal.asAbsUInt() == 1);
|
||||||
|
|
||||||
|
intReal = -4294967295.0;
|
||||||
|
CHECK(intReal.asAbsUInt() == 4294967295);
|
||||||
|
|
||||||
|
intReal = -4294967296.0;
|
||||||
|
CHECK_THROWS_AS(intReal.asAbsUInt(), Json::error);
|
||||||
|
|
||||||
|
intReal = 2147483648.0;
|
||||||
|
CHECK_THROWS_AS(intReal.asInt(), Json::error);
|
||||||
|
CHECK(intReal.asAbsUInt() == 2147483648);
|
||||||
|
|
||||||
|
intReal = 2147483647.0;
|
||||||
|
CHECK(intReal.asInt() == 2147483647);
|
||||||
|
CHECK(intReal.asAbsUInt() == 2147483647);
|
||||||
|
|
||||||
|
intReal = -2147483648.0;
|
||||||
|
CHECK(intReal.asInt() == -2147483648LL); // MSVC wants the LL
|
||||||
|
CHECK(intReal.asAbsUInt() == 2147483648LL);
|
||||||
|
|
||||||
|
intReal = -2147483649.0;
|
||||||
|
CHECK_THROWS_AS(intReal.asInt(), Json::error);
|
||||||
|
CHECK(intReal.asAbsUInt() == 2147483649);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("copy")
|
TEST_CASE("copy")
|
||||||
@@ -793,6 +874,7 @@ TEST_CASE("conversions")
|
|||||||
CHECK(val.asString() == "");
|
CHECK(val.asString() == "");
|
||||||
CHECK(val.asInt() == 0);
|
CHECK(val.asInt() == 0);
|
||||||
CHECK(val.asUInt() == 0);
|
CHECK(val.asUInt() == 0);
|
||||||
|
CHECK(val.asAbsUInt() == 0);
|
||||||
CHECK(val.asDouble() == 0.0);
|
CHECK(val.asDouble() == 0.0);
|
||||||
CHECK(val.asBool() == false);
|
CHECK(val.asBool() == false);
|
||||||
|
|
||||||
@@ -813,6 +895,7 @@ TEST_CASE("conversions")
|
|||||||
CHECK(val.asString() == "-1234");
|
CHECK(val.asString() == "-1234");
|
||||||
CHECK(val.asInt() == -1234);
|
CHECK(val.asInt() == -1234);
|
||||||
CHECK_THROWS_AS(val.asUInt(), Json::error);
|
CHECK_THROWS_AS(val.asUInt(), Json::error);
|
||||||
|
CHECK(val.asAbsUInt() == 1234u);
|
||||||
CHECK(val.asDouble() == -1234.0);
|
CHECK(val.asDouble() == -1234.0);
|
||||||
CHECK(val.asBool() == true);
|
CHECK(val.asBool() == true);
|
||||||
|
|
||||||
@@ -833,6 +916,7 @@ TEST_CASE("conversions")
|
|||||||
CHECK(val.asString() == "1234");
|
CHECK(val.asString() == "1234");
|
||||||
CHECK(val.asInt() == 1234);
|
CHECK(val.asInt() == 1234);
|
||||||
CHECK(val.asUInt() == 1234u);
|
CHECK(val.asUInt() == 1234u);
|
||||||
|
CHECK(val.asAbsUInt() == 1234u);
|
||||||
CHECK(val.asDouble() == 1234.0);
|
CHECK(val.asDouble() == 1234.0);
|
||||||
CHECK(val.asBool() == true);
|
CHECK(val.asBool() == true);
|
||||||
|
|
||||||
@@ -853,6 +937,7 @@ TEST_CASE("conversions")
|
|||||||
CHECK(std::regex_match(val.asString(), std::regex("^2\\.0*$")));
|
CHECK(std::regex_match(val.asString(), std::regex("^2\\.0*$")));
|
||||||
CHECK(val.asInt() == 2);
|
CHECK(val.asInt() == 2);
|
||||||
CHECK(val.asUInt() == 2u);
|
CHECK(val.asUInt() == 2u);
|
||||||
|
CHECK(val.asAbsUInt() == 2u);
|
||||||
CHECK(val.asDouble() == 2.0);
|
CHECK(val.asDouble() == 2.0);
|
||||||
CHECK(val.asBool() == true);
|
CHECK(val.asBool() == true);
|
||||||
|
|
||||||
@@ -873,6 +958,7 @@ TEST_CASE("conversions")
|
|||||||
CHECK(val.asString() == "54321");
|
CHECK(val.asString() == "54321");
|
||||||
CHECK(val.asInt() == 54321);
|
CHECK(val.asInt() == 54321);
|
||||||
CHECK(val.asUInt() == 54321u);
|
CHECK(val.asUInt() == 54321u);
|
||||||
|
CHECK(val.asAbsUInt() == 54321);
|
||||||
CHECK_THROWS_AS(val.asDouble(), Json::error);
|
CHECK_THROWS_AS(val.asDouble(), Json::error);
|
||||||
CHECK(val.asBool() == true);
|
CHECK(val.asBool() == true);
|
||||||
|
|
||||||
@@ -893,6 +979,7 @@ TEST_CASE("conversions")
|
|||||||
CHECK(val.asString() == "");
|
CHECK(val.asString() == "");
|
||||||
CHECK_THROWS_AS(val.asInt(), std::exception);
|
CHECK_THROWS_AS(val.asInt(), std::exception);
|
||||||
CHECK_THROWS_AS(val.asUInt(), std::exception);
|
CHECK_THROWS_AS(val.asUInt(), std::exception);
|
||||||
|
CHECK_THROWS_AS(val.asAbsUInt(), std::exception);
|
||||||
CHECK_THROWS_AS(val.asDouble(), std::exception);
|
CHECK_THROWS_AS(val.asDouble(), std::exception);
|
||||||
CHECK(val.asBool() == false);
|
CHECK(val.asBool() == false);
|
||||||
|
|
||||||
@@ -913,6 +1000,7 @@ TEST_CASE("conversions")
|
|||||||
CHECK(val.asString() == "false");
|
CHECK(val.asString() == "false");
|
||||||
CHECK(val.asInt() == 0);
|
CHECK(val.asInt() == 0);
|
||||||
CHECK(val.asUInt() == 0);
|
CHECK(val.asUInt() == 0);
|
||||||
|
CHECK(val.asAbsUInt() == 0);
|
||||||
CHECK(val.asDouble() == 0.0);
|
CHECK(val.asDouble() == 0.0);
|
||||||
CHECK(val.asBool() == false);
|
CHECK(val.asBool() == false);
|
||||||
|
|
||||||
@@ -933,6 +1021,7 @@ TEST_CASE("conversions")
|
|||||||
CHECK(val.asString() == "true");
|
CHECK(val.asString() == "true");
|
||||||
CHECK(val.asInt() == 1);
|
CHECK(val.asInt() == 1);
|
||||||
CHECK(val.asUInt() == 1);
|
CHECK(val.asUInt() == 1);
|
||||||
|
CHECK(val.asAbsUInt() == 1);
|
||||||
CHECK(val.asDouble() == 1.0);
|
CHECK(val.asDouble() == 1.0);
|
||||||
CHECK(val.asBool() == true);
|
CHECK(val.asBool() == true);
|
||||||
|
|
||||||
@@ -953,6 +1042,7 @@ TEST_CASE("conversions")
|
|||||||
CHECK_THROWS_AS(val.asString(), Json::error);
|
CHECK_THROWS_AS(val.asString(), Json::error);
|
||||||
CHECK_THROWS_AS(val.asInt(), Json::error);
|
CHECK_THROWS_AS(val.asInt(), Json::error);
|
||||||
CHECK_THROWS_AS(val.asUInt(), Json::error);
|
CHECK_THROWS_AS(val.asUInt(), Json::error);
|
||||||
|
CHECK_THROWS_AS(val.asAbsUInt(), Json::error);
|
||||||
CHECK_THROWS_AS(val.asDouble(), Json::error);
|
CHECK_THROWS_AS(val.asDouble(), Json::error);
|
||||||
CHECK(val.asBool() == false); // empty or not
|
CHECK(val.asBool() == false); // empty or not
|
||||||
|
|
||||||
@@ -973,6 +1063,7 @@ TEST_CASE("conversions")
|
|||||||
CHECK_THROWS_AS(val.asString(), Json::error);
|
CHECK_THROWS_AS(val.asString(), Json::error);
|
||||||
CHECK_THROWS_AS(val.asInt(), Json::error);
|
CHECK_THROWS_AS(val.asInt(), Json::error);
|
||||||
CHECK_THROWS_AS(val.asUInt(), Json::error);
|
CHECK_THROWS_AS(val.asUInt(), Json::error);
|
||||||
|
CHECK_THROWS_AS(val.asAbsUInt(), Json::error);
|
||||||
CHECK_THROWS_AS(val.asDouble(), Json::error);
|
CHECK_THROWS_AS(val.asDouble(), Json::error);
|
||||||
CHECK(val.asBool() == false); // empty or not
|
CHECK(val.asBool() == false); // empty or not
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user