mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +00:00
Integrate STNumber with STParsedJSON
This commit is contained in:
committed by
Bronek Kozicki
parent
49e0d54c76
commit
1a032f04e3
@@ -417,7 +417,7 @@ STAmount
|
||||
amountFromQuality(std::uint64_t rate);
|
||||
|
||||
STAmount
|
||||
amountFromString(Asset const& issue, std::string const& amount);
|
||||
amountFromString(Asset const& asset, std::string const& amount);
|
||||
|
||||
STAmount
|
||||
amountFromJson(SField const& name, Json::Value const& v);
|
||||
|
||||
@@ -83,6 +83,19 @@ private:
|
||||
std::ostream&
|
||||
operator<<(std::ostream& out, STNumber const& rhs);
|
||||
|
||||
struct NumberParts
|
||||
{
|
||||
std::uint64_t mantissa = 0;
|
||||
int exponent = 0;
|
||||
bool negative = false;
|
||||
};
|
||||
|
||||
NumberParts
|
||||
partsFromString(std::string const& number);
|
||||
|
||||
STNumber
|
||||
numberFromJson(SField const& field, Json::Value const& value);
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,15 +20,14 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/beast/core/LexicalCast.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STNumber.h>
|
||||
#include <xrpl/protocol/SystemParameters.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
@@ -833,75 +832,16 @@ amountFromQuality(std::uint64_t rate)
|
||||
STAmount
|
||||
amountFromString(Asset const& asset, std::string const& amount)
|
||||
{
|
||||
static boost::regex const reNumber(
|
||||
"^" // the beginning of the string
|
||||
"([-+]?)" // (optional) + or - character
|
||||
"(0|[1-9][0-9]*)" // a number (no leading zeroes, unless 0)
|
||||
"(\\.([0-9]+))?" // (optional) period followed by any number
|
||||
"([eE]([+-]?)([0-9]+))?" // (optional) E, optional + or -, any number
|
||||
"$",
|
||||
boost::regex_constants::optimize);
|
||||
|
||||
boost::smatch match;
|
||||
|
||||
if (!boost::regex_match(amount, match, reNumber))
|
||||
Throw<std::runtime_error>("Number '" + amount + "' is not valid");
|
||||
|
||||
// Match fields:
|
||||
// 0 = whole input
|
||||
// 1 = sign
|
||||
// 2 = integer portion
|
||||
// 3 = whole fraction (with '.')
|
||||
// 4 = fraction (without '.')
|
||||
// 5 = whole exponent (with 'e')
|
||||
// 6 = exponent sign
|
||||
// 7 = exponent number
|
||||
|
||||
// CHECKME: Why 32? Shouldn't this be 16?
|
||||
if ((match[2].length() + match[4].length()) > 32)
|
||||
Throw<std::runtime_error>("Number '" + amount + "' is overlong");
|
||||
|
||||
bool negative = (match[1].matched && (match[1] == "-"));
|
||||
|
||||
// Can't specify XRP or MPT using fractional representation
|
||||
if ((asset.native() || asset.holds<MPTIssue>()) && match[3].matched)
|
||||
auto const parts = partsFromString(amount);
|
||||
if ((asset.native() || asset.holds<MPTIssue>()) && parts.exponent < 0)
|
||||
Throw<std::runtime_error>(
|
||||
"XRP and MPT must be specified as integral amount.");
|
||||
|
||||
std::uint64_t mantissa;
|
||||
int exponent;
|
||||
|
||||
if (!match[4].matched) // integer only
|
||||
{
|
||||
mantissa =
|
||||
beast::lexicalCastThrow<std::uint64_t>(std::string(match[2]));
|
||||
exponent = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// integer and fraction
|
||||
mantissa = beast::lexicalCastThrow<std::uint64_t>(match[2] + match[4]);
|
||||
exponent = -(match[4].length());
|
||||
}
|
||||
|
||||
if (match[5].matched)
|
||||
{
|
||||
// we have an exponent
|
||||
if (match[6].matched && (match[6] == "-"))
|
||||
exponent -= beast::lexicalCastThrow<int>(std::string(match[7]));
|
||||
else
|
||||
exponent += beast::lexicalCastThrow<int>(std::string(match[7]));
|
||||
}
|
||||
|
||||
return {asset, mantissa, exponent, negative};
|
||||
return {asset, parts.mantissa, parts.exponent, parts.negative};
|
||||
}
|
||||
|
||||
STAmount
|
||||
amountFromJson(SField const& name, Json::Value const& v)
|
||||
{
|
||||
STAmount::mantissa_type mantissa = 0;
|
||||
STAmount::exponent_type exponent = 0;
|
||||
bool negative = false;
|
||||
Asset asset;
|
||||
|
||||
Json::Value value;
|
||||
@@ -993,36 +933,38 @@ amountFromJson(SField const& name, Json::Value const& v)
|
||||
}
|
||||
}
|
||||
|
||||
NumberParts parts;
|
||||
|
||||
if (value.isInt())
|
||||
{
|
||||
if (value.asInt() >= 0)
|
||||
{
|
||||
mantissa = value.asInt();
|
||||
parts.mantissa = value.asInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
mantissa = -value.asInt();
|
||||
negative = true;
|
||||
parts.mantissa = -value.asInt();
|
||||
parts.negative = true;
|
||||
}
|
||||
}
|
||||
else if (value.isUInt())
|
||||
{
|
||||
mantissa = v.asUInt();
|
||||
parts.mantissa = v.asUInt();
|
||||
}
|
||||
else if (value.isString())
|
||||
{
|
||||
auto const ret = amountFromString(asset, value.asString());
|
||||
|
||||
mantissa = ret.mantissa();
|
||||
exponent = ret.exponent();
|
||||
negative = ret.negative();
|
||||
parts = partsFromString(value.asString());
|
||||
// Can't specify XRP or MPT using fractional representation
|
||||
if ((asset.native() || asset.holds<MPTIssue>()) && parts.exponent < 0)
|
||||
Throw<std::runtime_error>(
|
||||
"XRP and MPT must be specified as integral amount.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Throw<std::runtime_error>("invalid amount type");
|
||||
}
|
||||
|
||||
return {name, asset, mantissa, exponent, negative};
|
||||
return {name, asset, parts.mantissa, parts.exponent, parts.negative};
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -19,8 +19,10 @@
|
||||
|
||||
#include <xrpl/protocol/STNumber.h>
|
||||
|
||||
#include <xrpl/beast/core/LexicalCast.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -108,4 +110,101 @@ operator<<(std::ostream& out, STNumber const& rhs)
|
||||
return out << rhs.getText();
|
||||
}
|
||||
|
||||
NumberParts
|
||||
partsFromString(std::string const& number)
|
||||
{
|
||||
static boost::regex const reNumber(
|
||||
"^" // the beginning of the string
|
||||
"([-+]?)" // (optional) + or - character
|
||||
"(0|[1-9][0-9]*)" // a number (no leading zeroes, unless 0)
|
||||
"(\\.([0-9]+))?" // (optional) period followed by any number
|
||||
"([eE]([+-]?)([0-9]+))?" // (optional) E, optional + or -, any number
|
||||
"$",
|
||||
boost::regex_constants::optimize);
|
||||
|
||||
boost::smatch match;
|
||||
|
||||
if (!boost::regex_match(number, match, reNumber))
|
||||
Throw<std::runtime_error>("'" + number + "' is not a number");
|
||||
|
||||
// Match fields:
|
||||
// 0 = whole input
|
||||
// 1 = sign
|
||||
// 2 = integer portion
|
||||
// 3 = whole fraction (with '.')
|
||||
// 4 = fraction (without '.')
|
||||
// 5 = whole exponent (with 'e')
|
||||
// 6 = exponent sign
|
||||
// 7 = exponent number
|
||||
|
||||
bool negative = (match[1].matched && (match[1] == "-"));
|
||||
|
||||
std::uint64_t mantissa;
|
||||
int exponent;
|
||||
|
||||
if (!match[4].matched) // integer only
|
||||
{
|
||||
mantissa =
|
||||
beast::lexicalCastThrow<std::uint64_t>(std::string(match[2]));
|
||||
exponent = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// integer and fraction
|
||||
mantissa = beast::lexicalCastThrow<std::uint64_t>(match[2] + match[4]);
|
||||
exponent = -(match[4].length());
|
||||
}
|
||||
|
||||
if (match[5].matched)
|
||||
{
|
||||
// we have an exponent
|
||||
if (match[6].matched && (match[6] == "-"))
|
||||
exponent -= beast::lexicalCastThrow<int>(std::string(match[7]));
|
||||
else
|
||||
exponent += beast::lexicalCastThrow<int>(std::string(match[7]));
|
||||
}
|
||||
|
||||
return {mantissa, exponent, negative};
|
||||
}
|
||||
|
||||
STNumber
|
||||
numberFromJson(SField const& field, Json::Value const& value)
|
||||
{
|
||||
NumberParts parts;
|
||||
|
||||
if (value.isInt())
|
||||
{
|
||||
if (value.asInt() >= 0)
|
||||
{
|
||||
parts.mantissa = value.asInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
parts.mantissa = -value.asInt();
|
||||
parts.negative = true;
|
||||
}
|
||||
}
|
||||
else if (value.isUInt())
|
||||
{
|
||||
parts.mantissa = value.asUInt();
|
||||
}
|
||||
else if (value.isString())
|
||||
{
|
||||
parts = partsFromString(value.asString());
|
||||
// Only strings can represent out-of-range values.
|
||||
if (parts.mantissa > std::numeric_limits<std::int64_t>::max())
|
||||
Throw<std::range_error>("too high");
|
||||
}
|
||||
else
|
||||
{
|
||||
Throw<std::runtime_error>("not a number");
|
||||
}
|
||||
|
||||
std::int64_t mantissa = parts.mantissa;
|
||||
if (parts.negative)
|
||||
mantissa = -mantissa;
|
||||
|
||||
return STNumber{field, Number{mantissa, parts.exponent}};
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <xrpl/protocol/STBlob.h>
|
||||
#include <xrpl/protocol/STInteger.h>
|
||||
#include <xrpl/protocol/STIssue.h>
|
||||
#include <xrpl/protocol/STNumber.h>
|
||||
#include <xrpl/protocol/STParsedJSON.h>
|
||||
#include <xrpl/protocol/STPathSet.h>
|
||||
#include <xrpl/protocol/STVector256.h>
|
||||
@@ -574,6 +575,20 @@ parseLeaf(
|
||||
|
||||
break;
|
||||
|
||||
case STI_NUMBER:
|
||||
try
|
||||
{
|
||||
ret =
|
||||
detail::make_stvar<STNumber>(numberFromJson(field, value));
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
error = invalid_data(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case STI_VECTOR256:
|
||||
if (!value.isArrayOrNull())
|
||||
{
|
||||
|
||||
@@ -188,6 +188,9 @@ STVar::constructST(SerializedTypeID id, int depth, Args&&... args)
|
||||
case STI_AMOUNT:
|
||||
construct<STAmount>(std::forward<Args>(args)...);
|
||||
return;
|
||||
case STI_NUMBER:
|
||||
construct<STNumber>(std::forward<Args>(args)...);
|
||||
return;
|
||||
case STI_UINT128:
|
||||
construct<STUInt128>(std::forward<Args>(args)...);
|
||||
return;
|
||||
|
||||
@@ -78,6 +78,15 @@ struct STNumber_test : public beast::unit_test::suite
|
||||
STAmount const totalAmount{totalValue, strikePrice.issue()};
|
||||
BEAST_EXPECT(totalAmount == Number{10'000});
|
||||
}
|
||||
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
numberFromJson(sfNumber, "123") == STNumber(sfNumber, 123));
|
||||
BEAST_EXPECT(
|
||||
numberFromJson(sfNumber, "3.14e2") == STNumber(sfNumber, 314));
|
||||
BEAST_EXPECT(
|
||||
numberFromJson(sfNumber, "1000e-2") == STNumber(sfNumber, 10));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user