Merge pull request #474 from ethereum/cpp-operators

cpp: Add all comparison operators for address and bytes32
This commit is contained in:
Paweł Bylica 2019-12-05 15:54:29 +01:00 committed by GitHub
commit 0faf6d1aea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 146 additions and 51 deletions

View File

@ -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

View File

@ -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
{

View File

@ -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 <typename T>
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);
}
}