xrpld
Loading...
Searching...
No Matches
base_uint.cpp
1#include <xrpl/basics/base_uint.h>
2
3#include <xrpl/basics/Blob.h>
4#include <xrpl/basics/hardened_hash.h>
5#include <xrpl/beast/utility/Zero.h>
6
7#include <boost/endian/detail/order.hpp>
8
9#include <gmock/gmock.h>
10#include <gtest/gtest.h>
11
12#include <array>
13#include <cassert>
14#include <complex>
15#include <cstddef>
16#include <cstdint>
17#include <iterator>
18#include <stdexcept>
19#include <string>
20#include <string_view>
21#include <type_traits>
22#include <unordered_set>
23#include <utility>
24#include <vector>
25
26namespace xrpl::test {
27
28// a non-hashing Hasher that just copies the bytes.
29// Used to test hash_append in base_uint
30template <std::size_t Bits>
31struct Nonhash
32{
33 static constexpr auto const kEndian = boost::endian::order::big;
34 static constexpr std::size_t kWidth = Bits / 8;
35
37
38 Nonhash() = default;
39
40 void
41 operator()(void const* key, std::size_t len) noexcept
42 {
43 assert(len == kWidth);
44 memcpy(data.data(), key, len);
45 }
46
47 explicit
48 operator std::size_t() noexcept
49 {
50 return kWidth;
51 }
52};
53
54struct BaseUintTest : public ::testing::Test
55{
59
60 static void
62 {
64
65 {
66 static constexpr auto kTestArgs = std::to_array<HexPair>({
67 {"0000000000000000", "0000000000000001"},
68 {"0000000000000000", "ffffffffffffffff"},
69 {"1234567812345678", "2345678923456789"},
70 {"8000000000000000", "8000000000000001"},
71 {"aaaaaaaaaaaaaaa9", "aaaaaaaaaaaaaaaa"},
72 {"fffffffffffffffe", "ffffffffffffffff"},
73 });
74
75 for (auto const& [smallerText, largerText] : kTestArgs)
76 {
77 xrpl::BaseUInt<64> const smaller{smallerText}, larger{largerText};
78 // For code readability, we want to use general boolean
79 // expectations instead of specific EXPECT_LT etc.
80 EXPECT_TRUE(smaller < larger);
81 EXPECT_TRUE(smaller <= larger);
82 EXPECT_TRUE(smaller != larger);
83 EXPECT_FALSE(smaller == larger);
84 EXPECT_FALSE(smaller > larger);
85 EXPECT_FALSE(smaller >= larger);
86 EXPECT_FALSE(larger < smaller);
87 EXPECT_FALSE(larger <= smaller);
88 EXPECT_TRUE(larger != smaller);
89 EXPECT_FALSE(larger == smaller);
90 EXPECT_TRUE(larger > smaller);
91 EXPECT_TRUE(larger >= smaller);
92 EXPECT_TRUE(smaller == smaller);
93 EXPECT_TRUE(larger == larger);
94 }
95 }
96
97 {
98 static constexpr auto kTestArgs = std::to_array<HexPair>({
99 {"000000000000000000000000", "000000000000000000000001"},
100 {"000000000000000000000000", "ffffffffffffffffffffffff"},
101 {"0123456789ab0123456789ab", "123456789abc123456789abc"},
102 {"555555555555555555555555", "55555555555a555555555555"},
103 {"aaaaaaaaaaaaaaa9aaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaa"},
104 {"fffffffffffffffffffffffe", "ffffffffffffffffffffffff"},
105 });
106
107 for (auto const& [smallerText, largerText] : kTestArgs)
108 {
109 xrpl::BaseUInt<96> const smaller{smallerText}, larger{largerText};
110 EXPECT_TRUE(smaller < larger);
111 EXPECT_TRUE(smaller <= larger);
112 EXPECT_TRUE(smaller != larger);
113 EXPECT_FALSE(smaller == larger);
114 EXPECT_FALSE(smaller > larger);
115 EXPECT_FALSE(smaller >= larger);
116 EXPECT_FALSE(larger < smaller);
117 EXPECT_FALSE(larger <= smaller);
118 EXPECT_TRUE(larger != smaller);
119 EXPECT_FALSE(larger == smaller);
120 EXPECT_TRUE(larger > smaller);
121 EXPECT_TRUE(larger >= smaller);
122 EXPECT_TRUE(smaller == smaller);
123 EXPECT_TRUE(larger == larger);
124 }
125 }
126 }
127};
128
130
131TEST_F(BaseUintDeathTest, fromRaw_size_mismatch)
132{
133 // ENABLE_VOIDSTAR is a debug build, but does not crash on failed asserts. Rather than twist
134 // these tests into knots to make them work, just skip them.
135#ifdef ENABLE_VOIDSTAR
136 GTEST_SKIP() << "ENABLE_VOIDSTAR is a debug build, but does not crash on failed asserts.";
137#else
138 auto smallConstruct = [] {
139 // Container smaller than the base_uint (8 bytes vs 12 bytes for
140 // test96). Only the first 8 bytes are copied; the remaining 4 bytes
141 // stay zero.
142 Blob const tooSmall{1, 2, 3, 4, 5, 6, 7, 8};
143 BaseUInt96 const result = BaseUInt96::fromRaw(tooSmall);
144 auto const resultText = to_string(result);
145 EXPECT_EQ(resultText, "010203040506070800000000") << resultText;
146 };
147 EXPECT_DEBUG_DEATH(smallConstruct(), "input size match");
148
149 auto largeConstruct = [] {
150 // Container larger than the base_uint (16 bytes vs 12 bytes for
151 // test96). Only the first 12 bytes are copied; the extra bytes are
152 // ignored.
153 Blob const tooBig{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
154 BaseUInt96 const result = BaseUInt96::fromRaw(tooBig);
155 auto const resultText = to_string(result);
156 EXPECT_EQ(resultText, "0102030405060708090A0B0C") << resultText;
157 };
158 EXPECT_DEBUG_DEATH(largeConstruct(), "input size match");
159
160 auto smallCopy = [] {
161 // Container smaller than the base_uint (8 bytes vs 12 bytes for
162 // test96). Only the first 8 bytes are copied; the remaining 4 bytes
163 // stay zero.
164 Blob const tooSmall{1, 2, 3, 4, 5, 6, 7, 8};
165 BaseUInt96 result{};
166 --result;
167 {
168 auto const originalText = to_string(result);
169 EXPECT_EQ(originalText, "FFFFFFFFFFFFFFFFFFFFFFFF") << originalText;
170 }
171 result = tooSmall;
172 auto const resultText = to_string(result);
173 EXPECT_EQ(resultText, "010203040506070800000000") << resultText;
174 };
175 EXPECT_DEBUG_DEATH(smallCopy(), "input size match");
176
177 auto const largeCopy = [] {
178 // Container larger than the base_uint (16 bytes vs 12 bytes for
179 // test96). Only the first 12 bytes are copied; the extra bytes are
180 // ignored.
181 Blob const tooBig{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
182 BaseUInt96 result{};
183 --result;
184 {
185 auto const originalText = to_string(result);
186 EXPECT_EQ(originalText, "FFFFFFFFFFFFFFFFFFFFFFFF") << originalText;
187 }
188 result = tooBig;
189 auto const resultText = to_string(result);
190 EXPECT_EQ(resultText, "0102030405060708090A0B0C") << resultText;
191 };
192 EXPECT_DEBUG_DEATH(largeCopy(), "input size match");
193#endif
194}
195
197{
200
201 testComparisons();
202
203 // used to verify set insertion (hashing required)
205
206 Blob const raw{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
207 EXPECT_EQ(BaseUInt96::kBytes, raw.size());
208
209 BaseUInt96 ascending = BaseUInt96::fromRaw(raw);
210 uset.insert(ascending);
211 EXPECT_EQ(raw.size(), ascending.size());
212 EXPECT_EQ(to_string(ascending), "0102030405060708090A0B0C");
213 EXPECT_EQ(toShortString(ascending), "01020304...");
214 EXPECT_EQ(*ascending.data(), 1);
215 EXPECT_EQ(ascending.signum(), 1);
216 EXPECT_FALSE(!ascending);
217 EXPECT_FALSE(ascending.isZero());
218 EXPECT_TRUE(ascending.isNonZero());
219 unsigned char expectedByte = 0;
220 for (auto& byte : ascending)
221 EXPECT_EQ(byte, ++expectedByte);
222
223 // Test hash_append by "hashing" with a no-op hasher (hasher)
224 // and then extracting the bytes that were written during hashing
225 // back into another base_uint (rehashed) for comparison with the original
226 Nonhash<96> hasher{};
227 hash_append(hasher, ascending);
228 BaseUInt96 const rehashed =
229 BaseUInt96::fromRaw(std::vector<std::uint8_t>(hasher.data.begin(), hasher.data.end()));
230 EXPECT_EQ(rehashed, ascending);
231
232 BaseUInt96 complement{~ascending};
233 uset.insert(complement);
234 EXPECT_EQ(to_string(complement), "FEFDFCFBFAF9F8F7F6F5F4F3");
235 EXPECT_EQ(toShortString(complement), "FEFDFCFB...");
236 EXPECT_EQ(*complement.data(), 0xfe);
237 EXPECT_EQ(complement.signum(), 1);
238 EXPECT_FALSE(!complement);
239 EXPECT_FALSE(complement.isZero());
240 EXPECT_TRUE(complement.isNonZero());
241
242 expectedByte = 0xff;
243 for (auto& byte : complement)
244 EXPECT_EQ(byte, --expectedByte);
245
246 EXPECT_LT(ascending, complement);
247 EXPECT_GT(complement, ascending);
248
249 complement = ascending;
250 EXPECT_EQ(complement, ascending);
251
252 BaseUInt96 zero{beast::kZero};
253 uset.insert(zero);
254 EXPECT_EQ(to_string(zero), "000000000000000000000000");
255 EXPECT_EQ(toShortString(zero), "00000000...");
256 EXPECT_EQ(*zero.data(), 0);
257 EXPECT_EQ(*zero.begin(), 0);
258 EXPECT_EQ(*std::prev(zero.end(), 1), 0);
259 EXPECT_EQ(zero.signum(), 0);
260 EXPECT_TRUE(!zero);
261 EXPECT_TRUE(zero.isZero());
262 EXPECT_FALSE(zero.isNonZero());
263 for (auto& byte : zero)
264 EXPECT_EQ(byte, 0);
265
266 {
267 // There are several ways to create a zero. beast::kZero is tested above. Test some
268 // others.
269 BaseUInt96 const defaultZero;
270 EXPECT_EQ(defaultZero, zero) << to_string(defaultZero);
271
272 BaseUInt96 const bracedZero{};
273 EXPECT_EQ(bracedZero, zero) << to_string(bracedZero);
274
275 BaseUInt96 const zeroFromUInt{0u};
276 EXPECT_EQ(zeroFromUInt, zero) << to_string(zeroFromUInt);
277 }
278
279 BaseUInt96 counter{zero};
280 counter++;
281 EXPECT_EQ(counter, BaseUInt96(1));
282 counter--;
283 EXPECT_EQ(counter, beast::kZero);
284 EXPECT_EQ(counter, zero);
285 counter--;
286 EXPECT_EQ(to_string(counter), "FFFFFFFFFFFFFFFFFFFFFFFF");
287 EXPECT_EQ(toShortString(counter), "FFFFFFFF...");
288 counter = beast::kZero;
289 EXPECT_EQ(counter, zero);
290
291 BaseUInt96 zeroPlusOne{zero};
292 zeroPlusOne++;
293 BaseUInt96 zeroMinusOne{zero};
294 zeroMinusOne--;
295 BaseUInt96 const xored{zeroMinusOne ^ zeroPlusOne};
296 uset.insert(xored);
297 EXPECT_EQ(to_string(xored), "FFFFFFFFFFFFFFFFFFFFFFFE") << to_string(xored);
298 EXPECT_EQ(toShortString(xored), "FFFFFFFF...") << toShortString(xored);
299
300 EXPECT_EQ(uset.size(), 4);
301
302 BaseUInt96 parsed;
303 EXPECT_TRUE(parsed.parseHex(to_string(ascending)));
304 EXPECT_EQ(parsed, ascending);
305 parsed = zero;
306
307 // fails with extra char
308 EXPECT_FALSE(parsed.parseHex("A" + to_string(ascending)));
309 parsed = zero;
310
311 // fails with extra char at end
312 EXPECT_FALSE(parsed.parseHex(to_string(ascending) + "A"));
313
314 // fails with a non-hex character at some point in the string:
315 parsed = zero;
316
317 for (std::size_t i = 0; i != 24; ++i)
318 {
319 std::string xored = to_string(zero);
320 xored[i] = ('G' + (i % 10));
321 EXPECT_FALSE(parsed.parseHex(xored));
322 }
323
324 // Walking 1s:
325 for (std::size_t i = 0; i != 24; ++i)
326 {
327 std::string s1 = "000000000000000000000000";
328 s1[i] = '1';
329
330 EXPECT_TRUE(parsed.parseHex(s1));
331 EXPECT_EQ(to_string(parsed), s1);
332 }
333
334 // Walking 0s:
335 for (std::size_t i = 0; i != 24; ++i)
336 {
337 std::string s1 = "111111111111111111111111";
338 s1[i] = '0';
339
340 EXPECT_TRUE(parsed.parseHex(s1));
341 EXPECT_EQ(to_string(parsed), s1);
342 }
343
344 // Constexpr constructors
345 {
346 static_assert(BaseUInt96{}.signum() == 0);
347 static_assert(BaseUInt96("0").signum() == 0);
348 static_assert(BaseUInt96("000000000000000000000000").signum() == 0);
349 static_assert(BaseUInt96("000000000000000000000001").signum() == 1);
350 static_assert(BaseUInt96("800000000000000000000000").signum() == 1);
351
352 // Using the constexpr constructor in a non-constexpr context
353 // with an error in the parsing throws an exception.
354 {
355 // Invalid length for string. The vector keeps this out of a constant
356 // expression, so the constructor throws instead of failing to compile.
357 auto tooShort = [] {
358 std::vector<char> const str(23, '7');
359 std::string_view const sView(str.data(), str.size());
360 [[maybe_unused]] BaseUInt96 const t96(sView);
361 };
362 EXPECT_THAT(
363 tooShort,
364 ::testing::ThrowsMessage<std::invalid_argument>("invalid length for hex string"));
365 }
366 {
367 // Invalid character in string.
368 auto badCharacter = [] {
369 std::vector<char> str(23, '7');
370 str.push_back('G');
371 std::string_view const sView(str.data(), str.size());
372 [[maybe_unused]] BaseUInt96 const t96(sView);
373 };
374 EXPECT_THAT(
375 badCharacter, ::testing::ThrowsMessage<std::range_error>("invalid hex character"));
376 }
377
378 // Verify that constexpr base_uints interpret a string the same
379 // way parseHex() does.
380 struct StrBaseUInt
381 {
382 char const* const str;
383 BaseUInt96 tst;
384
385 constexpr StrBaseUInt(char const* s) : str(s), tst(s)
386 {
387 }
388 };
389 constexpr auto kTestCases = std::to_array<StrBaseUInt>({
390 "000000000000000000000000",
391 "000000000000000000000001",
392 "fedcba9876543210ABCDEF91",
393 "19FEDCBA0123456789abcdef",
394 "800000000000000000000000",
395 "fFfFfFfFfFfFfFfFfFfFfFfF",
396 });
397
398 for (StrBaseUInt const& expectedByte : kTestCases)
399 {
400 BaseUInt96 t96;
401 EXPECT_TRUE(t96.parseHex(expectedByte.str));
402 EXPECT_EQ(t96, expectedByte.tst);
403 }
404 }
405}
406
407} // namespace xrpl::test
T begin(T... args)
Integers of any length that is a multiple of 32-bits.
Definition base_uint.h:82
T data(T... args)
T end(T... args)
T insert(T... args)
T is_assignable_v
T is_constructible_v
T is_copy_assignable_v
T is_copy_constructible_v
constexpr Zero kZero
Definition Zero.h:30
void hash_append(Hasher &h, Account const &v) noexcept
BaseUintTest BaseUintDeathTest
TEST_F(BaseUintDeathTest, fromRaw_size_mismatch)
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
std::string toShortString(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:658
std::vector< unsigned char > Blob
Storage for linear binary data.
Definition Blob.h:11
T prev(T... args)
T push_back(T... args)
T size(T... args)
static void testComparisons()
Definition base_uint.cpp:61
BaseUInt< 96 > BaseUInt96
Definition base_uint.cpp:56
void operator()(void const *key, std::size_t len) noexcept
Definition base_uint.cpp:41
std::array< std::uint8_t, kWidth > data
Definition base_uint.cpp:36
static constexpr std::size_t kWidth
Definition base_uint.cpp:34
static constexpr auto const kEndian
Definition base_uint.cpp:33