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