//------------------------------------------------------------------------------ /* This file is part of rippled: https://github.com/ripple/rippled Copyright (c) 2012-2016 Ripple Labs Inc. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ //============================================================================== #include #include #include #include #include #include #include namespace ripple { namespace test { // a non-hashing Hasher that just copies the bytes. // Used to test hash_append in base_uint template struct nonhash { static constexpr auto const endian = boost::endian::order::big; static constexpr std::size_t WIDTH = Bits / 8; std::array data_; nonhash() = default; void operator()(void const* key, std::size_t len) noexcept { assert(len == WIDTH); memcpy(data_.data(), key, len); } explicit operator std::size_t() noexcept { return WIDTH; } }; struct base_uint_test : beast::unit_test::suite { using test96 = base_uint<96>; static_assert(std::is_copy_constructible::value, ""); static_assert(std::is_copy_assignable::value, ""); void run() override { testcase("base_uint: general purpose tests"); static_assert( !std::is_constructible>::value, ""); static_assert( !std::is_assignable>::value, ""); // used to verify set insertion (hashing required) std::unordered_set> uset; Blob raw{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; BEAST_EXPECT(test96::bytes == raw.size()); test96 u{raw}; uset.insert(u); BEAST_EXPECT(raw.size() == u.size()); BEAST_EXPECT(to_string(u) == "0102030405060708090A0B0C"); BEAST_EXPECT(*u.data() == 1); BEAST_EXPECT(u.signum() == 1); BEAST_EXPECT(!!u); BEAST_EXPECT(!u.isZero()); BEAST_EXPECT(u.isNonZero()); unsigned char t = 0; for (auto& d : u) { BEAST_EXPECT(d == ++t); } // Test hash_append by "hashing" with a no-op hasher (h) // and then extracting the bytes that were written during hashing // back into another base_uint (w) for comparison with the original nonhash<96> h; hash_append(h, u); test96 w{std::vector(h.data_.begin(), h.data_.end())}; BEAST_EXPECT(w == u); test96 v{~u}; uset.insert(v); BEAST_EXPECT(to_string(v) == "FEFDFCFBFAF9F8F7F6F5F4F3"); BEAST_EXPECT(*v.data() == 0xfe); BEAST_EXPECT(v.signum() == 1); BEAST_EXPECT(!!v); BEAST_EXPECT(!v.isZero()); BEAST_EXPECT(v.isNonZero()); t = 0xff; for (auto& d : v) { BEAST_EXPECT(d == --t); } BEAST_EXPECT(compare(u, v) < 0); BEAST_EXPECT(compare(v, u) > 0); v = u; BEAST_EXPECT(v == u); test96 z{beast::zero}; uset.insert(z); BEAST_EXPECT(to_string(z) == "000000000000000000000000"); BEAST_EXPECT(*z.data() == 0); BEAST_EXPECT(*z.begin() == 0); BEAST_EXPECT(*std::prev(z.end(), 1) == 0); BEAST_EXPECT(z.signum() == 0); BEAST_EXPECT(!z); BEAST_EXPECT(z.isZero()); BEAST_EXPECT(!z.isNonZero()); for (auto& d : z) { BEAST_EXPECT(d == 0); } test96 n{z}; n++; BEAST_EXPECT(n == test96(1)); n--; BEAST_EXPECT(n == beast::zero); BEAST_EXPECT(n == z); n--; BEAST_EXPECT(to_string(n) == "FFFFFFFFFFFFFFFFFFFFFFFF"); n = beast::zero; BEAST_EXPECT(n == z); test96 zp1{z}; zp1++; test96 zm1{z}; zm1--; test96 x{zm1 ^ zp1}; uset.insert(x); BEAST_EXPECTS(to_string(x) == "FFFFFFFFFFFFFFFFFFFFFFFE", to_string(x)); BEAST_EXPECT(uset.size() == 4); test96 tmp; BEAST_EXPECT(tmp.parseHex(to_string(u))); BEAST_EXPECT(tmp == u); tmp = z; // fails with extra char BEAST_EXPECT(!tmp.parseHex("A" + to_string(u))); tmp = z; // fails with extra char at end BEAST_EXPECT(!tmp.parseHex(to_string(u) + "A")); // fails with a non-hex character at some point in the string: tmp = z; for (std::size_t i = 0; i != 24; ++i) { std::string x = to_string(z); x[i] = ('G' + (i % 10)); BEAST_EXPECT(!tmp.parseHex(x)); } // Walking 1s: for (std::size_t i = 0; i != 24; ++i) { std::string s1 = "000000000000000000000000"; s1[i] = '1'; BEAST_EXPECT(tmp.parseHex(s1)); BEAST_EXPECT(to_string(tmp) == s1); } // Walking 0s: for (std::size_t i = 0; i != 24; ++i) { std::string s1 = "111111111111111111111111"; s1[i] = '0'; BEAST_EXPECT(tmp.parseHex(s1)); BEAST_EXPECT(to_string(tmp) == s1); } // Constexpr constructors { static_assert(test96{}.signum() == 0); static_assert(test96("0").signum() == 0); static_assert(test96("000000000000000000000000").signum() == 0); static_assert(test96("000000000000000000000001").signum() == 1); static_assert(test96("800000000000000000000000").signum() == 1); // Everything within the #if should fail during compilation. #if 0 // Too few characters static_assert(test96("00000000000000000000000").signum() == 0); // Too many characters static_assert(test96("0000000000000000000000000").signum() == 0); // Non-hex characters static_assert(test96("00000000000000000000000 ").signum() == 1); static_assert(test96("00000000000000000000000/").signum() == 1); static_assert(test96("00000000000000000000000:").signum() == 1); static_assert(test96("00000000000000000000000@").signum() == 1); static_assert(test96("00000000000000000000000G").signum() == 1); static_assert(test96("00000000000000000000000`").signum() == 1); static_assert(test96("00000000000000000000000g").signum() == 1); static_assert(test96("00000000000000000000000~").signum() == 1); #endif // 0 // Using the constexpr constructor in a non-constexpr context // with an error in the parsing throws an exception. { // Invalid length for string. bool caught = false; try { // Try to prevent constant evaluation. std::vector str(23, '7'); std::string_view sView(str.data(), str.size()); [[maybe_unused]] test96 t96(sView); } catch (std::invalid_argument const& e) { BEAST_EXPECT( e.what() == std::string("invalid length for hex string")); caught = true; } BEAST_EXPECT(caught); } { // Invalid character in string. bool caught = false; try { // Try to prevent constant evaluation. std::vector str(23, '7'); str.push_back('G'); std::string_view sView(str.data(), str.size()); [[maybe_unused]] test96 t96(sView); } catch (std::range_error const& e) { BEAST_EXPECT( e.what() == std::string("invalid hex character")); caught = true; } BEAST_EXPECT(caught); } // Verify that constexpr base_uints interpret a string the same // way parseHex() does. struct StrBaseUint { char const* const str; test96 tst; constexpr StrBaseUint(char const* s) : str(s), tst(s) { } }; constexpr StrBaseUint testCases[] = { "000000000000000000000000", "000000000000000000000001", "fedcba9876543210ABCDEF91", "19FEDCBA0123456789abcdef", "800000000000000000000000", "fFfFfFfFfFfFfFfFfFfFfFfF"}; for (StrBaseUint const& t : testCases) { test96 t96; BEAST_EXPECT(t96.parseHex(t.str)); BEAST_EXPECT(t96 == t.tst); } } } }; BEAST_DEFINE_TESTSUITE(base_uint, ripple_basics, ripple); } // namespace test } // namespace ripple