From ed13af878e89a718a409765bb7fa904d8220f613 Mon Sep 17 00:00:00 2001 From: Valentin Balaschenko <13349202+vlntb@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:54:26 +0000 Subject: [PATCH] refactor --- include/xrpl/protocol/STAmount.h | 31 +++++----------------------- src/libxrpl/protocol/STAmount.cpp | 34 ++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 38 deletions(-) diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index fa9bd97b18..b5683e179d 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -527,32 +527,11 @@ STAmount::fromNumber(A const& a, Number const& number) auto const [mantissa, exponent] = working.normalizeToRange(cMinValue, cMaxValue); - // Special case: normalizeToRange returns mantissa=0 with Number's default - // exponent (std::numeric_limits::lowest()), but STAmount expects zero - // IOUs to have the canonical zero offset. Handle this explicitly. - if (mantissa == 0) - { - return STAmount{asset, 0, cZeroOffset, false, unchecked{}}; - } - - // Handle underflow: if exponent is below minimum or mantissa is too small, - // the value underflows to zero. - if ((exponent < cMinOffset) || (mantissa < cMinValue)) - { - return STAmount{asset, 0, cZeroOffset, false, unchecked{}}; - } - - // Handle overflow: if exponent exceeds maximum, throw. - if (exponent > cMaxOffset) - Throw("value overflow"); - - // normalizeToRange already produced canonical mantissa/exponent in the range - // [cMinValue, cMaxValue], so bypass canonicalize() to avoid redundant work. - XRPL_ASSERT( - mantissa >= cMinValue && mantissa <= cMaxValue, "xrpl::STAmount::fromNumber : mantissa in canonical range"); - XRPL_ASSERT( - exponent >= cMinOffset && exponent <= cMaxOffset, "xrpl::STAmount::fromNumber : exponent in canonical range"); - return STAmount{asset, static_cast(mantissa), exponent, negative, unchecked{}}; + // normalizeToRange produces values in canonical mantissa range [cMinValue, cMaxValue], + // but may produce out-of-range exponents for overflow/underflow cases. + // Use the regular constructor - canonicalize() will detect already-normalized mantissa + // and skip redundant scaling loops, while still handling overflow/underflow. + return STAmount{asset, static_cast(mantissa), exponent, negative}; } inline void diff --git a/src/libxrpl/protocol/STAmount.cpp b/src/libxrpl/protocol/STAmount.cpp index 682f73fcf3..22de7f6630 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -871,21 +871,30 @@ STAmount::canonicalize() return; } - while ((mValue < cMinValue) && (mOffset > cMinOffset)) + // Fast path: if mantissa is already in canonical range, skip scaling loops. + // This handles values from normalizeToRange that only need overflow/underflow checks. + bool const mantissaCanonical = (mValue >= cMinValue) && (mValue <= cMaxValue); + + if (!mantissaCanonical) { - mValue *= 10; - --mOffset; - } - - while (mValue > cMaxValue) - { - if (mOffset >= cMaxOffset) - Throw("value overflow"); - - mValue /= 10; - ++mOffset; + // Mantissa needs normalization + while ((mValue < cMinValue) && (mOffset > cMinOffset)) + { + mValue *= 10; + --mOffset; + } + + while (mValue > cMaxValue) + { + if (mOffset >= cMaxOffset) + Throw("value overflow"); + + mValue /= 10; + ++mOffset; + } } + // Check for underflow (applies whether we scaled or not) if ((mOffset < cMinOffset) || (mValue < cMinValue)) { mValue = 0; @@ -894,6 +903,7 @@ STAmount::canonicalize() return; } + // Check for overflow (applies whether we scaled or not) if (mOffset > cMaxOffset) Throw("value overflow");