/tmp/bitcoin/src/util/feefrac.h
Line | Count | Source |
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 <util/check.h> |
9 | | #include <util/overflow.h> |
10 | | |
11 | | #include <compare> |
12 | | #include <concepts> |
13 | | #include <cstdint> |
14 | | #include <span> |
15 | | #include <utility> |
16 | | |
17 | | /** Data structure storing a fee and size. |
18 | | * |
19 | | * The size of a FeeFrac cannot be zero unless the fee is also zero. |
20 | | */ |
21 | | struct FeeFrac |
22 | | { |
23 | | /** Helper function for 32*64 signed multiplication, returning an unspecified but totally |
24 | | * ordered type. This is a fallback version, separate so it can be tested on platforms where |
25 | | * it isn't actually needed. */ |
26 | | static inline std::pair<int64_t, uint32_t> MulFallback(int64_t a, int32_t b) noexcept |
27 | 0 | { |
28 | 0 | int64_t low = int64_t{static_cast<uint32_t>(a)} * b; |
29 | 0 | int64_t high = (a >> 32) * b; |
30 | 0 | return {high + (low >> 32), static_cast<uint32_t>(low)}; |
31 | 0 | } |
32 | | |
33 | | /** Helper function for 96/32 signed division, rounding towards negative infinity (if |
34 | | * round_down) or positive infinity (if !round_down). This is a fallback version, separate so |
35 | | * that it can be tested on platforms where it isn't actually needed. |
36 | | * |
37 | | * The exact behavior with negative n does not really matter, but this implementation chooses |
38 | | * to be consistent for testability reasons. |
39 | | * |
40 | | * The result must fit in an int64_t, and d must be strictly positive. */ |
41 | | static inline int64_t DivFallback(std::pair<int64_t, uint32_t> n, int32_t d, bool round_down) noexcept |
42 | 0 | { |
43 | 0 | Assume(d > 0); |
44 | 0 | // Compute quot_high = n.first / d, so the result becomes |
45 | 0 | // (n.second + (n.first - quot_high * d) * 2**32) / d + (quot_high * 2**32), or |
46 | 0 | // (n.second + (n.first % d) * 2**32) / d + (quot_high * 2**32). |
47 | 0 | int64_t quot_high = n.first / d; |
48 | 0 | // Evaluate the parenthesized expression above, so the result becomes |
49 | 0 | // n_low / d + (quot_high * 2**32) |
50 | 0 | int64_t n_low = ((n.first % d) << 32) + n.second; |
51 | 0 | // Evaluate the division so the result becomes quot_low + quot_high * 2**32. It is possible |
52 | 0 | // that the / operator here rounds in the wrong direction (if n_low is not a multiple of |
53 | 0 | // size, and is (if round_down) negative, or (if !round_down) positive). If so, make a |
54 | 0 | // correction. |
55 | 0 | int64_t quot_low = n_low / d; |
56 | 0 | int32_t mod_low = n_low % d; |
57 | 0 | quot_low += (mod_low > 0) - (mod_low && round_down); |
58 | 0 | // Combine and return the result |
59 | 0 | return (quot_high << 32) + quot_low; |
60 | 0 | } |
61 | | |
62 | | #ifdef __SIZEOF_INT128__ |
63 | | /** Helper function for 32*64 signed multiplication, returning an unspecified but totally |
64 | | * ordered type. This is a version relying on __int128. */ |
65 | | static inline __int128 Mul(int64_t a, int32_t b) noexcept |
66 | 446M | { |
67 | 446M | return __int128{a} * b; |
68 | 446M | } |
69 | | |
70 | | /** Helper function for 96/32 signed division, rounding towards negative infinity (if |
71 | | * round_down), or towards positive infinity (if !round_down). This is a |
72 | | * version relying on __int128. |
73 | | * |
74 | | * The result must fit in an int64_t, and d must be strictly positive. */ |
75 | | static inline int64_t Div(__int128 n, int32_t d, bool round_down) noexcept |
76 | 72 | { |
77 | 72 | Assume(d > 0); |
78 | | // Compute the division. |
79 | 72 | int64_t quot = n / d; |
80 | 72 | int32_t mod = n % d; |
81 | | // Correct result if the / operator above rounded in the wrong direction. |
82 | 72 | return quot + ((mod > 0) - (mod && round_down)); |
83 | 72 | } |
84 | | #else |
85 | | static constexpr auto Mul = MulFallback; |
86 | | static constexpr auto Div = DivFallback; |
87 | | #endif |
88 | | |
89 | | int64_t fee; |
90 | | int32_t size; |
91 | | |
92 | | /** Construct an IsEmpty() FeeFrac. */ |
93 | 21.7M | constexpr inline FeeFrac() noexcept : fee{0}, size{0} {} |
94 | | |
95 | | /** Construct a FeeFrac with specified fee and size. */ |
96 | 80.6M | constexpr inline FeeFrac(int64_t f, int32_t s) noexcept : fee{f}, size{s} {} |
97 | | |
98 | | constexpr inline FeeFrac(const FeeFrac&) noexcept = default; |
99 | | constexpr inline FeeFrac& operator=(const FeeFrac&) noexcept = default; |
100 | | |
101 | | /** Check if this is empty (size and fee are 0). */ |
102 | 9.78M | bool inline IsEmpty() const noexcept { |
103 | 9.78M | return size == 0; |
104 | 9.78M | } |
105 | | |
106 | | /** Add fee and size of another FeeFrac to this one. */ |
107 | | void inline operator+=(const FeeFrac& other) noexcept |
108 | 43.3M | { |
109 | 43.3M | fee += other.fee; |
110 | 43.3M | size += other.size; |
111 | 43.3M | } |
112 | | |
113 | | /** Subtract fee and size of another FeeFrac from this one. */ |
114 | | void inline operator-=(const FeeFrac& other) noexcept |
115 | 15.8M | { |
116 | 15.8M | fee -= other.fee; |
117 | 15.8M | size -= other.size; |
118 | 15.8M | } |
119 | | |
120 | | /** Sum fee and size. */ |
121 | | friend inline FeeFrac operator+(const FeeFrac& a, const FeeFrac& b) noexcept |
122 | 8.56k | { |
123 | 8.56k | return {a.fee + b.fee, a.size + b.size}; |
124 | 8.56k | } |
125 | | |
126 | | /** Subtract both fee and size. */ |
127 | | friend inline FeeFrac operator-(const FeeFrac& a, const FeeFrac& b) noexcept |
128 | 4.64k | { |
129 | 4.64k | return {a.fee - b.fee, a.size - b.size}; |
130 | 4.64k | } |
131 | | |
132 | | /** Check if two FeeFrac objects are equal (both same fee and same size). */ |
133 | | friend inline bool operator==(const FeeFrac& a, const FeeFrac& b) noexcept |
134 | 35.4M | { |
135 | 35.4M | return a.fee == b.fee && a.size == b.size; |
136 | 35.4M | } |
137 | | |
138 | | /** Swap two FeeFracs. */ |
139 | | friend inline void swap(FeeFrac& a, FeeFrac& b) noexcept |
140 | 1.86k | { |
141 | 1.86k | std::swap(a.fee, b.fee); |
142 | 1.86k | std::swap(a.size, b.size); |
143 | 1.86k | } |
144 | | |
145 | | /** Compute the fee for a given size `at_size` using this object's feerate. |
146 | | * |
147 | | * This effectively corresponds to evaluating (this->fee * at_size) / this->size, with the |
148 | | * result rounded towards negative infinity (if RoundDown) or towards positive infinity |
149 | | * (if !RoundDown). |
150 | | * |
151 | | * Requires this->size > 0, at_size >= 0, and that the correct result fits in a int64_t. This |
152 | | * is guaranteed to be the case when 0 <= at_size <= this->size. |
153 | | */ |
154 | | template<bool RoundDown> |
155 | | int64_t EvaluateFee(int32_t at_size) const noexcept |
156 | 2.51M | { |
157 | 2.51M | Assume(size > 0); |
158 | 2.51M | Assume(at_size >= 0); |
159 | 2.51M | if (fee >= 0 && fee < 0x200000000) [[likely]] { |
160 | | // Common case where (this->fee * at_size) is guaranteed to fit in a uint64_t. |
161 | 2.51M | if constexpr (RoundDown) { |
162 | 672k | return (uint64_t(fee) * at_size) / uint32_t(size); |
163 | 1.83M | } else { |
164 | 1.83M | return CeilDiv(uint64_t(fee) * at_size, uint32_t(size)); |
165 | 1.83M | } |
166 | 2.51M | } else { |
167 | | // Otherwise, use Mul and Div. |
168 | 72 | return Div(Mul(fee, at_size), size, RoundDown); |
169 | 72 | } |
170 | 2.51M | } long FeeFrac::EvaluateFee<true>(int) const Line | Count | Source | 156 | 672k | { | 157 | 672k | Assume(size > 0); | 158 | 672k | Assume(at_size >= 0); | 159 | 672k | if (fee >= 0 && fee < 0x200000000) [[likely]] { | 160 | | // Common case where (this->fee * at_size) is guaranteed to fit in a uint64_t. | 161 | 672k | if constexpr (RoundDown) { | 162 | 672k | return (uint64_t(fee) * at_size) / uint32_t(size); | 163 | | } else { | 164 | | return CeilDiv(uint64_t(fee) * at_size, uint32_t(size)); | 165 | | } | 166 | 672k | } else { | 167 | | // Otherwise, use Mul and Div. | 168 | 42 | return Div(Mul(fee, at_size), size, RoundDown); | 169 | 42 | } | 170 | 672k | } |
long FeeFrac::EvaluateFee<false>(int) const Line | Count | Source | 156 | 1.83M | { | 157 | 1.83M | Assume(size > 0); | 158 | 1.83M | Assume(at_size >= 0); | 159 | 1.83M | if (fee >= 0 && fee < 0x200000000) [[likely]] { | 160 | | // Common case where (this->fee * at_size) is guaranteed to fit in a uint64_t. | 161 | | if constexpr (RoundDown) { | 162 | | return (uint64_t(fee) * at_size) / uint32_t(size); | 163 | 1.83M | } else { | 164 | 1.83M | return CeilDiv(uint64_t(fee) * at_size, uint32_t(size)); | 165 | 1.83M | } | 166 | 1.83M | } else { | 167 | | // Otherwise, use Mul and Div. | 168 | 30 | return Div(Mul(fee, at_size), size, RoundDown); | 169 | 30 | } | 170 | 1.83M | } |
|
171 | | |
172 | | public: |
173 | | /** Compute the fee for a given size `at_size` using this object's feerate, rounding down. */ |
174 | 672k | int64_t EvaluateFeeDown(int32_t at_size) const noexcept { return EvaluateFee<true>(at_size); } |
175 | | /** Compute the fee for a given size `at_size` using this object's feerate, rounding up. */ |
176 | 1.83M | int64_t EvaluateFeeUp(int32_t at_size) const noexcept { return EvaluateFee<false>(at_size); } |
177 | | }; |
178 | | |
179 | | /** Compare the feerate diagrams implied by the provided sorted chunks data. |
180 | | * |
181 | | * The implied diagram for each starts at (0, 0), then contains for each chunk the cumulative fee |
182 | | * and size up to that chunk, and then extends infinitely to the right with a horizontal line. |
183 | | * |
184 | | * The caller must guarantee that the sum of the FeeFracs in either of the chunks' data set do not |
185 | | * overflow (so sum fees < 2^63, and sum sizes < 2^31). |
186 | | */ |
187 | | std::partial_ordering CompareChunks(std::span<const FeeFrac> chunks0, std::span<const FeeFrac> chunks1); |
188 | | |
189 | | /** Tagged wrapper around FeeFrac to avoid unit confusion. */ |
190 | | template<typename Tag> |
191 | | struct FeePerUnit : public FeeFrac |
192 | | { |
193 | | // Inherit FeeFrac constructors. |
194 | | using FeeFrac::FeeFrac; |
195 | | |
196 | | /** Convert a FeeFrac to a FeePerUnit. */ |
197 | | static FeePerUnit FromFeeFrac(const FeeFrac& feefrac) noexcept |
198 | 73.3k | { |
199 | 73.3k | return {feefrac.fee, feefrac.size}; |
200 | 73.3k | } |
201 | | }; |
202 | | |
203 | | // FeePerUnit instance for satoshi / vbyte. |
204 | | struct VSizeTag {}; |
205 | | using FeePerVSize = FeePerUnit<VSizeTag>; |
206 | | |
207 | | // FeePerUnit instance for satoshi / WU. |
208 | | struct WeightTag {}; |
209 | | using FeePerWeight = FeePerUnit<WeightTag>; |
210 | | |
211 | | /** Wrapper around FeeFrac & derived types, which adds a feerate-based ordering which treats |
212 | | * equal-feerate but distinct-size FeeFracs as equals. |
213 | | * |
214 | | * This is not included inside FeeFrac itself, because it is not a total ordering (as would be |
215 | | * expected for built-in comparison operators). |
216 | | */ |
217 | | template<std::derived_from<FeeFrac> T> |
218 | | class ByRatio |
219 | | { |
220 | | const T& m_feefrac; |
221 | | |
222 | | public: |
223 | 358M | constexpr ByRatio(const T& feefrac) noexcept : m_feefrac{feefrac} {}ByRatio<FeePerUnit<VSizeTag>>::ByRatio(FeePerUnit<VSizeTag> const&) Line | Count | Source | 223 | 206k | constexpr ByRatio(const T& feefrac) noexcept : m_feefrac{feefrac} {} |
ByRatio<FeeFrac>::ByRatio(FeeFrac const&) Line | Count | Source | 223 | 165M | constexpr ByRatio(const T& feefrac) noexcept : m_feefrac{feefrac} {} |
ByRatio<FeePerUnit<WeightTag>>::ByRatio(FeePerUnit<WeightTag> const&) Line | Count | Source | 223 | 193M | constexpr ByRatio(const T& feefrac) noexcept : m_feefrac{feefrac} {} |
|
224 | | |
225 | | friend bool operator==(const ByRatio& a, const ByRatio& b) noexcept |
226 | 154k | { |
227 | 154k | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); |
228 | 154k | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); |
229 | 154k | return cross_a == cross_b; |
230 | 154k | } operator==(ByRatio<FeePerUnit<VSizeTag>> const&, ByRatio<FeePerUnit<VSizeTag>> const&) Line | Count | Source | 226 | 8.63k | { | 227 | 8.63k | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); | 228 | 8.63k | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); | 229 | 8.63k | return cross_a == cross_b; | 230 | 8.63k | } |
operator==(ByRatio<FeeFrac> const&, ByRatio<FeeFrac> const&) Line | Count | Source | 226 | 146k | { | 227 | 146k | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); | 228 | 146k | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); | 229 | 146k | return cross_a == cross_b; | 230 | 146k | } |
|
231 | | |
232 | | // Note that we can use std::strong_ordering here, because even though FeeFrac{1,2} and |
233 | | // FeeFrac{2,4} are distinct as FeeFracs, they are indistinguishable from ByRatio's perspective |
234 | | // (operator== also treats them as equal). |
235 | | friend std::strong_ordering operator<=>(const ByRatio& a, const ByRatio& b) noexcept |
236 | 164M | { |
237 | 164M | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); |
238 | 164M | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); |
239 | 164M | return cross_a <=> cross_b; |
240 | 164M | } operator<=>(ByRatio<FeePerUnit<VSizeTag>> const&, ByRatio<FeePerUnit<VSizeTag>> const&) Line | Count | Source | 236 | 52.2k | { | 237 | 52.2k | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); | 238 | 52.2k | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); | 239 | 52.2k | return cross_a <=> cross_b; | 240 | 52.2k | } |
operator<=>(ByRatio<FeeFrac> const&, ByRatio<FeeFrac> const&) Line | Count | Source | 236 | 67.5M | { | 237 | 67.5M | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); | 238 | 67.5M | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); | 239 | 67.5M | return cross_a <=> cross_b; | 240 | 67.5M | } |
operator<=>(ByRatio<FeePerUnit<WeightTag>> const&, ByRatio<FeePerUnit<WeightTag>> const&) Line | Count | Source | 236 | 96.6M | { | 237 | 96.6M | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); | 238 | 96.6M | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); | 239 | 96.6M | return cross_a <=> cross_b; | 240 | 96.6M | } |
|
241 | | |
242 | | // Specialized versions for efficiency. GCC 15+ and Clang 11+ produce operator<=>-derived |
243 | | // versions that are equally efficient as this at -O2, but earlier versions do not. |
244 | | friend bool operator<(const ByRatio& a, const ByRatio& b) noexcept |
245 | 6.59M | { |
246 | 6.59M | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); |
247 | 6.59M | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); |
248 | 6.59M | return cross_a < cross_b; |
249 | 6.59M | } operator<(ByRatio<FeeFrac> const&, ByRatio<FeeFrac> const&) Line | Count | Source | 245 | 6.54M | { | 246 | 6.54M | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); | 247 | 6.54M | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); | 248 | 6.54M | return cross_a < cross_b; | 249 | 6.54M | } |
operator<(ByRatio<FeePerUnit<VSizeTag>> const&, ByRatio<FeePerUnit<VSizeTag>> const&) Line | Count | Source | 245 | 42.2k | { | 246 | 42.2k | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); | 247 | 42.2k | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); | 248 | 42.2k | return cross_a < cross_b; | 249 | 42.2k | } |
|
250 | | friend bool operator>(const ByRatio& a, const ByRatio& b) noexcept |
251 | 405k | { |
252 | 405k | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); |
253 | 405k | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); |
254 | 405k | return cross_a > cross_b; |
255 | 405k | } |
256 | | friend bool operator<=(const ByRatio& a, const ByRatio& b) noexcept |
257 | | { |
258 | | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); |
259 | | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); |
260 | | return cross_a <= cross_b; |
261 | | } |
262 | | friend bool operator>=(const ByRatio& a, const ByRatio& b) noexcept |
263 | 7.83M | { |
264 | 7.83M | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); |
265 | 7.83M | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); |
266 | 7.83M | return cross_a >= cross_b; |
267 | 7.83M | } |
268 | | }; |
269 | | |
270 | | /** Wrapper around FeeFrac & derived types, which adds a total ordering which first sorts by feerate |
271 | | * and then by reversed size (i.e., larger sizes come first). |
272 | | * |
273 | | * This is not included inside FeeFrac itself, because it is not the most natural behavior, so it |
274 | | * is better to make code using it invoke this explicitly. |
275 | | * |
276 | | * The empty FeeFrac (fee and size both 0) sorts last. So for example, the following FeeFracs are |
277 | | * in sorted order: |
278 | | * |
279 | | * - fee=0 size=1 (feerate 0) |
280 | | * - fee=1 size=2 (feerate 0.5) |
281 | | * - fee=2 size=3 (feerate 0.667...) |
282 | | * - fee=2 size=2 (feerate 1) |
283 | | * - fee=1 size=1 (feerate 1) |
284 | | * - fee=3 size=2 (feerate 1.5) |
285 | | * - fee=2 size=1 (feerate 2) |
286 | | * - fee=0 size=0 (undefined feerate) |
287 | | */ |
288 | | template<std::derived_from<FeeFrac> T> |
289 | | class ByRatioNegSize |
290 | | { |
291 | | const T& m_feefrac; |
292 | | |
293 | | public: |
294 | 88.2M | constexpr ByRatioNegSize(const T& feefrac) noexcept : m_feefrac{feefrac} {}ByRatioNegSize<FeeFrac>::ByRatioNegSize(FeeFrac const&) Line | Count | Source | 294 | 86.0M | constexpr ByRatioNegSize(const T& feefrac) noexcept : m_feefrac{feefrac} {} |
ByRatioNegSize<FeePerUnit<WeightTag>>::ByRatioNegSize(FeePerUnit<WeightTag> const&) Line | Count | Source | 294 | 2.15M | constexpr ByRatioNegSize(const T& feefrac) noexcept : m_feefrac{feefrac} {} |
|
295 | | |
296 | | friend bool operator==(const ByRatioNegSize& a, const ByRatioNegSize& b) noexcept |
297 | 1 | { |
298 | 1 | return a.m_feefrac == b.m_feefrac; |
299 | 1 | } |
300 | | |
301 | | friend std::strong_ordering operator<=>(const ByRatioNegSize& a, const ByRatioNegSize& b) noexcept |
302 | 44.1M | { |
303 | 44.1M | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); |
304 | 44.1M | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); |
305 | 44.1M | auto cmp = cross_a <=> cross_b; |
306 | 44.1M | if (cmp != 0) return cmp; |
307 | 42.2M | return b.m_feefrac.size <=> a.m_feefrac.size; |
308 | 44.1M | } operator<=>(ByRatioNegSize<FeeFrac> const&, ByRatioNegSize<FeeFrac> const&) Line | Count | Source | 302 | 43.0M | { | 303 | 43.0M | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); | 304 | 43.0M | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); | 305 | 43.0M | auto cmp = cross_a <=> cross_b; | 306 | 43.0M | if (cmp != 0) return cmp; | 307 | 42.2M | return b.m_feefrac.size <=> a.m_feefrac.size; | 308 | 43.0M | } |
operator<=>(ByRatioNegSize<FeePerUnit<WeightTag>> const&, ByRatioNegSize<FeePerUnit<WeightTag>> const&) Line | Count | Source | 302 | 1.07M | { | 303 | 1.07M | auto cross_a = T::Mul(a.m_feefrac.fee, b.m_feefrac.size); | 304 | 1.07M | auto cross_b = T::Mul(b.m_feefrac.fee, a.m_feefrac.size); | 305 | 1.07M | auto cmp = cross_a <=> cross_b; | 306 | 1.07M | if (cmp != 0) return cmp; | 307 | 31 | return b.m_feefrac.size <=> a.m_feefrac.size; | 308 | 1.07M | } |
|
309 | | |
310 | | // Support conversion back to underlying FeeFrac, which allows using std::max(). |
311 | 39.5M | operator const T&() const noexcept { return m_feefrac; } |
312 | | }; |
313 | | |
314 | | #endif // BITCOIN_UTIL_FEEFRAC_H |