rippled
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
20namespace b58_fast {
21namespace detail {
22
23// This optimizes to what hand written asm would do (single divide)
25div_rem(std::uint64_t a, std::uint64_t b)
26{
27 return {a / b, a % b};
28}
29
30// This optimizes to what hand written asm would do (single multiply)
32carrying_mul(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
41carrying_add(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
55inplace_bigint_add(std::span<std::uint64_t> a, std::uint64_t b)
56{
57 if (a.size() <= 1)
58 {
59 return TokenCodecErrc::inputTooSmall;
60 }
61
62 std::uint64_t carry;
63 std::tie(a[0], carry) = carrying_add(a[0], b);
64
65 for (auto& v : a.subspan(1))
66 {
67 if (!carry)
68 {
69 return TokenCodecErrc::success;
70 }
71 std::tie(v, carry) = carrying_add(v, 1);
72 }
73 if (carry)
74 {
75 return TokenCodecErrc::overflowAdd;
76 }
77 return TokenCodecErrc::success;
78}
79
80[[nodiscard]] inline TokenCodecErrc
81inplace_bigint_mul(std::span<std::uint64_t> a, std::uint64_t b)
82{
83 if (a.empty())
84 {
85 return TokenCodecErrc::inputTooSmall;
86 }
87
88 auto const last_index = a.size() - 1;
89 if (a[last_index] != 0)
90 {
91 return TokenCodecErrc::inputTooLarge;
92 }
93
94 std::uint64_t carry = 0;
95 for (auto& coeff : a.subspan(0, last_index))
96 {
97 std::tie(coeff, carry) = carrying_mul(coeff, b, carry);
98 }
99 a[last_index] = carry;
100 return TokenCodecErrc::success;
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
106inplace_bigint_div_rem(std::span<uint64_t> numerator, std::uint64_t divisor)
107{
108 if (numerator.size() == 0)
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::inplace_bigint_div_rem : empty "
115 "numerator");
116 return 0;
117 // LCOV_EXCL_STOP
118 }
119
120 auto to_u128 = [](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 div_rem_64 = [](unsigned __int128 num, std::uint64_t denom) -> std::tuple<std::uint64_t, std::uint64_t> {
126 unsigned __int128 const denom128 = denom;
127 unsigned __int128 const d = num / denom128;
128 unsigned __int128 const r = num - (denom128 * d);
129 XRPL_ASSERT(
130 d >> 64 == 0,
131 "xrpl::b58_fast::detail::inplace_bigint_div_rem::div_rem_64 : "
132 "valid division result");
133 XRPL_ASSERT(
134 r >> 64 == 0,
135 "xrpl::b58_fast::detail::inplace_bigint_div_rem::div_rem_64 : "
136 "valid remainder");
137 return {static_cast<std::uint64_t>(d), static_cast<std::uint64_t>(r)};
138 };
139
140 std::uint64_t prev_rem = 0;
141 int const last_index = numerator.size() - 1;
142 std::tie(numerator[last_index], prev_rem) = div_rem(numerator[last_index], divisor);
143 for (int i = last_index - 1; i >= 0; --i)
144 {
145 unsigned __int128 const cur_num = to_u128(prev_rem, numerator[i]);
146 std::tie(numerator[i], prev_rem) = div_rem_64(cur_num, divisor);
147 }
148 return prev_rem;
149}
150
151// convert from base 58^10 to base 58
152// put largest coeffs first
153// the `_be` suffix stands for "big endian"
154[[nodiscard]] inline std::array<std::uint8_t, 10>
155b58_10_to_b58_be(std::uint64_t input)
156{
157 [[maybe_unused]] static constexpr std::uint64_t B_58_10 = 430804206899405824; // 58^10;
158 XRPL_ASSERT(input < B_58_10, "xrpl::b58_fast::detail::b58_10_to_b58_be : valid input");
159 constexpr std::size_t resultSize = 10;
161 int i = 0;
162 while (input > 0)
163 {
164 std::uint64_t rem;
165 std::tie(input, rem) = div_rem(input, 58);
166 result[resultSize - 1 - i] = rem;
167 i += 1;
168 }
169
170 return result;
171}
172} // namespace detail
173} // namespace b58_fast
174#endif
175
176} // namespace xrpl
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
TokenCodecErrc
Definition token_errors.h:6
boost::outcome_v2::result< T, std::error_code > Result
Definition b58_utils.h:17
T size(T... args)
T tie(T... args)