LCOV - code coverage report
Current view: top level - src/util - feefrac.h (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 86.0 % 50 43
Test Date: 2025-04-16 05:11:06 Functions: 100.0 % 4 4
Branches: 48.5 % 270 131

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) The Bitcoin Core developers
       2                 :             : // Distributed under the MIT software license, see the accompanying
       3                 :             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4                 :             : 
       5                 :             : #ifndef BITCOIN_UTIL_FEEFRAC_H
       6                 :             : #define BITCOIN_UTIL_FEEFRAC_H
       7                 :             : 
       8                 :             : #include <stdint.h>
       9                 :             : #include <compare>
      10                 :             : #include <vector>
      11                 :             : #include <span.h>
      12                 :             : #include <util/check.h>
      13                 :             : 
      14                 :             : /** Data structure storing a fee and size, ordered by increasing fee/size.
      15                 :             :  *
      16                 :             :  * The size of a FeeFrac cannot be zero unless the fee is also zero.
      17                 :             :  *
      18                 :             :  * FeeFracs have a total ordering, first by increasing feerate (ratio of fee over size), and then
      19                 :             :  * by decreasing size. The empty FeeFrac (fee and size both 0) sorts last. So for example, the
      20                 :             :  * following FeeFracs are in sorted order:
      21                 :             :  *
      22                 :             :  * - fee=0 size=1 (feerate 0)
      23                 :             :  * - fee=1 size=2 (feerate 0.5)
      24                 :             :  * - fee=2 size=3 (feerate 0.667...)
      25                 :             :  * - fee=2 size=2 (feerate 1)
      26                 :             :  * - fee=1 size=1 (feerate 1)
      27                 :             :  * - fee=3 size=2 (feerate 1.5)
      28                 :             :  * - fee=2 size=1 (feerate 2)
      29                 :             :  * - fee=0 size=0 (undefined feerate)
      30                 :             :  *
      31                 :             :  * A FeeFrac is considered "better" if it sorts after another, by this ordering. All standard
      32                 :             :  * comparison operators (<=>, ==, !=, >, <, >=, <=) respect this ordering.
      33                 :             :  *
      34                 :             :  * The FeeRateCompare, and >> and << operators only compare feerate and treat equal feerate but
      35                 :             :  * different size as equivalent. The empty FeeFrac is neither lower or higher in feerate than any
      36                 :             :  * other.
      37                 :             :  */
      38                 :             : struct FeeFrac
      39                 :             : {
      40                 :             :     /** Helper function for 32*64 signed multiplication, returning an unspecified but totally
      41                 :             :      *  ordered type. This is a fallback version, separate so it can be tested on platforms where
      42                 :             :      *  it isn't actually needed. */
      43                 :             :     static inline std::pair<int64_t, uint32_t> MulFallback(int64_t a, int32_t b) noexcept
      44                 :             :     {
      45                 :             :         int64_t low = int64_t{static_cast<uint32_t>(a)} * b;
      46                 :             :         int64_t high = (a >> 32) * b;
      47                 :             :         return {high + (low >> 32), static_cast<uint32_t>(low)};
      48                 :             :     }
      49                 :             : 
      50                 :             :     /** Helper function for 96/32 signed division, rounding towards negative infinity (if
      51                 :             :      *  round_down) or positive infinity (if !round_down). This is a fallback version, separate so
      52                 :             :      *  that it can be tested on platforms where it isn't actually needed.
      53                 :             :      *
      54                 :             :      * The exact behavior with negative n does not really matter, but this implementation chooses
      55                 :             :      * to be consistent for testability reasons.
      56                 :             :      *
      57                 :             :      * The result must fit in an int64_t, and d must be strictly positive. */
      58                 :             :     static inline int64_t DivFallback(std::pair<int64_t, uint32_t> n, int32_t d, bool round_down) noexcept
      59                 :             :     {
      60                 :             :         Assume(d > 0);
      61                 :             :         // Compute quot_high = n.first / d, so the result becomes
      62                 :             :         // (n.second + (n.first - quot_high * d) * 2**32) / d + (quot_high * 2**32), or
      63                 :             :         // (n.second + (n.first % d) * 2**32) / d + (quot_high * 2**32).
      64                 :             :         int64_t quot_high = n.first / d;
      65                 :             :         // Evaluate the parenthesized expression above, so the result becomes
      66                 :             :         // n_low / d + (quot_high * 2**32)
      67                 :             :         int64_t n_low = ((n.first % d) << 32) + n.second;
      68                 :             :         // Evaluate the division so the result becomes quot_low + quot_high * 2**32. It is possible
      69                 :             :         // that the / operator here rounds in the wrong direction (if n_low is not a multiple of
      70                 :             :         // size, and is (if round_down) negative, or (if !round_down) positive). If so, make a
      71                 :             :         // correction.
      72                 :             :         int64_t quot_low = n_low / d;
      73                 :             :         int32_t mod_low = n_low % d;
      74                 :             :         quot_low += (mod_low > 0) - (mod_low && round_down);
      75                 :             :         // Combine and return the result
      76                 :             :         return (quot_high << 32) + quot_low;
      77                 :             :     }
      78                 :             : 
      79                 :             : #ifdef __SIZEOF_INT128__
      80                 :             :     /** Helper function for 32*64 signed multiplication, returning an unspecified but totally
      81                 :             :      *  ordered type. This is a version relying on __int128. */
      82                 :    42432673 :     static inline __int128 Mul(int64_t a, int32_t b) noexcept
      83                 :             :     {
      84                 :    42432675 :         return __int128{a} * b;
      85                 :             :     }
      86                 :             : 
      87                 :             :     /** Helper function for 96/32 signed division, rounding towards negative infinity (if
      88                 :             :      *  round_down), or towards positive infinity (if !round_down). This is a
      89                 :             :      *  version relying on __int128.
      90                 :             :      *
      91                 :             :      * The result must fit in an int64_t, and d must be strictly positive. */
      92                 :          42 :     static inline int64_t Div(__int128 n, int32_t d, bool round_down) noexcept
      93                 :             :     {
      94                 :          42 :         Assume(d > 0);
      95                 :             :         // Compute the division.
      96                 :          42 :         int64_t quot = n / d;
      97                 :          42 :         int32_t mod = n % d;
      98                 :             :         // Correct result if the / operator above rounded in the wrong direction.
      99                 :          42 :         return quot + (mod > 0) - (mod && round_down);
     100                 :             :     }
     101                 :             : #else
     102                 :             :     static constexpr auto Mul = MulFallback;
     103                 :             :     static constexpr auto Div = DivFallback;
     104                 :             : #endif
     105                 :             : 
     106                 :             :     int64_t fee;
     107                 :             :     int32_t size;
     108                 :             : 
     109                 :             :     /** Construct an IsEmpty() FeeFrac. */
     110                 :          30 :     constexpr inline FeeFrac() noexcept : fee{0}, size{0} {}
     111                 :             : 
     112                 :             :     /** Construct a FeeFrac with specified fee and size. */
     113   [ +  +  +  + ]:    39038749 :     constexpr inline FeeFrac(int64_t f, int32_t s) noexcept : fee{f}, size{s} {}
         [ +  + ][ +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                -  +  - ]
     114                 :             : 
     115                 :             :     constexpr inline FeeFrac(const FeeFrac&) noexcept = default;
     116                 :             :     constexpr inline FeeFrac& operator=(const FeeFrac&) noexcept = default;
     117                 :             : 
     118                 :             :     /** Check if this is empty (size and fee are 0). */
     119                 :          20 :     bool inline IsEmpty() const noexcept {
     120   [ +  -  #  #  :          20 :         return size == 0;
             #  #  #  # ]
                 [ #  # ]
     121                 :             :     }
     122                 :             : 
     123                 :             :     /** Add fee and size of another FeeFrac to this one. */
     124                 :         276 :     void inline operator+=(const FeeFrac& other) noexcept
     125                 :             :     {
     126                 :         276 :         fee += other.fee;
     127   [ #  #  #  #  :         276 :         size += other.size;
             #  #  #  # ]
     128                 :             :     }
     129                 :             : 
     130                 :             :     /** Subtract fee and size of another FeeFrac from this one. */
     131                 :           0 :     void inline operator-=(const FeeFrac& other) noexcept
     132                 :             :     {
     133                 :           0 :         fee -= other.fee;
     134         [ #  # ]:           0 :         size -= other.size;
     135                 :             :     }
     136                 :             : 
     137                 :             :     /** Sum fee and size. */
     138                 :         452 :     friend inline FeeFrac operator+(const FeeFrac& a, const FeeFrac& b) noexcept
     139                 :             :     {
     140                 :         452 :         return {a.fee + b.fee, a.size + b.size};
     141                 :             :     }
     142                 :             : 
     143                 :             :     /** Subtract both fee and size. */
     144                 :         338 :     friend inline FeeFrac operator-(const FeeFrac& a, const FeeFrac& b) noexcept
     145                 :             :     {
     146         [ +  - ]:         249 :         return {a.fee - b.fee, a.size - b.size};
           [ +  +  +  + ]
     147                 :             :     }
     148                 :             : 
     149                 :             :     /** Check if two FeeFrac objects are equal (both same fee and same size). */
     150                 :    19514731 :     friend inline bool operator==(const FeeFrac& a, const FeeFrac& b) noexcept
     151                 :             :     {
     152   [ +  +  +  + ]:    19514731 :         return a.fee == b.fee && a.size == b.size;
           [ +  -  +  +  
             +  +  +  + ]
           [ +  -  -  +  
          +  -  -  +  +  
          -  -  +  -  +  
          -  -  -  +  -  
          -  +  -  -  +  
          +  -  -  +  -  
                +  -  - ]
     153                 :             :     }
     154                 :             : 
     155                 :             :     /** Compare two FeeFracs just by feerate. */
     156                 :         189 :     friend inline std::weak_ordering FeeRateCompare(const FeeFrac& a, const FeeFrac& b) noexcept
     157                 :             :     {
     158                 :         189 :         auto cross_a = Mul(a.fee, b.size), cross_b = Mul(b.fee, a.size);
     159   [ +  +  +  + ]:         189 :         return cross_a <=> cross_b;
     160                 :             :     }
     161                 :             : 
     162                 :             :     /** Check if a FeeFrac object has strictly lower feerate than another. */
     163                 :           4 :     friend inline bool operator<<(const FeeFrac& a, const FeeFrac& b) noexcept
     164                 :             :     {
     165         [ +  - ]:           1 :         auto cross_a = Mul(a.fee, b.size), cross_b = Mul(b.fee, a.size);
     166   [ +  -  +  -  :           4 :         return cross_a < cross_b;
             +  -  +  - ]
           [ #  #  #  #  
             #  #  #  # ]
     167                 :             :     }
     168                 :             : 
     169                 :             :     /** Check if a FeeFrac object has strictly higher feerate than another. */
     170                 :          69 :     friend inline bool operator>>(const FeeFrac& a, const FeeFrac& b) noexcept
     171                 :             :     {
     172         [ +  - ]:          67 :         auto cross_a = Mul(a.fee, b.size), cross_b = Mul(b.fee, a.size);
     173 [ +  + ][ +  -  :          69 :         return cross_a > cross_b;
          +  -  +  -  #  
           #  #  # ][ #  
             #  #  #  #  
                      # ]
     174                 :             :     }
     175                 :             : 
     176                 :             :     /** Compare two FeeFracs. <, >, <=, and >= are auto-generated from this. */
     177                 :    42432369 :     friend inline std::strong_ordering operator<=>(const FeeFrac& a, const FeeFrac& b) noexcept
     178                 :             :     {
     179                 :    42432369 :         auto cross_a = Mul(a.fee, b.size), cross_b = Mul(b.fee, a.size);
     180   [ +  +  +  +  :    42432369 :         if (cross_a == cross_b) return b.size <=> a.size;
                   +  + ]
     181         [ +  + ]:      560779 :         return cross_a <=> cross_b;
     182                 :             :     }
     183                 :             : 
     184                 :             :     /** Swap two FeeFracs. */
     185                 :         145 :     friend inline void swap(FeeFrac& a, FeeFrac& b) noexcept
     186                 :             :     {
     187                 :           7 :         std::swap(a.fee, b.fee);
     188                 :           7 :         std::swap(a.size, b.size);
     189                 :             :     }
     190                 :             : 
     191                 :             :     /** Compute the fee for a given size `at_size` using this object's feerate.
     192                 :             :      *
     193                 :             :      * This effectively corresponds to evaluating (this->fee * at_size) / this->size, with the
     194                 :             :      * result rounded towards negative infinity (if RoundDown) or towards positive infinity
     195                 :             :      * (if !RoundDown).
     196                 :             :      *
     197                 :             :      * Requires this->size > 0, at_size >= 0, and that the correct result fits in a int64_t. This
     198                 :             :      * is guaranteed to be the case when 0 <= at_size <= this->size.
     199                 :             :      */
     200                 :             :     template<bool RoundDown>
     201                 :          60 :     int64_t EvaluateFee(int32_t at_size) const noexcept
     202                 :             :     {
     203                 :             :         Assume(size > 0);
     204                 :             :         Assume(at_size >= 0);
     205         [ +  + ]:          60 :         if (fee >= 0 && fee < 0x200000000) [[likely]] {
     206                 :             :             // Common case where (this->fee * at_size) is guaranteed to fit in a uint64_t.
     207                 :             :             if constexpr (RoundDown) {
     208                 :           9 :                 return (uint64_t(fee) * at_size) / uint32_t(size);
     209                 :             :             } else {
     210                 :           9 :                 return (uint64_t(fee) * at_size + size - 1U) / uint32_t(size);
     211                 :             :             }
     212                 :             :         } else {
     213                 :             :             // Otherwise, use Mul and Div.
     214                 :          42 :             return Div(Mul(fee, at_size), size, RoundDown);
     215                 :             :         }
     216                 :             :     }
     217                 :             : 
     218                 :             : public:
     219                 :             :     /** Compute the fee for a given size `at_size` using this object's feerate, rounding down. */
     220   [ +  -  +  -  :          30 :     int64_t EvaluateFeeDown(int32_t at_size) const noexcept { return EvaluateFee<true>(at_size); }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     221                 :             :     /** Compute the fee for a given size `at_size` using this object's feerate, rounding up. */
     222   [ +  -  +  -  :          30 :     int64_t EvaluateFeeUp(int32_t at_size) const noexcept { return EvaluateFee<false>(at_size); }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     223                 :             : };
     224                 :             : 
     225                 :             : /** Compare the feerate diagrams implied by the provided sorted chunks data.
     226                 :             :  *
     227                 :             :  * The implied diagram for each starts at (0, 0), then contains for each chunk the cumulative fee
     228                 :             :  * and size up to that chunk, and then extends infinitely to the right with a horizontal line.
     229                 :             :  *
     230                 :             :  * The caller must guarantee that the sum of the FeeFracs in either of the chunks' data set do not
     231                 :             :  * overflow (so sum fees < 2^63, and sum sizes < 2^31).
     232                 :             :  */
     233                 :             : std::partial_ordering CompareChunks(std::span<const FeeFrac> chunks0, std::span<const FeeFrac> chunks1);
     234                 :             : 
     235                 :             : /** Tagged wrapper around FeeFrac to avoid unit confusion. */
     236                 :             : template<typename Tag>
     237                 :           0 : struct FeePerUnit : public FeeFrac
     238                 :             : {
     239                 :             :     // Inherit FeeFrac constructors.
     240                 :           0 :     using FeeFrac::FeeFrac;
     241                 :             : 
     242                 :             :     /** Convert a FeeFrac to a FeePerUnit. */
     243                 :           0 :     static FeePerUnit FromFeeFrac(const FeeFrac& feefrac) noexcept
     244                 :             :     {
     245         [ #  # ]:           0 :         return {feefrac.fee, feefrac.size};
     246                 :             :     }
     247                 :             : };
     248                 :             : 
     249                 :             : // FeePerUnit instance for satoshi / vbyte.
     250                 :             : struct VSizeTag {};
     251                 :             : using FeePerVSize = FeePerUnit<VSizeTag>;
     252                 :             : 
     253                 :             : // FeePerUnit instance for satoshi / WU.
     254                 :             : struct WeightTag {};
     255                 :             : using FeePerWeight = FeePerUnit<WeightTag>;
     256                 :             : 
     257                 :             : #endif // BITCOIN_UTIL_FEEFRAC_H
        

Generated by: LCOV version 2.0-1