#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _DEBUG // #define DEBUG_OUTPUT 1 #endif namespace xrpl { namespace wasm_float { namespace detail { // Decode a serialized STNumber float payload. Returns nullopt if the data is // not a well-formed encoding. std::optional floatDecode(Slice const& data) { static unsigned constexpr encodedFloatSize = 12; if (data.size() != encodedFloatSize) return std::nullopt; try { SerialIter it(data); return STNumber(it, sfNumber).value(); } catch (...) { return std::nullopt; } } // 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 { return Number(mantissa, exponent); } catch (...) { return std::nullopt; } } // Serialize a Number to the STNumber float encoding. std::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) << 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 std::expected(std::move(data)); } struct FloatState { // Set only when the requested mode is valid; sets the rounding mode on // construction and restores the previous mode on destruction. std::optional guard; explicit FloatState(int32_t mode) { if (auto const rm = Number::checkedRoundingMode(mode)) guard.emplace(*rm); } explicit operator bool() const { return guard.has_value(); } }; } // namespace detail std::string 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)); auto const num = detail::floatDecode(data); if (!num) { std::string hex; hex.reserve(data.size() * 2); boost::algorithm::hex(data.begin(), data.end(), std::back_inserter(hex)); return "Invalid data: " + hex; } return to_string(*num); } std::expected floatFromIntImpl(int64_t x, int32_t mode) { try { detail::FloatState const rm(mode); if (!rm) return std::unexpected(HostFunctionError::FloatInputMalformed); return detail::floatEncode(Number(x)); } // LCOV_EXCL_START catch (...) { return std::unexpected(HostFunctionError::FloatComputationError); } // LCOV_EXCL_STOP } std::expected floatFromUintImpl(uint64_t x, int32_t mode) { try { detail::FloatState const rm(mode); if (!rm) return std::unexpected(HostFunctionError::FloatInputMalformed); return detail::floatEncode(Number(x, 0, Number::Normalized{})); } // LCOV_EXCL_START catch (...) { return std::unexpected(HostFunctionError::FloatComputationError); } // LCOV_EXCL_STOP } std::expected floatFromSTAmountImpl(STAmount const& x, int32_t mode) { try { detail::FloatState const rm(mode); if (!rm) return std::unexpected(HostFunctionError::FloatInputMalformed); return detail::floatEncode(static_cast(x)); } // LCOV_EXCL_START catch (...) { return std::unexpected(HostFunctionError::FloatComputationError); } // LCOV_EXCL_STOP } std::expected floatFromSTNumberImpl(STNumber const& x, int32_t mode) { try { detail::FloatState const rm(mode); if (!rm) return std::unexpected(HostFunctionError::FloatInputMalformed); return detail::floatEncode(x.value()); } // LCOV_EXCL_START catch (...) { return std::unexpected(HostFunctionError::FloatComputationError); } // LCOV_EXCL_STOP } std::expected floatToIntImpl(Slice const& x, int32_t mode) { try { detail::FloatState const rm(mode); if (!rm) return std::unexpected(HostFunctionError::FloatInputMalformed); auto const num = detail::floatDecode(x); if (!num) return std::unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE return static_cast(*num); } // LCOV_EXCL_START catch (...) { return std::unexpected(HostFunctionError::FloatComputationError); } // LCOV_EXCL_STOP } std::expected floatToMantExpImpl(Slice const& x) { try { detail::FloatState const rm(static_cast(Number::RoundingMode::ToNearest)); if (!rm) return std::unexpected(HostFunctionError::FloatInputMalformed); auto const num = detail::floatDecode(x); if (!num) return std::unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE return FloatPair(num->mantissa(), num->exponent()); } // LCOV_EXCL_START catch (...) { return std::unexpected(HostFunctionError::FloatComputationError); } // LCOV_EXCL_STOP } std::expected floatFromMantExpImpl(int64_t mantissa, int32_t exponent, int32_t mode) { try { detail::FloatState const rm(mode); if (!rm) return std::unexpected(HostFunctionError::FloatInputMalformed); auto const num = detail::numberFromMantExp(mantissa, exponent); if (!num) return std::unexpected(HostFunctionError::FloatInputMalformed); return detail::floatEncode(*num); } catch (...) { return std::unexpected(HostFunctionError::FloatComputationError); } } std::expected floatCompareImpl(Slice const& x, Slice const& y) { try { // set default mode as we don't expect it will be used here detail::FloatState const rm(static_cast(Number::RoundingMode::ToNearest)); auto const xx = detail::floatDecode(x); if (!xx) return std::unexpected(HostFunctionError::FloatInputMalformed); auto const yy = detail::floatDecode(y); if (!yy) return std::unexpected(HostFunctionError::FloatInputMalformed); if (*xx < *yy) return 2; if (*xx == *yy) return 0; return 1; } // LCOV_EXCL_START catch (...) { return std::unexpected(HostFunctionError::FloatComputationError); } // LCOV_EXCL_STOP } std::expected floatAddImpl(Slice const& x, Slice const& y, int32_t mode) { try { detail::FloatState const rm(mode); if (!rm) return std::unexpected(HostFunctionError::FloatInputMalformed); auto const xx = detail::floatDecode(x); if (!xx) return std::unexpected(HostFunctionError::FloatInputMalformed); auto const yy = detail::floatDecode(y); if (!yy) return std::unexpected(HostFunctionError::FloatInputMalformed); return detail::floatEncode(*xx + *yy); } // LCOV_EXCL_START catch (...) { return std::unexpected(HostFunctionError::FloatComputationError); } // LCOV_EXCL_STOP } std::expected floatSubtractImpl(Slice const& x, Slice const& y, int32_t mode) { try { detail::FloatState const rm(mode); if (!rm) return std::unexpected(HostFunctionError::FloatInputMalformed); auto const xx = detail::floatDecode(x); if (!xx) return std::unexpected(HostFunctionError::FloatInputMalformed); auto const yy = detail::floatDecode(y); if (!yy) return std::unexpected(HostFunctionError::FloatInputMalformed); return detail::floatEncode(*xx - *yy); } // LCOV_EXCL_START catch (...) { return std::unexpected(HostFunctionError::FloatComputationError); } // LCOV_EXCL_STOP } std::expected floatMultiplyImpl(Slice const& x, Slice const& y, int32_t mode) { try { detail::FloatState const rm(mode); if (!rm) return std::unexpected(HostFunctionError::FloatInputMalformed); auto const xx = detail::floatDecode(x); if (!xx) return std::unexpected(HostFunctionError::FloatInputMalformed); auto const yy = detail::floatDecode(y); if (!yy) return std::unexpected(HostFunctionError::FloatInputMalformed); return detail::floatEncode(*xx * *yy); } // LCOV_EXCL_START catch (...) { return std::unexpected(HostFunctionError::FloatComputationError); } // LCOV_EXCL_STOP } std::expected floatDivideImpl(Slice const& x, Slice const& y, int32_t mode) { try { detail::FloatState const rm(mode); if (!rm) return std::unexpected(HostFunctionError::FloatInputMalformed); auto const xx = detail::floatDecode(x); if (!xx) return std::unexpected(HostFunctionError::FloatInputMalformed); auto const yy = detail::floatDecode(y); if (!yy) return std::unexpected(HostFunctionError::FloatInputMalformed); return detail::floatEncode(*xx / *yy); } catch (...) { return std::unexpected(HostFunctionError::FloatComputationError); } } std::expected floatRootImpl(Slice const& x, int32_t n, int32_t mode) { try { if (n < 1) return std::unexpected(HostFunctionError::FloatInputMalformed); detail::FloatState const rm(mode); if (!rm) return std::unexpected(HostFunctionError::FloatInputMalformed); auto const xx = detail::floatDecode(x); if (!xx) return std::unexpected(HostFunctionError::FloatInputMalformed); return detail::floatEncode(root(*xx, n)); } // LCOV_EXCL_START catch (...) { return std::unexpected(HostFunctionError::FloatComputationError); } // LCOV_EXCL_STOP } std::expected floatPowerImpl(Slice const& x, int32_t n, int32_t mode) { try { if ((n < 0) || (n > Number::kMaxExponent)) return std::unexpected(HostFunctionError::FloatInputMalformed); detail::FloatState const rm(mode); if (!rm) return std::unexpected(HostFunctionError::FloatInputMalformed); auto const xx = detail::floatDecode(x); if (!xx) return std::unexpected(HostFunctionError::FloatInputMalformed); if (*xx == Number() && (n == 0)) return std::unexpected(HostFunctionError::InvalidParams); return detail::floatEncode(power(*xx, n, 1)); } // LCOV_EXCL_START catch (...) { return std::unexpected(HostFunctionError::FloatComputationError); } // LCOV_EXCL_STOP } } // namespace wasm_float // ========================================================= // ACTUAL HOST FUNCTIONS // ========================================================= std::expected WasmHostFunctionsImpl::floatFromInt(int64_t x, int32_t mode) const { return wasm_float::floatFromIntImpl(x, mode); } std::expected WasmHostFunctionsImpl::floatFromUint(uint64_t x, int32_t mode) const { return wasm_float::floatFromUintImpl(x, mode); } std::expected WasmHostFunctionsImpl::floatFromSTAmount(STAmount const& x, int32_t mode) const { return wasm_float::floatFromSTAmountImpl(x, mode); } std::expected WasmHostFunctionsImpl::floatFromSTNumber(STNumber const& x, int32_t mode) const { return wasm_float::floatFromSTNumberImpl(x, mode); } std::expected WasmHostFunctionsImpl::floatToInt(Slice const& x, int32_t mode) const { return wasm_float::floatToIntImpl(x, mode); } std::expected WasmHostFunctionsImpl::floatToMantExp(Slice const& x) const { return wasm_float::floatToMantExpImpl(x); } std::expected WasmHostFunctionsImpl::floatFromMantExp(int64_t mantissa, int32_t exponent, int32_t mode) const { return wasm_float::floatFromMantExpImpl(mantissa, exponent, mode); } std::expected WasmHostFunctionsImpl::floatCompare(Slice const& x, Slice const& y) const { return wasm_float::floatCompareImpl(x, y); } std::expected WasmHostFunctionsImpl::floatAdd(Slice const& x, Slice const& y, int32_t mode) const { return wasm_float::floatAddImpl(x, y, mode); } std::expected WasmHostFunctionsImpl::floatSubtract(Slice const& x, Slice const& y, int32_t mode) const { return wasm_float::floatSubtractImpl(x, y, mode); } std::expected WasmHostFunctionsImpl::floatMultiply(Slice const& x, Slice const& y, int32_t mode) const { return wasm_float::floatMultiplyImpl(x, y, mode); } std::expected WasmHostFunctionsImpl::floatDivide(Slice const& x, Slice const& y, int32_t mode) const { return wasm_float::floatDivideImpl(x, y, mode); } std::expected WasmHostFunctionsImpl::floatRoot(Slice const& x, int32_t n, int32_t mode) const { return wasm_float::floatRootImpl(x, n, mode); } std::expected WasmHostFunctionsImpl::floatPower(Slice const& x, int32_t n, int32_t mode) const { return wasm_float::floatPowerImpl(x, n, mode); } } // namespace xrpl