rippled
FeeUnits.h
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2019 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 
19 #ifndef BASICS_FEES_H_INCLUDED
20 #define BASICS_FEES_H_INCLUDED
21 
22 #include <ripple/basics/XRPAmount.h>
23 #include <boost/multiprecision/cpp_int.hpp>
24 #include <limits>
25 #include <utility>
26 
27 #include <cassert>
28 #include <cmath>
29 #include <ios>
30 #include <iosfwd>
31 #include <sstream>
32 #include <string>
33 
34 namespace ripple {
35 
36 namespace feeunit {
37 
41 struct feeunitTag;
45 struct feelevelTag;
48 struct unitlessTag;
49 
50 template <class T>
51 using enable_if_unit_t = typename std::enable_if_t<
52  std::is_class_v<T> && std::is_object_v<typename T::unit_type> &&
53  std::is_object_v<typename T::value_type>>;
54 
62 template <class T, class = enable_if_unit_t<T>>
63 constexpr bool is_usable_unit_v =
64  std::is_same_v<typename T::unit_type, feeunitTag> ||
65  std::is_same_v<typename T::unit_type, feelevelTag> ||
66  std::is_same_v<typename T::unit_type, unitlessTag> ||
67  std::is_same_v<typename T::unit_type, dropTag>;
68 
69 template <class UnitTag, class T>
70 class TaggedFee : private boost::totally_ordered<TaggedFee<UnitTag, T>>,
71  private boost::additive<TaggedFee<UnitTag, T>>,
72  private boost::equality_comparable<TaggedFee<UnitTag, T>, T>,
73  private boost::dividable<TaggedFee<UnitTag, T>, T>,
74  private boost::modable<TaggedFee<UnitTag, T>, T>,
75  private boost::unit_steppable<TaggedFee<UnitTag, T>>
76 {
77 public:
78  using unit_type = UnitTag;
79  using value_type = T;
80 
81 private:
83 
84 protected:
85  template <class Other>
86  static constexpr bool is_compatible_v =
87  std::is_arithmetic_v<Other>&& std::is_arithmetic_v<value_type>&&
88  std::is_convertible_v<Other, value_type>;
89 
90  template <class OtherFee, class = enable_if_unit_t<OtherFee>>
91  static constexpr bool is_compatiblefee_v =
92  is_compatible_v<typename OtherFee::value_type>&&
93  std::is_same_v<UnitTag, typename OtherFee::unit_type>;
94 
95  template <class Other>
98 
99  template <class OtherFee>
102 
103 public:
104  TaggedFee() = default;
105  constexpr TaggedFee(TaggedFee const& other) = default;
106  constexpr TaggedFee&
107  operator=(TaggedFee const& other) = default;
108 
109  constexpr explicit TaggedFee(beast::Zero) : fee_(0)
110  {
111  }
112 
114  {
115  fee_ = 0;
116  return *this;
117  }
118 
119  constexpr explicit TaggedFee(value_type fee) : fee_(fee)
120  {
121  }
122 
123  TaggedFee&
125  {
126  fee_ = fee;
127  return *this;
128  }
129 
133  template <
134  class Other,
135  class = std::enable_if_t<
136  is_compatible_v<Other> &&
137  is_safetocasttovalue_v<value_type, Other>>>
140  {
141  }
142 
143  constexpr TaggedFee
144  operator*(value_type const& rhs) const
145  {
146  return TaggedFee{fee_ * rhs};
147  }
148 
149  friend constexpr TaggedFee
150  operator*(value_type lhs, TaggedFee const& rhs)
151  {
152  // multiplication is commutative
153  return rhs * lhs;
154  }
155 
156  constexpr value_type
157  operator/(TaggedFee const& rhs) const
158  {
159  return fee_ / rhs.fee_;
160  }
161 
162  TaggedFee&
163  operator+=(TaggedFee const& other)
164  {
165  fee_ += other.fee();
166  return *this;
167  }
168 
169  TaggedFee&
170  operator-=(TaggedFee const& other)
171  {
172  fee_ -= other.fee();
173  return *this;
174  }
175 
176  TaggedFee&
178  {
179  ++fee_;
180  return *this;
181  }
182 
183  TaggedFee&
185  {
186  --fee_;
187  return *this;
188  }
189 
190  TaggedFee&
191  operator*=(value_type const& rhs)
192  {
193  fee_ *= rhs;
194  return *this;
195  }
196 
197  TaggedFee&
198  operator/=(value_type const& rhs)
199  {
200  fee_ /= rhs;
201  return *this;
202  }
203 
204  template <class transparent = value_type>
206  operator%=(value_type const& rhs)
207  {
208  fee_ %= rhs;
209  return *this;
210  }
211 
212  TaggedFee
213  operator-() const
214  {
215  static_assert(
216  std::is_signed_v<T>, "- operator illegal on unsigned fee types");
217  return TaggedFee{-fee_};
218  }
219 
220  bool
221  operator==(TaggedFee const& other) const
222  {
223  return fee_ == other.fee_;
224  }
225 
226  template <class Other, class = enable_if_compatible_t<Other>>
227  bool
229  {
230  return fee_ == other.fee();
231  }
232 
233  bool
234  operator==(value_type other) const
235  {
236  return fee_ == other;
237  }
238 
239  template <class Other, class = enable_if_compatible_t<Other>>
240  bool
242  {
243  return !operator==(other);
244  }
245 
246  bool
247  operator<(TaggedFee const& other) const
248  {
249  return fee_ < other.fee_;
250  }
251 
253  explicit constexpr operator bool() const noexcept
254  {
255  return fee_ != 0;
256  }
257 
259  constexpr int
260  signum() const noexcept
261  {
262  return (fee_ < 0) ? -1 : (fee_ ? 1 : 0);
263  }
264 
266  constexpr value_type
267  fee() const
268  {
269  return fee_;
270  }
271 
272  template <class Other>
273  constexpr double
275  {
276  return static_cast<double>(fee_) / reference.fee();
277  }
278 
279  // `is_usable_unit_v` is checked to ensure that only values with
280  // known valid type tags can be converted to JSON. At the time
281  // of implementation, that includes all known tags, but more may
282  // be added in the future.
284  jsonClipped() const
285  {
286  if constexpr (std::is_integral_v<value_type>)
287  {
288  using jsontype = std::conditional_t<
289  std::is_signed_v<value_type>,
290  Json::Int,
291  Json::UInt>;
292 
293  constexpr auto min = std::numeric_limits<jsontype>::min();
294  constexpr auto max = std::numeric_limits<jsontype>::max();
295 
296  if (fee_ < min)
297  return min;
298  if (fee_ > max)
299  return max;
300  return static_cast<jsontype>(fee_);
301  }
302  else
303  {
304  return fee_;
305  }
306  }
307 
312  constexpr value_type
313  value() const
314  {
315  return fee_;
316  }
317 
318  friend std::istream&
320  {
321  s >> val.fee_;
322  return s;
323  }
324 };
325 
326 // Output Fees as just their numeric value.
327 template <class Char, class Traits, class UnitTag, class T>
330 {
331  return os << q.value();
332 }
333 
334 template <class UnitTag, class T>
337 {
338  return std::to_string(amount.fee());
339 }
340 
341 template <class Source, class = enable_if_unit_t<Source>>
342 constexpr bool can_muldiv_source_v =
343  std::is_convertible_v<typename Source::value_type, std::uint64_t>;
344 
345 template <class Dest, class = enable_if_unit_t<Dest>>
346 constexpr bool can_muldiv_dest_v =
347  can_muldiv_source_v<Dest>&& // Dest is also a source
348  std::is_convertible_v<std::uint64_t, typename Dest::value_type> &&
349  sizeof(typename Dest::value_type) >= sizeof(std::uint64_t);
350 
351 template <
352  class Source1,
353  class Source2,
356 constexpr bool can_muldiv_sources_v =
357  can_muldiv_source_v<Source1>&& can_muldiv_source_v<Source2>&& std::
358  is_same_v<typename Source1::unit_type, typename Source2::unit_type>;
359 
360 template <
361  class Source1,
362  class Source2,
363  class Dest,
366  class = enable_if_unit_t<Dest>>
367 constexpr bool can_muldiv_v =
368  can_muldiv_sources_v<Source1, Source2>&& can_muldiv_dest_v<Dest>;
369 // Source and Dest can be the same by default
370 
371 template <
372  class Source1,
373  class Source2,
374  class Dest,
377  class = enable_if_unit_t<Dest>>
378 constexpr bool can_muldiv_commute_v = can_muldiv_v<Source1, Source2, Dest> &&
379  !std::is_same_v<typename Source1::unit_type, typename Dest::unit_type>;
380 
381 template <class T>
384 
385 template <class T>
387 
388 template <class Source1, class Source2>
391 
392 template <class Source1, class Source2, class Dest>
393 using enable_muldiv_t =
395 
396 template <class Source1, class Source2, class Dest>
399 
400 template <class T>
402 scalar(T value)
403 {
404  return TaggedFee<unitlessTag, T>{value};
405 }
406 
407 template <
408  class Source1,
409  class Source2,
410  class Dest,
411  class = enable_muldiv_t<Source1, Source2, Dest>>
413 mulDivU(Source1 value, Dest mul, Source2 div)
414 {
415  // Fees can never be negative in any context.
416  if (value.value() < 0 || mul.value() < 0 || div.value() < 0)
417  {
418  // split the asserts so if one hits, the user can tell which
419  // without a debugger.
420  assert(value.value() >= 0);
421  assert(mul.value() >= 0);
422  assert(div.value() >= 0);
423  return {false, Dest{0}};
424  }
425 
426  using desttype = typename Dest::value_type;
427  constexpr auto max = std::numeric_limits<desttype>::max();
428 
429  // Shortcuts, since these happen a lot in the real world
430  if (value == div)
431  return {true, mul};
432  if (mul.value() == div.value())
433  {
434  if (value.value() > max)
435  return {false, Dest{max}};
436  return {true, Dest{static_cast<desttype>(value.value())}};
437  }
438 
439  using namespace boost::multiprecision;
440 
441  uint128_t product;
442  product = multiply(
443  product,
444  static_cast<std::uint64_t>(value.value()),
445  static_cast<std::uint64_t>(mul.value()));
446 
447  auto quotient = product / div.value();
448 
449  if (quotient > max)
450  return {false, Dest{max}};
451 
452  return {true, Dest{static_cast<desttype>(quotient)}};
453 }
454 
455 } // namespace feeunit
456 
457 template <class T>
461 
462 template <
463  class Source1,
464  class Source2,
465  class Dest,
468 mulDiv(Source1 value, Dest mul, Source2 div)
469 {
470  return feeunit::mulDivU(value, mul, div);
471 }
472 
473 template <
474  class Source1,
475  class Source2,
476  class Dest,
477  class = feeunit::enable_muldiv_commute_t<Source1, Source2, Dest>>
479 mulDiv(Dest value, Source1 mul, Source2 div)
480 {
481  // Multiplication is commutative
482  return feeunit::mulDivU(mul, value, div);
483 }
484 
485 template <class Dest, class = feeunit::enable_muldiv_dest_t<Dest>>
487 mulDiv(std::uint64_t value, Dest mul, std::uint64_t div)
488 {
489  // Give the scalars a non-tag so the
490  // unit-handling version gets called.
491  return feeunit::mulDivU(feeunit::scalar(value), mul, feeunit::scalar(div));
492 }
493 
494 template <class Dest, class = feeunit::enable_muldiv_dest_t<Dest>>
496 mulDiv(Dest value, std::uint64_t mul, std::uint64_t div)
497 {
498  // Multiplication is commutative
499  return mulDiv(mul, value, div);
500 }
501 
502 template <
503  class Source1,
504  class Source2,
505  class = feeunit::enable_muldiv_sources_t<Source1, Source2>>
507 mulDiv(Source1 value, std::uint64_t mul, Source2 div)
508 {
509  // Give the scalars a dimensionless unit so the
510  // unit-handling version gets called.
511  auto unitresult = feeunit::mulDivU(value, feeunit::scalar(mul), div);
512  return {unitresult.first, unitresult.second.value()};
513 }
514 
515 template <
516  class Source1,
517  class Source2,
518  class = feeunit::enable_muldiv_sources_t<Source1, Source2>>
520 mulDiv(std::uint64_t value, Source1 mul, Source2 div)
521 {
522  // Multiplication is commutative
523  return mulDiv(mul, value, div);
524 }
525 
526 template <class Dest, class Src>
527 constexpr std::enable_if_t<
528  std::is_same_v<typename Dest::unit_type, typename Src::unit_type> &&
529  std::is_integral_v<typename Dest::value_type> &&
530  std::is_integral_v<typename Src::value_type>,
531  Dest>
532 safe_cast(Src s) noexcept
533 {
534  // Dest may not have an explicit value constructor
535  return Dest{safe_cast<typename Dest::value_type>(s.value())};
536 }
537 
538 template <class Dest, class Src>
539 constexpr std::enable_if_t<
540  std::is_same_v<typename Dest::unit_type, typename Src::unit_type> &&
541  std::is_integral_v<typename Dest::value_type> &&
542  std::is_integral_v<typename Src::value_type>,
543  Dest>
544 unsafe_cast(Src s) noexcept
545 {
546  // Dest may not have an explicit value constructor
547  return Dest{unsafe_cast<typename Dest::value_type>(s.value())};
548 }
549 
550 } // namespace ripple
551 
552 #endif // BASICS_FEES_H_INCLUDED
ripple::feeunit::TaggedFee::operator-
TaggedFee operator-() const
Definition: FeeUnits.h:213
ripple::feeunit::TaggedFee::TaggedFee
constexpr TaggedFee(value_type fee)
Definition: FeeUnits.h:119
ripple::feeunit::TaggedFee::operator<
bool operator<(TaggedFee const &other) const
Definition: FeeUnits.h:247
sstream
ripple::feeunit::TaggedFee::operator/
constexpr value_type operator/(TaggedFee const &rhs) const
Definition: FeeUnits.h:157
ripple::feeunit::TaggedFee::operator=
constexpr TaggedFee & operator=(beast::Zero)
Definition: FeeUnits.h:113
std::string
STL class.
ripple::feeunit::TaggedFee::operator=
TaggedFee & operator=(value_type fee)
Definition: FeeUnits.h:124
utility
std::pair
iosfwd
ripple::feeunit::is_usable_unit_v
constexpr bool is_usable_unit_v
is_usable_unit_v is checked to ensure that only values with known valid type tags can be used (someti...
Definition: FeeUnits.h:63
Json::UInt
unsigned int UInt
Definition: json_forwards.h:27
ripple::feeunit::TaggedFee::operator*
constexpr friend TaggedFee operator*(value_type lhs, TaggedFee const &rhs)
Definition: FeeUnits.h:150
ripple::feeunit::TaggedFee::operator%=
std::enable_if_t< std::is_integral_v< transparent >, TaggedFee & > operator%=(value_type const &rhs)
Definition: FeeUnits.h:206
ripple::unsafe_cast
constexpr std::enable_if_t< std::is_same_v< typename Dest::unit_type, typename Src::unit_type > &&std::is_integral_v< typename Dest::value_type > &&std::is_integral_v< typename Src::value_type >, Dest > unsafe_cast(Src s) noexcept
Definition: FeeUnits.h:544
ripple::feeunit::TaggedFee::operator/=
TaggedFee & operator/=(value_type const &rhs)
Definition: FeeUnits.h:198
ripple::feeunit::TaggedFee< std::uint64_t >::enable_if_compatiblefee_t
typename std::enable_if_t< is_compatiblefee_v< OtherFee > > enable_if_compatiblefee_t
Definition: FeeUnits.h:101
ripple::feeunit::TaggedFee::operator--
TaggedFee & operator--()
Definition: FeeUnits.h:184
ripple::feeunit::TaggedFee::jsonClipped
std::enable_if_t< is_usable_unit_v< TaggedFee >, Json::Value > jsonClipped() const
Definition: FeeUnits.h:284
ripple::feeunit::TaggedFee::is_compatible_v
static constexpr bool is_compatible_v
Definition: FeeUnits.h:86
ripple::feeunit::enable_muldiv_commute_t
typename std::enable_if_t< can_muldiv_commute_v< Source1, Source2, Dest > > enable_muldiv_commute_t
Definition: FeeUnits.h:398
ripple::feeunit::TaggedFee::is_compatiblefee_v
static constexpr bool is_compatiblefee_v
Definition: FeeUnits.h:91
ripple::feeunit::TaggedFee::operator==
bool operator==(TaggedFee const &other) const
Definition: FeeUnits.h:221
ripple::feeunit::TaggedFee::fee
constexpr value_type fee() const
Returns the number of drops.
Definition: FeeUnits.h:267
ripple::feeunit::TaggedFee::TaggedFee
constexpr TaggedFee(beast::Zero)
Definition: FeeUnits.h:109
ripple::feeunit::TaggedFee::operator=
constexpr TaggedFee & operator=(TaggedFee const &other)=default
cmath
ripple::feeunit::TaggedFee< std::uint64_t >::enable_if_compatible_t
typename std::enable_if_t< is_compatible_v< Other > > enable_if_compatible_t
Definition: FeeUnits.h:97
ripple::feeunit::enable_muldiv_dest_t
typename std::enable_if_t< can_muldiv_dest_v< T > > enable_muldiv_dest_t
Definition: FeeUnits.h:386
ripple::feeunit::TaggedFee::TaggedFee
constexpr TaggedFee(TaggedFee< unit_type, Other > const &fee)
Instances with the same unit, and a type that is "safe" to covert to this one can be converted implic...
Definition: FeeUnits.h:138
ripple::feeunit::TaggedFee::operator++
TaggedFee & operator++()
Definition: FeeUnits.h:177
ripple::feeunit::can_muldiv_sources_v
constexpr bool can_muldiv_sources_v
Definition: FeeUnits.h:356
ripple::feeunit::enable_muldiv_sources_t
typename std::enable_if_t< can_muldiv_sources_v< Source1, Source2 > > enable_muldiv_sources_t
Definition: FeeUnits.h:390
ripple::feeunit::TaggedFee::TaggedFee
TaggedFee()=default
ripple::feeunit::TaggedFee::operator*
constexpr TaggedFee operator*(value_type const &rhs) const
Definition: FeeUnits.h:144
std::enable_if_t
ripple::feeunit::to_string
std::string to_string(TaggedFee< UnitTag, T > const &amount)
Definition: FeeUnits.h:336
std::basic_ostream
STL class.
ripple::safe_cast
constexpr std::enable_if_t< std::is_same_v< typename Dest::unit_type, typename Src::unit_type > &&std::is_integral_v< typename Dest::value_type > &&std::is_integral_v< typename Src::value_type >, Dest > safe_cast(Src s) noexcept
Definition: FeeUnits.h:532
ripple::feeunit::enable_muldiv_source_t
typename std::enable_if_t< can_muldiv_source_v< T > > enable_muldiv_source_t
Definition: FeeUnits.h:383
std::to_string
T to_string(T... args)
beast::Zero
Zero allows classes to offer efficient comparisons to zero.
Definition: Zero.h:42
ripple::feeunit::scalar
TaggedFee< unitlessTag, T > scalar(T value)
Definition: FeeUnits.h:402
ripple::feeunit::mulDivU
std::pair< bool, Dest > mulDivU(Source1 value, Dest mul, Source2 div)
Definition: FeeUnits.h:413
std::uint64_t
ripple::feeunit::TaggedFee
Definition: FeeUnits.h:70
Json::Int
int Int
Definition: json_forwards.h:26
ripple::feeunit::can_muldiv_commute_v
constexpr bool can_muldiv_commute_v
Definition: FeeUnits.h:378
ripple::multiply
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:47
std::numeric_limits::min
T min(T... args)
ripple::feeunit::TaggedFee::operator>>
friend std::istream & operator>>(std::istream &s, TaggedFee &val)
Definition: FeeUnits.h:319
ripple::feeunit::TaggedFee::operator+=
TaggedFee & operator+=(TaggedFee const &other)
Definition: FeeUnits.h:163
ripple::feeunit::TaggedFee::fee_
value_type fee_
Definition: FeeUnits.h:82
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::feeunit::TaggedFee::decimalFromReference
constexpr double decimalFromReference(TaggedFee< unit_type, Other > reference) const
Definition: FeeUnits.h:274
ripple::feeunit::enable_if_unit_t
typename std::enable_if_t< std::is_class_v< T > &&std::is_object_v< typename T::unit_type > &&std::is_object_v< typename T::value_type > > enable_if_unit_t
Definition: FeeUnits.h:53
ripple::feeunit::operator<<
std::basic_ostream< Char, Traits > & operator<<(std::basic_ostream< Char, Traits > &os, const TaggedFee< UnitTag, T > &q)
Definition: FeeUnits.h:329
ripple::feeunit::enable_muldiv_t
typename std::enable_if_t< can_muldiv_v< Source1, Source2, Dest > > enable_muldiv_t
Definition: FeeUnits.h:394
ripple::feeunit::TaggedFee::operator!=
bool operator!=(TaggedFee< unit_type, Other > const &other) const
Definition: FeeUnits.h:241
limits
ripple::feeunit::TaggedFee::value
constexpr value_type value() const
Returns the underlying value.
Definition: FeeUnits.h:313
ripple::feeunit::TaggedFee::operator==
bool operator==(value_type other) const
Definition: FeeUnits.h:234
cassert
ripple::feeunit::TaggedFee::operator*=
TaggedFee & operator*=(value_type const &rhs)
Definition: FeeUnits.h:191
ripple::feeunit::can_muldiv_source_v
constexpr bool can_muldiv_source_v
Definition: FeeUnits.h:342
ripple::feeunit::TaggedFee< std::uint64_t >::value_type
T value_type
Definition: FeeUnits.h:79
ripple::mulDiv
std::pair< bool, Dest > mulDiv(Source1 value, Dest mul, Source2 div)
Definition: FeeUnits.h:468
ios
std::conditional_t
std::numeric_limits::max
T max(T... args)
std::istream
STL class.
ripple::feeunit::TaggedFee::signum
constexpr int signum() const noexcept
Return the sign of the amount.
Definition: FeeUnits.h:260
ripple::feeunit::can_muldiv_v
constexpr bool can_muldiv_v
Definition: FeeUnits.h:367
ripple::feeunit::can_muldiv_dest_v
constexpr bool can_muldiv_dest_v
Definition: FeeUnits.h:346
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::feeunit::TaggedFee::operator==
bool operator==(TaggedFee< unit_type, Other > const &other) const
Definition: FeeUnits.h:228
ripple::feeunit::TaggedFee::operator-=
TaggedFee & operator-=(TaggedFee const &other)
Definition: FeeUnits.h:170
string