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