#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include namespace xrpl { namespace unit { /** * "drops" are the smallest divisible amount of XRP. This is what most * of the code uses. */ struct dropTag; /** * "fee levels" are used by the transaction queue to compare the relative * cost of transactions that require different levels of effort to process. * See also: src/xrpld/app/misc/FeeEscalation.md#fee-level */ struct feelevelTag; /** * unitless values are plain scalars wrapped in a ValueUnit. They are * used for calculations in this header. */ struct unitlessTag; /** * Units to represent basis points (bips) and 1/10 basis points */ class BipsTag; class TenthBipsTag; // These names don't have to be too descriptive, because we're in the "unit" // namespace. template concept Valid = std::is_class_v && std::is_object_v && std::is_object_v; /** * `Usable` is checked to ensure that only values with * known valid type tags can be used (sometimes transparently) in * non-unit contexts. At the time of implementation, this includes * all known tags, but more may be added in the future, and they * should not be added automatically unless determined to be * appropriate. */ template concept Usable = Valid && (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v); template concept Compatible = Valid && std::is_arithmetic_v && std::is_arithmetic_v && std::is_convertible_v; template concept Integral = std::is_integral_v; template concept IntegralValue = Integral; template concept CastableValue = IntegralValue && IntegralValue && std::is_same_v; template class ValueUnit : private boost::totally_ordered>, private boost::additive>, private boost::equality_comparable, T>, private boost::dividable, T>, private boost::modable, T>, private boost::unit_steppable> { public: using unit_type = UnitTag; using value_type = T; private: value_type value_; public: ValueUnit() = default; constexpr ValueUnit(ValueUnit const& other) = default; constexpr ValueUnit& operator=(ValueUnit const& other) = default; constexpr explicit ValueUnit(beast::Zero) : value_(0) { } constexpr ValueUnit& operator=(beast::Zero) { value_ = 0; return *this; } constexpr explicit ValueUnit(value_type value) : value_(value) { } constexpr ValueUnit& operator=(value_type value) { value_ = value; return *this; } /** * Instances with the same unit, and a type that is * "safe" to convert to this one can be converted * implicitly */ template Other> constexpr ValueUnit(ValueUnit const& value) requires SafeToCast : ValueUnit(safeCast(value.value())) { } constexpr ValueUnit operator+(value_type const& rhs) const { return ValueUnit{value_ + rhs}; } friend constexpr ValueUnit operator+(value_type lhs, ValueUnit const& rhs) { // addition is commutative return rhs + lhs; } constexpr ValueUnit operator-(value_type const& rhs) const { return ValueUnit{value_ - rhs}; } friend constexpr ValueUnit operator-(value_type lhs, ValueUnit const& rhs) { // subtraction is NOT commutative, but (lhs + (-rhs)) is addition, which // is return -rhs + lhs; } constexpr ValueUnit operator*(value_type const& rhs) const { return ValueUnit{value_ * rhs}; } friend constexpr ValueUnit operator*(value_type lhs, ValueUnit const& rhs) { // multiplication is commutative return rhs * lhs; } constexpr value_type operator/(ValueUnit const& rhs) const { return value_ / rhs.value_; } ValueUnit& operator+=(ValueUnit const& other) { value_ += other.value(); return *this; } ValueUnit& operator-=(ValueUnit const& other) { value_ -= other.value(); return *this; } ValueUnit& operator++() { ++value_; return *this; } ValueUnit& operator--() { --value_; return *this; } ValueUnit& operator*=(value_type const& rhs) { value_ *= rhs; return *this; } ValueUnit& operator/=(value_type const& rhs) { value_ /= rhs; return *this; } template ValueUnit& operator%=(value_type const& rhs) { value_ %= rhs; return *this; } ValueUnit operator-() const { static_assert(std::is_signed_v, "- operator illegal on unsigned value types"); return ValueUnit{-value_}; } constexpr bool operator==(ValueUnit const& other) const { return value_ == other.value_; } template Other> constexpr bool operator==(ValueUnit const& other) const { return value_ == other.value(); } constexpr bool operator==(value_type other) const { return value_ == other; } template Other> constexpr bool operator!=(ValueUnit const& other) const { return !operator==(other); } constexpr bool operator<(ValueUnit const& other) const { return value_ < other.value_; } /** * Returns true if the amount is not zero */ explicit constexpr operator bool() const noexcept { return value_ != 0; } /** * Return the sign of the amount */ [[nodiscard]] constexpr int signum() const noexcept { if (value_ < 0) return -1; return value_ ? 1 : 0; } /** * Returns the number of drops */ // TODO: Move this to a new class, maybe with the old "TaggedFee" name [[nodiscard]] constexpr value_type fee() const { return value_; } template [[nodiscard]] constexpr double decimalFromReference(ValueUnit reference) const { return static_cast(value_) / reference.value(); } // `Usable` is checked to ensure that only values with // known valid type tags can be converted to JSON. At the time // of implementation, that includes all known tags, but more may // be added in the future. [[nodiscard]] json::Value jsonClipped() const requires Usable { if constexpr (std::is_integral_v) { using jsontype = std::conditional_t, json::Int, json::UInt>; constexpr auto kMin = std::numeric_limits::min(); constexpr auto kMax = std::numeric_limits::max(); if (value_ < kMin) return kMin; if (value_ > kMax) return kMax; return static_cast(value_); } else { return value_; } } /** * Returns the underlying value. Code SHOULD NOT call this * function unless the type has been abstracted away, * e.g. in a templated function. */ [[nodiscard]] constexpr value_type value() const { return value_; } friend std::istream& operator>>(std::istream& s, ValueUnit& val) { s >> val.value_; return s; } }; // Output Values as just their numeric value. template std::basic_ostream& operator<<(std::basic_ostream& os, ValueUnit const& q) { return os << q.value(); } template std::string to_string(ValueUnit const& amount) { return std::to_string(amount.value()); } template concept muldivSource = Valid && std::is_convertible_v; template concept muldivDest = muldivSource && // Dest is also a source std::is_convertible_v && sizeof(typename Dest::value_type) >= sizeof(std::uint64_t); template concept muldivSources = muldivSource && muldivSource && std::is_same_v; template concept muldivable = muldivSources && muldivDest; // Source and Dest can be the same by default template concept muldivCommutable = muldivable && !std::is_same_v; template ValueUnit scalar(T value) { return ValueUnit{value}; } template Dest> std::optional mulDivU(Source1 value, Dest mul, Source2 div) { // values can never be negative in any context. if (value.value() < 0 || mul.value() < 0 || div.value() < 0) { // split the asserts so if one hits, the user can tell which // without a debugger. XRPL_ASSERT(value.value() >= 0, "xrpl::unit::mulDivU : minimum value input"); XRPL_ASSERT(mul.value() >= 0, "xrpl::unit::mulDivU : minimum mul input"); XRPL_ASSERT(div.value() > 0, "xrpl::unit::mulDivU : minimum div input"); return std::nullopt; } using desttype = Dest::value_type; constexpr auto kMax = std::numeric_limits::max(); // Shortcuts, since these happen a lot in the real world if (value == div) return mul; if (mul.value() == div.value()) { if (value.value() > kMax) return std::nullopt; return Dest{static_cast(value.value())}; } using namespace boost::multiprecision; uint128_t product; product = multiply( product, static_cast(value.value()), static_cast(mul.value())); auto quotient = product / div.value(); if (quotient > kMax) return std::nullopt; return Dest{static_cast(quotient)}; } } // namespace unit // Fee Levels template using FeeLevel = unit::ValueUnit; using FeeLevel64 = FeeLevel; using FeeLevelDouble = FeeLevel; // Basis points (Bips) template using Bips = unit::ValueUnit; using Bips16 = Bips; using Bips32 = Bips; template using TenthBips = unit::ValueUnit; using TenthBips16 = TenthBips; using TenthBips32 = TenthBips; template Dest> std::optional mulDiv(Source1 value, Dest mul, Source2 div) { return unit::mulDivU(value, mul, div); } template Dest> std::optional mulDiv(Dest value, Source1 mul, Source2 div) { // Multiplication is commutative return unit::mulDivU(mul, value, div); } template std::optional mulDiv(std::uint64_t value, Dest mul, std::uint64_t div) { // Give the scalars a non-tag so the // unit-handling version gets called. return unit::mulDivU(unit::scalar(value), mul, unit::scalar(div)); } template std::optional mulDiv(Dest value, std::uint64_t mul, std::uint64_t div) { // Multiplication is commutative return mulDiv(mul, value, div); } template Source2> std::optional mulDiv(Source1 value, std::uint64_t mul, Source2 div) { // Give the scalars a dimensionless unit so the // unit-handling version gets called. auto unitresult = unit::mulDivU(value, unit::scalar(mul), div); if (!unitresult) return std::nullopt; return unitresult->value(); } template Source2> std::optional mulDiv(std::uint64_t value, Source1 mul, Source2 div) { // Multiplication is commutative return mulDiv(mul, value, div); } template Src> constexpr Dest safeCast(Src s) noexcept { // Dest may not have an explicit value constructor return Dest{safeCast(s.value())}; } template constexpr Dest safeCast(Src s) noexcept { // Dest may not have an explicit value constructor return Dest{safeCast(s)}; } template Src> constexpr Dest unsafeCast(Src s) noexcept { // Dest may not have an explicit value constructor return Dest{unsafeCast(s.value())}; } template constexpr Dest unsafeCast(Src s) noexcept { // Dest may not have an explicit value constructor return Dest{unsafeCast(s)}; } } // namespace xrpl