Break out the portion that sets the value from the STAmount code.

Fix its mishandling of negative amounts.
Support array types in STAmount JSON input.
This commit is contained in:
JoelKatz
2012-10-02 04:28:01 -07:00
parent 17a99e686b
commit ddab25ab44
2 changed files with 114 additions and 84 deletions

View File

@@ -57,16 +57,22 @@ std::string STAmount::getHumanCurrency() const
}
STAmount::STAmount(SField::ref n, const Json::Value& v)
: SerializedType(n), mValue(0), mOffset(0), mIsNative(false), mIsNegative(false)
: SerializedType(n), mValue(0), mOffset(0), mIsNegative(false)
{
Json::Value value, currency, issuer;
if (v.isArray())
if (v.isObject())
{
value = v["value"];
currency = v["currency"];
issuer = v["issuer"];
}
else if (v.isArray())
{
value = v.get(Json::UInt(0), 0);
currency = v.get(Json::UInt(1), Json::nullValue);
issuer = v.get(Json::UInt(2), Json::nullValue);
}
else if (v.isString())
{
std::string val = v.asString();
@@ -85,6 +91,8 @@ STAmount::STAmount(SField::ref n, const Json::Value& v)
else
value = v;
mIsNative = !currency.isString() || currency.asString().empty() || (currency.asString() == SYSTEM_CURRENCY_CODE);
if (value.isInt())
{
if (value.asInt() >= 0)
@@ -97,29 +105,27 @@ STAmount::STAmount(SField::ref n, const Json::Value& v)
}
else if (value.isUInt())
mValue = v.asUInt();
else if (value.isDouble())
{
// WRITEME
}
else if (value.isString())
{ // FIXME: If it has a '.' we have to process it specially!
int64 val = lexical_cast_st<int64>(value.asString());
if (val >= 0)
mValue = val;
else
if (mIsNative)
{
mValue = -val;
mIsNegative = true;
int64 val = lexical_cast_st<int64>(value.asString());
if (val >= 0)
mValue = val;
else
{
mValue = -val;
mIsNegative = true;
}
}
else
setValue(value.asString());
}
else
throw std::runtime_error("invalid amount type");
if (!currency.isString() || currency.asString().empty() || (currency.asString() == SYSTEM_CURRENCY_CODE))
{
mIsNative = true;
if (mIsNative)
return;
}
// parse currency and issuer
// WRITEME
@@ -173,6 +179,94 @@ std::string STAmount::createHumanCurrency(const uint160& uCurrency)
return sCurrency;
}
bool STAmount::setValue(const std::string& sAmount)
{ // Note: mIsNative must be set already!
uint64 uValue;
int iOffset;
size_t uDecimal = sAmount.find_first_of(mIsNative ? "^" : ".");
bool bInteger = uDecimal == std::string::npos;
mIsNegative = false;
if (bInteger)
{
try
{
int64 a = sAmount.empty() ? 0 : lexical_cast_st<int64>(sAmount);
if (a >= 0)
uValue = static_cast<uint64>(a);
else
{
uValue = static_cast<uint64>(-a);
mIsNegative = true;
}
}
catch (...)
{
Log(lsINFO) << "Bad integer amount: " << sAmount;
return false;
}
iOffset = 0;
}
else
{
// Example size decimal size-decimal offset
// ^1 2 0 2 -1
// 123^ 4 3 1 0
// 1^23 4 1 3 -2
iOffset = -int(sAmount.size() - uDecimal - 1);
// Issolate integer and fraction.
uint64 uInteger;
int64 iInteger = uDecimal ? lexical_cast_st<uint64>(sAmount.substr(0, uDecimal)) : 0;
if (iInteger >= 0)
uInteger = static_cast<uint64>(iInteger);
else
{
uInteger = static_cast<uint64>(-iInteger);
mIsNegative = true;
}
uint64 uFraction = iOffset ? lexical_cast_st<uint64>(sAmount.substr(uDecimal+1)) : 0;
// Scale the integer portion to the same offset as the fraction.
uValue = uInteger;
for (int i = -iOffset; i--;)
uValue *= 10;
// Add in the fraction.
uValue += uFraction;
}
if (mIsNative)
{
if (bInteger)
iOffset = -SYSTEM_CURRENCY_PRECISION;
while (iOffset > -SYSTEM_CURRENCY_PRECISION) {
uValue *= 10;
--iOffset;
}
while (iOffset < -SYSTEM_CURRENCY_PRECISION) {
uValue /= 10;
++iOffset;
}
mValue = uValue;
}
else
{
mValue = uValue;
mOffset = iOffset;
canonicalize();
}
return true;
}
// Not meant to be the ultimate parser. For use by RPC which is supposed to be sane and trusted.
// Native has special handling:
// - Integer values are in base units.
@@ -216,72 +310,7 @@ bool STAmount::setFullValue(const std::string& sAmount, const std::string& sCurr
return false;
}
uint64 uValue;
int iOffset;
size_t uDecimal = sAmount.find_first_of(mIsNative ? "^" : ".");
bool bInteger = uDecimal == std::string::npos;
if (bInteger)
{
try
{
uValue = sAmount.empty() ? 0 : boost::lexical_cast<uint64>(sAmount);
}
catch (...)
{
Log(lsINFO) << "Bad integer amount: " << sAmount;
return false;
}
iOffset = 0;
}
else
{
// Example size decimal size-decimal offset
// ^1 2 0 2 -1
// 123^ 4 3 1 0
// 1^23 4 1 3 -2
iOffset = -int(sAmount.size()-uDecimal-1);
// Issolate integer and fraction.
uint64 uInteger = uDecimal ? boost::lexical_cast<uint64>(sAmount.substr(0, uDecimal)) : 0;
uint64 uFraction = iOffset ? boost::lexical_cast<uint64>(sAmount.substr(uDecimal+1)) : 0;
// Scale the integer portion to the same offset as the fraction.
uValue = uInteger;
for (int i=-iOffset; i--;)
uValue *= 10;
// Add in the fraction.
uValue += uFraction;
}
if (mIsNative)
{
if (bInteger)
iOffset = -SYSTEM_CURRENCY_PRECISION;
while (iOffset > -SYSTEM_CURRENCY_PRECISION) {
uValue *= 10;
--iOffset;
}
while (iOffset < -SYSTEM_CURRENCY_PRECISION) {
uValue /= 10;
++iOffset;
}
mValue = uValue;
}
else
{
mValue = uValue;
mOffset = iOffset;
canonicalize();
}
return true;
return setValue(sAmount);
}
// amount = value * [10 ^ offset]

View File

@@ -220,8 +220,6 @@ protected:
: SerializedType(name), mCurrency(cur), mIssuer(iss), mValue(val), mOffset(off),
mIsNative(isNat), mIsNegative(isNeg) { ; }
STAmount(SField::ref name, const Json::Value& value);
uint64 toUInt64() const;
static uint64 muldiv(uint64, uint64, uint64);
@@ -245,6 +243,8 @@ public:
SerializedType(n), mCurrency(currency), mIssuer(issuer), mValue(v), mOffset(off), mIsNegative(isNeg)
{ canonicalize(); }
STAmount(SField::ref, const Json::Value&);
static STAmount createFromInt64(SField::ref n, int64 v);
static std::auto_ptr<SerializedType> deserialize(SerializerIterator& sit, SField::ref name)
@@ -288,6 +288,7 @@ public:
void setIssuer(const uint160& uIssuer) { mIssuer = uIssuer; }
const uint160& getCurrency() const { return mCurrency; }
bool setValue(const std::string& sAmount);
bool setFullValue(const std::string& sAmount, const std::string& sCurrency = "", const std::string& sIssuer = "");
void setValue(const STAmount &);