diff --git a/CHANGELOG.md b/CHANGELOG.md index 02ac419..67d7c66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning]. - Added Java bindings. [#455](https://github.com/ethereum/evmc/pull/455) +- The C++ EVMC basic types `address` and `bytes32` have all the comparison operators supported. + [#474](https://github.com/ethereum/evmc/pull/474) ## [7.1.0] — 2019-11-29 diff --git a/include/evmc/evmc.hpp b/include/evmc/evmc.hpp index 0b44cd1..5ff85fb 100644 --- a/include/evmc/evmc.hpp +++ b/include/evmc/evmc.hpp @@ -50,7 +50,6 @@ using uint256be = bytes32; /// Loads 64 bits / 8 bytes of data from the given @p bytes array in big-endian order. constexpr inline uint64_t load64be(const uint8_t* bytes) noexcept { - // TODO: Report bug in clang incorrectly optimizing this with AVX2 enabled. return (uint64_t{bytes[0]} << 56) | (uint64_t{bytes[1]} << 48) | (uint64_t{bytes[2]} << 40) | (uint64_t{bytes[3]} << 32) | (uint64_t{bytes[4]} << 24) | (uint64_t{bytes[5]} << 16) | (uint64_t{bytes[6]} << 8) | uint64_t{bytes[7]}; @@ -76,7 +75,7 @@ constexpr inline uint64_t fnv1a_by64(uint64_t h, uint64_t x) noexcept } // namespace fnv -/// The "equal" comparison operator for the evmc::address type. +/// The "equal to" comparison operator for the evmc::address type. constexpr bool operator==(const address& a, const address& b) noexcept { // TODO: Report bug in clang keeping unnecessary bswap. @@ -85,13 +84,13 @@ constexpr bool operator==(const address& a, const address& b) noexcept load32be(&a.bytes[16]) == load32be(&b.bytes[16]); } -/// The "not equal" comparison operator for the evmc::address type. +/// The "not equal to" comparison operator for the evmc::address type. constexpr bool operator!=(const address& a, const address& b) noexcept { return !(a == b); } -/// The "less" comparison operator for the evmc::address type. +/// The "less than" comparison operator for the evmc::address type. constexpr bool operator<(const address& a, const address& b) noexcept { return load64be(&a.bytes[0]) < load64be(&b.bytes[0]) || @@ -101,7 +100,25 @@ constexpr bool operator<(const address& a, const address& b) noexcept load32be(&a.bytes[16]) < load32be(&b.bytes[16])); } -/// The "equal" comparison operator for the evmc::bytes32 type. +/// The "greater than" comparison operator for the evmc::address type. +constexpr bool operator>(const address& a, const address& b) noexcept +{ + return b < a; +} + +/// The "less than or equal to" comparison operator for the evmc::address type. +constexpr bool operator<=(const address& a, const address& b) noexcept +{ + return !(b < a); +} + +/// The "greater than or equal to" comparison operator for the evmc::address type. +constexpr bool operator>=(const address& a, const address& b) noexcept +{ + return !(a < b); +} + +/// The "equal to" comparison operator for the evmc::bytes32 type. constexpr bool operator==(const bytes32& a, const bytes32& b) noexcept { return load64be(&a.bytes[0]) == load64be(&b.bytes[0]) && @@ -110,13 +127,13 @@ constexpr bool operator==(const bytes32& a, const bytes32& b) noexcept load64be(&a.bytes[24]) == load64be(&b.bytes[24]); } -/// The "not equal" comparison operator for the evmc::bytes32 type. +/// The "not equal to" comparison operator for the evmc::bytes32 type. constexpr bool operator!=(const bytes32& a, const bytes32& b) noexcept { return !(a == b); } -/// The "less" comparison operator for the evmc::bytes32 type. +/// The "less than" comparison operator for the evmc::bytes32 type. constexpr bool operator<(const bytes32& a, const bytes32& b) noexcept { return load64be(&a.bytes[0]) < load64be(&b.bytes[0]) || @@ -128,6 +145,24 @@ constexpr bool operator<(const bytes32& a, const bytes32& b) noexcept load64be(&a.bytes[24]) < load64be(&b.bytes[24])); } +/// The "greater than" comparison operator for the evmc::bytes32 type. +constexpr bool operator>(const bytes32& a, const bytes32& b) noexcept +{ + return b < a; +} + +/// The "less than or equal to" comparison operator for the evmc::bytes32 type. +constexpr bool operator<=(const bytes32& a, const bytes32& b) noexcept +{ + return !(b < a); +} + +/// The "greater than or equal to" comparison operator for the evmc::bytes32 type. +constexpr bool operator>=(const bytes32& a, const bytes32& b) noexcept +{ + return !(a < b); +} + /// Checks if the given address is the zero address. constexpr inline bool is_zero(const address& a) noexcept { diff --git a/test/unittests/cpp_test.cpp b/test/unittests/cpp_test.cpp index f7e4934..3a79632 100644 --- a/test/unittests/cpp_test.cpp +++ b/test/unittests/cpp_test.cpp @@ -155,9 +155,79 @@ TEST(cpp, std_maps) EXPECT_FALSE(unordered_storage.begin()->first); } +enum relation +{ + equal, + less, + greater +}; + +/// Compares x and y using all comparison operators (also with reversed argument order) +/// and validates results against the expected relation: eq: x == y, less: x < y. +template +static void expect_cmp(const T& x, const T& y, relation expected) +{ + switch (expected) + { + case equal: + EXPECT_TRUE(x == y); + EXPECT_FALSE(x != y); + EXPECT_FALSE(x < y); + EXPECT_TRUE(x <= y); + EXPECT_FALSE(x > y); + EXPECT_TRUE(x >= y); + + EXPECT_TRUE(y == x); + EXPECT_FALSE(y != x); + EXPECT_FALSE(y < x); + EXPECT_TRUE(y <= x); + EXPECT_FALSE(y > x); + EXPECT_TRUE(y >= x); + break; + case less: + EXPECT_FALSE(x == y); + EXPECT_TRUE(x != y); + EXPECT_TRUE(x < y); + EXPECT_TRUE(x <= y); + EXPECT_FALSE(x > y); + EXPECT_FALSE(x >= y); + + EXPECT_FALSE(y == x); + EXPECT_TRUE(y != x); + EXPECT_FALSE(y < x); + EXPECT_FALSE(y <= x); + EXPECT_TRUE(y > x); + EXPECT_TRUE(y >= x); + break; + case greater: + EXPECT_FALSE(x == y); + EXPECT_TRUE(x != y); + EXPECT_FALSE(x < y); + EXPECT_FALSE(x <= y); + EXPECT_TRUE(x > y); + EXPECT_TRUE(x >= y); + + EXPECT_FALSE(y == x); + EXPECT_TRUE(y != x); + EXPECT_TRUE(y < x); + EXPECT_TRUE(y <= x); + EXPECT_FALSE(y > x); + EXPECT_FALSE(y >= x); + break; + } +} + TEST(cpp, address_comparison) { const auto zero = evmc::address{}; + auto max = evmc::address{}; + std::fill_n(max.bytes, sizeof(max), uint8_t{0xff}); + + expect_cmp(zero, zero, equal); + expect_cmp(max, max, equal); + expect_cmp(zero, max, less); + expect_cmp(max, zero, greater); + for (size_t i = 0; i < sizeof(evmc::address); ++i) { auto t = evmc::address{}; @@ -167,37 +237,35 @@ TEST(cpp, address_comparison) auto f = evmc::address{}; f.bytes[i] = 0xff; - EXPECT_TRUE(zero < t); - EXPECT_TRUE(zero < u); - EXPECT_TRUE(zero < f); - EXPECT_TRUE(zero != t); - EXPECT_TRUE(zero != u); - EXPECT_TRUE(zero != f); + expect_cmp(zero, t, less); + expect_cmp(zero, u, less); + expect_cmp(zero, f, less); - EXPECT_TRUE(t < u); - EXPECT_TRUE(t < f); - EXPECT_TRUE(u < f); + expect_cmp(t, max, less); + expect_cmp(u, max, less); + expect_cmp(f, max, less); - EXPECT_FALSE(u < t); - EXPECT_FALSE(f < t); - EXPECT_FALSE(f < u); + expect_cmp(t, u, less); + expect_cmp(t, f, less); + expect_cmp(u, f, less); - EXPECT_TRUE(t != u); - EXPECT_TRUE(t != f); - EXPECT_TRUE(u != t); - EXPECT_TRUE(u != f); - EXPECT_TRUE(f != t); - EXPECT_TRUE(f != u); - - EXPECT_TRUE(t == t); - EXPECT_TRUE(u == u); - EXPECT_TRUE(f == f); + expect_cmp(t, t, equal); + expect_cmp(u, u, equal); + expect_cmp(f, f, equal); } } TEST(cpp, bytes32_comparison) { const auto zero = evmc::bytes32{}; + auto max = evmc::bytes32{}; + std::fill_n(max.bytes, sizeof(max), uint8_t{0xff}); + + expect_cmp(zero, zero, equal); + expect_cmp(max, max, equal); + expect_cmp(zero, max, less); + expect_cmp(max, zero, greater); + for (size_t i = 0; i < sizeof(evmc::bytes32); ++i) { auto t = evmc::bytes32{}; @@ -207,31 +275,21 @@ TEST(cpp, bytes32_comparison) auto f = evmc::bytes32{}; f.bytes[i] = 0xff; - EXPECT_TRUE(zero < t); - EXPECT_TRUE(zero < u); - EXPECT_TRUE(zero < f); - EXPECT_TRUE(zero != t); - EXPECT_TRUE(zero != u); - EXPECT_TRUE(zero != f); + expect_cmp(zero, t, less); + expect_cmp(zero, u, less); + expect_cmp(zero, f, less); - EXPECT_TRUE(t < u); - EXPECT_TRUE(t < f); - EXPECT_TRUE(u < f); + expect_cmp(t, max, less); + expect_cmp(u, max, less); + expect_cmp(f, max, less); - EXPECT_FALSE(u < t); - EXPECT_FALSE(f < t); - EXPECT_FALSE(f < u); + expect_cmp(t, u, less); + expect_cmp(t, f, less); + expect_cmp(u, f, less); - EXPECT_TRUE(t != u); - EXPECT_TRUE(t != f); - EXPECT_TRUE(u != t); - EXPECT_TRUE(u != f); - EXPECT_TRUE(f != t); - EXPECT_TRUE(f != u); - - EXPECT_TRUE(t == t); - EXPECT_TRUE(u == u); - EXPECT_TRUE(f == f); + expect_cmp(t, t, equal); + expect_cmp(u, u, equal); + expect_cmp(f, f, equal); } }