#include #include #include #include #include #include #include #include namespace xrpl::test { struct Buffer_test : beast::unit_test::Suite { static bool sane(Buffer const& b) { if (b.empty()) return b.data() == nullptr; return b.data() != nullptr; } void run() override { std::uint8_t const data[] = {0xa8, 0xa1, 0x38, 0x45, 0x23, 0xec, 0xe4, 0x23, 0x71, 0x6d, 0x2a, 0x18, 0xb4, 0x70, 0xcb, 0xf5, 0xac, 0x2d, 0x89, 0x4d, 0x19, 0x9c, 0xf0, 0x2c, 0x15, 0xd1, 0xf9, 0x9b, 0x66, 0xd2, 0x30, 0xd3}; Buffer const b0; BEAST_EXPECT(sane(b0)); BEAST_EXPECT(b0.empty()); Buffer b1{0}; BEAST_EXPECT(sane(b1)); BEAST_EXPECT(b1.empty()); std::memcpy(b1.alloc(16), data, 16); BEAST_EXPECT(sane(b1)); BEAST_EXPECT(!b1.empty()); BEAST_EXPECT(b1.size() == 16); Buffer b2{b1.size()}; BEAST_EXPECT(sane(b2)); BEAST_EXPECT(!b2.empty()); BEAST_EXPECT(b2.size() == b1.size()); std::memcpy(b2.data(), data + 16, 16); Buffer b3{data, sizeof(data)}; BEAST_EXPECT(sane(b3)); BEAST_EXPECT(!b3.empty()); BEAST_EXPECT(b3.size() == sizeof(data)); BEAST_EXPECT(std::memcmp(b3.data(), data, b3.size()) == 0); // Check equality and inequality comparisons BEAST_EXPECT(b0 == b0); BEAST_EXPECT(b0 != b1); BEAST_EXPECT(b1 == b1); BEAST_EXPECT(b1 != b2); BEAST_EXPECT(b2 != b3); // Check copy constructors and copy assignments: { testcase("Copy Construction / Assignment"); Buffer x{b0}; BEAST_EXPECT(x == b0); BEAST_EXPECT(sane(x)); Buffer y{b1}; BEAST_EXPECT(y == b1); BEAST_EXPECT(sane(y)); x = b2; BEAST_EXPECT(x == b2); BEAST_EXPECT(sane(x)); x = y; BEAST_EXPECT(x == y); BEAST_EXPECT(sane(x)); y = b3; BEAST_EXPECT(y == b3); BEAST_EXPECT(sane(y)); x = b0; BEAST_EXPECT(x == b0); BEAST_EXPECT(sane(x)); #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wself-assign-overloaded" #endif x = x; BEAST_EXPECT(x == b0); BEAST_EXPECT(sane(x)); y = y; BEAST_EXPECT(y == b3); BEAST_EXPECT(sane(y)); #if defined(__clang__) #pragma clang diagnostic pop #endif } // Check move constructor & move assignments: { testcase("Move Construction / Assignment"); static_assert(std::is_nothrow_move_constructible_v); static_assert(std::is_nothrow_move_assignable_v); { // Move-construct from empty buf Buffer x; Buffer const y{std::move(x)}; BEAST_EXPECT(sane(x)); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(x.empty()); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(sane(y)); BEAST_EXPECT(y.empty()); BEAST_EXPECT(x == y); // NOLINT(bugprone-use-after-move) } { // Move-construct from non-empty buf Buffer x{b1}; Buffer const y{std::move(x)}; BEAST_EXPECT(sane(x)); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(x.empty()); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(sane(y)); BEAST_EXPECT(y == b1); } { // Move assign empty buf to empty buf Buffer x; Buffer y; x = std::move(y); BEAST_EXPECT(sane(x)); BEAST_EXPECT(x.empty()); BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move) } { // Move assign non-empty buf to empty buf Buffer x; Buffer y{b1}; x = std::move(y); BEAST_EXPECT(sane(x)); BEAST_EXPECT(x == b1); BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move) } { // Move assign empty buf to non-empty buf Buffer x{b1}; Buffer y; x = std::move(y); BEAST_EXPECT(sane(x)); BEAST_EXPECT(x.empty()); BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move) } { // Move assign non-empty buf to non-empty buf Buffer x{b1}; Buffer y{b2}; Buffer z{b3}; x = std::move(y); BEAST_EXPECT(sane(x)); BEAST_EXPECT(!x.empty()); BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move) x = std::move(z); BEAST_EXPECT(sane(x)); BEAST_EXPECT(!x.empty()); BEAST_EXPECT(sane(z)); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(z.empty()); // NOLINT(bugprone-use-after-move) } } { testcase("Slice Conversion / Construction / Assignment"); Buffer w{static_cast(b0)}; BEAST_EXPECT(sane(w)); BEAST_EXPECT(w == b0); Buffer x{static_cast(b1)}; BEAST_EXPECT(sane(x)); BEAST_EXPECT(x == b1); Buffer y{static_cast(b2)}; BEAST_EXPECT(sane(y)); BEAST_EXPECT(y == b2); Buffer z{static_cast(b3)}; BEAST_EXPECT(sane(z)); BEAST_EXPECT(z == b3); // Assign empty slice to empty buffer w = static_cast(b0); BEAST_EXPECT(sane(w)); BEAST_EXPECT(w == b0); // Assign non-empty slice to empty buffer w = static_cast(b1); BEAST_EXPECT(sane(w)); BEAST_EXPECT(w == b1); // Assign non-empty slice to non-empty buffer x = static_cast(b2); BEAST_EXPECT(sane(x)); BEAST_EXPECT(x == b2); // Assign non-empty slice to non-empty buffer y = static_cast(z); BEAST_EXPECT(sane(y)); BEAST_EXPECT(y == z); // Assign empty slice to non-empty buffer: z = static_cast(b0); BEAST_EXPECT(sane(z)); BEAST_EXPECT(z == b0); } { testcase("Allocation, Deallocation and Clearing"); auto test = [this](Buffer const& b, std::size_t i) { Buffer x{b}; // Try to allocate some number of bytes, possibly // zero (which means clear) and sanity check x(i); BEAST_EXPECT(sane(x)); BEAST_EXPECT(x.size() == i); BEAST_EXPECT((x.data() == nullptr) == (i == 0)); // Try to allocate some more data (always non-zero) x(i + 1); BEAST_EXPECT(sane(x)); BEAST_EXPECT(x.size() == i + 1); BEAST_EXPECT(x.data() != nullptr); // Try to clear: x.clear(); BEAST_EXPECT(sane(x)); BEAST_EXPECT(x.empty()); BEAST_EXPECT(x.data() == nullptr); // Try to clear again: x.clear(); BEAST_EXPECT(sane(x)); BEAST_EXPECT(x.empty()); BEAST_EXPECT(x.data() == nullptr); }; for (std::size_t i = 0; i < 16; ++i) { test(b0, i); test(b1, i); } } } }; BEAST_DEFINE_TESTSUITE(Buffer, basics, xrpl); } // namespace xrpl::test