xrpld
Loading...
Searching...
No Matches
b58_utils.h
1#pragma once
2
3#include <xrpl/basics/contract.h>
4#include <xrpl/beast/utility/instrumentation.h>
5#include <xrpl/protocol/detail/token_errors.h>
6
7#include <boost/outcome.hpp>
8#include <boost/outcome/result.hpp>
9
10#include <span>
11#include <system_error>
12#include <tuple>
13
14namespace xrpl {
15
16template <class T>
17using Result = boost::outcome_v2::result<T, std::error_code>;
18
19#ifndef _MSC_VER
20
21namespace b58_fast::detail {
22
23// This optimizes to what hand written asm would do (single divide)
26{
27 return {a / b, a % b};
28}
29
30// This optimizes to what hand written asm would do (single multiply)
32carryingMul(std::uint64_t a, std::uint64_t b, std::uint64_t carry)
33{
34 unsigned __int128 const x = a;
35 unsigned __int128 const y = b;
36 unsigned __int128 const c = (x * y) + carry;
37 return {c & 0xffff'ffff'ffff'ffff, c >> 64};
38}
39
41carryingAdd(std::uint64_t a, std::uint64_t b)
42{
43 unsigned __int128 const x = a;
44 unsigned __int128 const y = b;
45 unsigned __int128 const c = x + y;
46 return {c & 0xffff'ffff'ffff'ffff, c >> 64};
47}
48
49// Add a u64 to a "big uint" value inplace.
50// The bigint value is stored with the smallest coefficients first
51// (i.e a[0] is the 2^0 coefficient, a[n] is the 2^(64*n) coefficient)
52// panics if overflows (this is a specialized adder for b58 decoding.
53// it should never overflow).
54[[nodiscard]] inline TokenCodecErrc
55inplaceBigintAdd(std::span<std::uint64_t> a, std::uint64_t b)
56{
57 if (a.size() <= 1)
58 {
60 }
61
62 std::uint64_t carry = 0;
63 std::tie(a[0], carry) = carryingAdd(a[0], b);
64
65 for (auto& v : a.subspan(1))
66 {
67 if (carry == 0u)
68 {
70 }
71 std::tie(v, carry) = carryingAdd(v, 1);
72 }
73 if (carry != 0u)
74 {
76 }
78}
79
80[[nodiscard]] inline TokenCodecErrc
81inplaceBigintMul(std::span<std::uint64_t> a, std::uint64_t b)
82{
83 if (a.empty())
84 {
86 }
87
88 auto const lastIndex = a.size() - 1;
89 if (a[lastIndex] != 0)
90 {
92 }
93
94 std::uint64_t carry = 0;
95 for (auto& coeff : a.subspan(0, lastIndex))
96 {
97 std::tie(coeff, carry) = carryingMul(coeff, b, carry);
98 }
99 a[lastIndex] = carry;
101}
102
103// divide a "big uint" value inplace and return the mod
104// numerator is stored so smallest coefficients come first
105[[nodiscard]] inline std::uint64_t
106inplaceBigintDivRem(std::span<uint64_t> numerator, std::uint64_t divisor)
107{
108 if (numerator.empty())
109 {
110 // should never happen, but if it does then it seems natural to define
111 // the a null set of numbers to be zero, so the remainder is also zero.
112 // LCOV_EXCL_START
113 UNREACHABLE(
114 "xrpl::b58_fast::detail::inplaceBigintDivRem : empty "
115 "numerator");
116 return 0;
117 // LCOV_EXCL_STOP
118 }
119
120 auto toU128 = [](std::uint64_t high, std::uint64_t low) -> unsigned __int128 {
121 unsigned __int128 const high128 = high;
122 unsigned __int128 const low128 = low;
123 return ((high128 << 64) | low128);
124 };
125 auto divRe64 = [](unsigned __int128 num,
127 unsigned __int128 const denom128 = denom;
128 unsigned __int128 const d = num / denom128;
129 unsigned __int128 const r = num - (denom128 * d);
130 XRPL_ASSERT(
131 d >> 64 == 0,
132 "xrpl::b58_fast::detail::inplaceBigintDivRem::divRe64 : "
133 "valid division result");
134 XRPL_ASSERT(
135 r >> 64 == 0,
136 "xrpl::b58_fast::detail::inplaceBigintDivRem::divRe64 : "
137 "valid remainder");
138 return {static_cast<std::uint64_t>(d), static_cast<std::uint64_t>(r)};
139 };
140
141 std::uint64_t prevRem = 0;
142 int const lastIndex = numerator.size() - 1;
143 std::tie(numerator[lastIndex], prevRem) = divRem(numerator[lastIndex], divisor);
144 for (int i = lastIndex - 1; i >= 0; --i)
145 {
146 unsigned __int128 const curNum = toU128(prevRem, numerator[i]);
147 std::tie(numerator[i], prevRem) = divRe64(curNum, divisor);
148 }
149 return prevRem;
150}
151
152// convert from base 58^10 to base 58
153// put largest coeffs first
154// the `_be` suffix stands for "big endian"
155[[nodiscard]] inline std::array<std::uint8_t, 10>
156b5810ToB58Be(std::uint64_t input)
157{
158 [[maybe_unused]] static constexpr std::uint64_t kB5810 = 430804206899405824; // 58^10;
159 XRPL_ASSERT(input < kB5810, "xrpl::b58_fast::detail::b5810ToB58Be : valid input");
160 static constexpr std::size_t kResultSize = 10;
162 int i = 0;
163 while (input > 0)
164 {
165 std::uint64_t rem = 0;
166 std::tie(input, rem) = divRem(input, 58);
167 result[kResultSize - 1 - i] = rem;
168 i += 1;
169 }
170
171 return result;
172}
173} // namespace b58_fast::detail
174
175#endif
176
177} // namespace xrpl
T empty(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
boost::outcome_v2::result< T, std::error_code > Result
Definition b58_utils.h:17
TokenCodecErrc
Definition token_errors.h:6
T size(T... args)
T tie(T... args)