From ddab25ab44eb6784eca0a6e41ca2c489655c7521 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Tue, 2 Oct 2012 04:28:01 -0700 Subject: [PATCH] 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. --- src/Amount.cpp | 193 ++++++++++++++++++++++++------------------ src/SerializedTypes.h | 5 +- 2 files changed, 114 insertions(+), 84 deletions(-) diff --git a/src/Amount.cpp b/src/Amount.cpp index 9ceaa11975..ef8f417c7a 100644 --- a/src/Amount.cpp +++ b/src/Amount.cpp @@ -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(value.asString()); - if (val >= 0) - mValue = val; - else + if (mIsNative) { - mValue = -val; - mIsNegative = true; + int64 val = lexical_cast_st(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(sAmount); + if (a >= 0) + uValue = static_cast(a); + else + { + uValue = static_cast(-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(sAmount.substr(0, uDecimal)) : 0; + if (iInteger >= 0) + uInteger = static_cast(iInteger); + else + { + uInteger = static_cast(-iInteger); + mIsNegative = true; + } + + + uint64 uFraction = iOffset ? lexical_cast_st(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(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(sAmount.substr(0, uDecimal)) : 0; - uint64 uFraction = iOffset ? boost::lexical_cast(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] diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 785e62515d..7bafa039d5 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -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 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 &);