diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index e67f1f534d..20900f044f 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -392,6 +392,20 @@ public: static RoundingMode setround(RoundingMode inMode); + /** Convert an integer to a RoundingMode, validating that it is in range. + * + * Returns std::nullopt if the value does not correspond to a valid + * RoundingMode. + */ + static std::optional + checkedRoundingMode(int mode) noexcept + { + if (mode < static_cast(RoundingMode::ToNearest) || + mode > static_cast(RoundingMode::Upward)) + return std::nullopt; + return static_cast(mode); + } + /** Returns which mantissa scale is currently in use for normalization. * * If you think you need to call this outside of unit tests, no you don't. diff --git a/src/libxrpl/tx/wasm/HostFuncImplFloat.cpp b/src/libxrpl/tx/wasm/HostFuncImplFloat.cpp index dda1a09296..c3a6e534a9 100644 --- a/src/libxrpl/tx/wasm/HostFuncImplFloat.cpp +++ b/src/libxrpl/tx/wasm/HostFuncImplFloat.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -25,114 +26,75 @@ namespace wasm_float { namespace detail { -class WasmNumber : public Number +// Decode a serialized STNumber float payload. Returns nullopt if the data is +// not a well-formed encoding. +std::optional +floatDecode(Slice const& data) { -protected: static unsigned constexpr encodedFloatSize = 12; - bool good_ = false; - -public: - WasmNumber(Slice const& data) + if (data.size() != encodedFloatSize) + return std::nullopt; + try { - if (data.size() != encodedFloatSize) - return; - try - { - SerialIter it(data); - Number const x = STNumber(it, sfNumber).value(); - *static_cast(this) = x; - } - catch (...) - { - return; - } - - good_ = true; + SerialIter it(data); + return STNumber(it, sfNumber).value(); } - - WasmNumber(Number const& n) : WasmNumber(n.mantissa(), n.exponent()) // ensure Number canonized + catch (...) { + return std::nullopt; } +} - template - WasmNumber(T mantissa = 0, int32_t exponent = 0) +// Build a Number from a raw mantissa/exponent pair. Returns nullopt if the +// value cannot be represented, e.g. the exponent is out of range. +std::optional +numberFromMantExp(int64_t mantissa, int32_t exponent) +{ + try { - try - { - Number n; - if constexpr (std::is_signed_v) - { - n = Number(static_cast(mantissa), exponent); - } - else - { - n = Number(static_cast(mantissa), exponent, Number::Normalized{}); - } - *static_cast(this) = n; - } - catch (...) - { - return; - } - good_ = true; + return Number(mantissa, exponent); } - - WasmNumber& - operator=(WasmNumber const&) = default; - - explicit - operator bool() const + catch (...) { - return good_; + return std::nullopt; } +} - explicit - operator int64_t() const - { - return Number::operator int64_t(); - } - - Expected - toBytes() const - { - Serializer msg; - STNumber(sfNumber, *this).add(msg); - auto data = msg.getData(); +// Serialize a Number to the STNumber float encoding. +Expected +floatEncode(Number const& n) +{ + Serializer msg; + STNumber(sfNumber, n).add(msg); + auto data = msg.getData(); #ifdef DEBUG_OUTPUT - std::cout << "m: " << std::setw(20) << mantissa() << ", e: " << std::setw(12) << exponent() - << ", hex: "; - std::cout << std::hex << std::uppercase << std::setfill('0'); - for (auto const& c : data) - std::cout << std::setw(2) << (unsigned)c << " "; - std::cout << std::dec << std::setfill(' ') << std::endl; + std::cout << "m: " << std::setw(20) << n.mantissa() << ", e: " << std::setw(12) << n.exponent() + << ", hex: "; + std::cout << std::hex << std::uppercase << std::setfill('0'); + for (auto const& c : data) + std::cout << std::setw(2) << (unsigned)c << " "; + std::cout << std::dec << std::setfill(' ') << std::endl; #endif - return Expected(std::move(data)); - } -}; + return Expected(std::move(data)); +} struct FloatState { - Number::RoundingMode oldMode; - bool good = false; + // Set only when the requested mode is valid; sets the rounding mode on + // construction and restores the previous mode on destruction. + std::optional guard; - FloatState(int32_t mode) : oldMode(Number::getround()) + explicit FloatState(int32_t mode) { - if (mode < static_cast(Number::RoundingMode::ToNearest) || - mode > static_cast(Number::RoundingMode::Upward)) - return; - Number::setround(static_cast(mode)); - good = true; - } - - ~FloatState() - { - Number::setround(oldMode); + if (auto const rm = Number::checkedRoundingMode(mode)) + guard.emplace(*rm); } + explicit operator bool() const { - return good; + return guard.has_value(); } }; @@ -143,7 +105,7 @@ floatToString(Slice const& data) { // set default mode as we don't expect it will be used here detail::FloatState const rm(static_cast(Number::RoundingMode::ToNearest)); - detail::WasmNumber const num(data); + auto const num = detail::floatDecode(data); if (!num) { std::string hex; @@ -151,8 +113,7 @@ floatToString(Slice const& data) boost::algorithm::hex(data.begin(), data.end(), std::back_inserter(hex)); return "Invalid data: " + hex; } - auto const s = to_string(num); - return s; + return to_string(*num); } Expected @@ -164,11 +125,7 @@ floatFromIntImpl(int64_t x, int32_t mode) if (!rm) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const num(x); - if (!num) - return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE - auto const r = num.toBytes(); - return r; + return detail::floatEncode(Number(x)); } // LCOV_EXCL_START catch (...) @@ -187,11 +144,7 @@ floatFromUintImpl(uint64_t x, int32_t mode) if (!rm) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const num(x); - if (!num) - return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE - auto const r = num.toBytes(); - return r; + return detail::floatEncode(Number(x, 0, Number::Normalized{})); } // LCOV_EXCL_START catch (...) @@ -210,11 +163,7 @@ floatFromSTAmountImpl(STAmount const& x, int32_t mode) if (!rm) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const num(static_cast(x)); - if (!num) - return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE - auto const r = num.toBytes(); - return r; + return detail::floatEncode(static_cast(x)); } // LCOV_EXCL_START catch (...) @@ -233,11 +182,7 @@ floatFromSTNumberImpl(STNumber const& x, int32_t mode) if (!rm) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const num(x.value()); - if (!num) - return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE - auto const r = num.toBytes(); - return r; + return detail::floatEncode(x.value()); } // LCOV_EXCL_START catch (...) @@ -256,11 +201,10 @@ floatToIntImpl(Slice const& x, int32_t mode) if (!rm) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const num(x); + auto const num = detail::floatDecode(x); if (!num) return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE - int64_t const r(num); - return r; + return static_cast(*num); } // LCOV_EXCL_START catch (...) @@ -279,11 +223,11 @@ floatToMantExpImpl(Slice const& x) if (!rm) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const num(x); + auto const num = detail::floatDecode(x); if (!num) return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE - return FloatPair(num.mantissa(), num.exponent()); + return FloatPair(num->mantissa(), num->exponent()); } // LCOV_EXCL_START catch (...) @@ -301,10 +245,10 @@ floatFromMantExpImpl(int64_t mantissa, int32_t exponent, int32_t mode) detail::FloatState const rm(mode); if (!rm) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const num(mantissa, exponent); + auto const num = detail::numberFromMantExp(mantissa, exponent); if (!num) return Unexpected(HostFunctionError::FloatInputMalformed); - return num.toBytes(); + return detail::floatEncode(*num); } catch (...) { @@ -320,15 +264,15 @@ floatCompareImpl(Slice const& x, Slice const& y) // set default mode as we don't expect it will be used here detail::FloatState const rm(static_cast(Number::RoundingMode::ToNearest)); - detail::WasmNumber const xx(x); + auto const xx = detail::floatDecode(x); if (!xx) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const yy(y); + auto const yy = detail::floatDecode(y); if (!yy) return Unexpected(HostFunctionError::FloatInputMalformed); - if (xx < yy) + if (*xx < *yy) return 2; - if (xx == yy) + if (*xx == *yy) return 0; return 1; } @@ -349,15 +293,14 @@ floatAddImpl(Slice const& x, Slice const& y, int32_t mode) if (!rm) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const xx(x); + auto const xx = detail::floatDecode(x); if (!xx) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const yy(y); + auto const yy = detail::floatDecode(y); if (!yy) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const res = xx + yy; - return res.toBytes(); + return detail::floatEncode(*xx + *yy); } // LCOV_EXCL_START catch (...) @@ -375,15 +318,14 @@ floatSubtractImpl(Slice const& x, Slice const& y, int32_t mode) detail::FloatState const rm(mode); if (!rm) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const xx(x); + auto const xx = detail::floatDecode(x); if (!xx) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const yy(y); + auto const yy = detail::floatDecode(y); if (!yy) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const res = xx - yy; - return res.toBytes(); + return detail::floatEncode(*xx - *yy); } // LCOV_EXCL_START catch (...) @@ -401,15 +343,14 @@ floatMultiplyImpl(Slice const& x, Slice const& y, int32_t mode) detail::FloatState const rm(mode); if (!rm) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const xx(x); + auto const xx = detail::floatDecode(x); if (!xx) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const yy(y); + auto const yy = detail::floatDecode(y); if (!yy) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const res = xx * yy; - return res.toBytes(); + return detail::floatEncode(*xx * *yy); } // LCOV_EXCL_START catch (...) @@ -427,15 +368,14 @@ floatDivideImpl(Slice const& x, Slice const& y, int32_t mode) detail::FloatState const rm(mode); if (!rm) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const xx(x); + auto const xx = detail::floatDecode(x); if (!xx) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const yy(y); + auto const yy = detail::floatDecode(y); if (!yy) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const res = xx / yy; - return res.toBytes(); + return detail::floatEncode(*xx / *yy); } catch (...) { @@ -455,13 +395,11 @@ floatRootImpl(Slice const& x, int32_t n, int32_t mode) if (!rm) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const xx(x); + auto const xx = detail::floatDecode(x); if (!xx) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const res(root(xx, n)); - - return res.toBytes(); + return detail::floatEncode(root(*xx, n)); } // LCOV_EXCL_START catch (...) @@ -483,15 +421,13 @@ floatPowerImpl(Slice const& x, int32_t n, int32_t mode) if (!rm) return Unexpected(HostFunctionError::FloatInputMalformed); - detail::WasmNumber const xx(x); + auto const xx = detail::floatDecode(x); if (!xx) return Unexpected(HostFunctionError::FloatInputMalformed); - if (xx == Number() && (n == 0)) + if (*xx == Number() && (n == 0)) return Unexpected(HostFunctionError::InvalidParams); - detail::WasmNumber const res(power(xx, n, 1)); - - return res.toBytes(); + return detail::floatEncode(power(*xx, n, 1)); } // LCOV_EXCL_START catch (...)