Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C++: Add basic types based on C types #357

Merged
merged 9 commits into from
Jul 23, 2019
Merged
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
Full support for 32-bit architectures has been added.
- Added: [[#341](https://github.com/ethereum/evmc/pull/341)]
Support for moving `evmc::vm` objects in C++ API.
- Added: [[#357](https://github.com/ethereum/evmc/pull/357)]
The basic types `address` and `bytes32` have received their C++ wrappers
to assure they are always initialized. They also have convenient operator
overloadings for comparison and usage as keys in standard containers.
- Changed: [[#293](https://github.com/ethereum/evmc/pull/293)]
In C++ API `evmc::result::raw()` renamed to `evmc::result::release_raw()`.
- Changed: [[#311](https://github.com/ethereum/evmc/pull/311)]
Expand Down
13 changes: 6 additions & 7 deletions examples/example_host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,20 @@
#include "example_host.h"

#include <evmc/evmc.hpp>
#include <evmc/helpers.hpp>

#include <map>

struct account
{
evmc_uint256be balance = {};
evmc::uint256be balance = {};
size_t code_size = 0;
evmc_bytes32 code_hash = {};
std::map<evmc_bytes32, evmc_bytes32> storage;
evmc::bytes32 code_hash = {};
std::map<evmc::bytes32, evmc::bytes32> storage;
};

class ExampleHost : public evmc::Host
{
std::map<evmc_address, account> accounts;
std::map<evmc::address, account> accounts;

public:
bool account_exists(const evmc_address& addr) noexcept final
Expand Down Expand Up @@ -111,9 +110,9 @@ class ExampleHost : public evmc::Host
{
int64_t current_block_number = get_tx_context().block_number;

auto example_block_hash = evmc_bytes32{};
auto example_block_hash = evmc::bytes32{};
if (number < current_block_number && number >= current_block_number - 256)
example_block_hash = {{1, 1, 1, 1}};
example_block_hash = evmc::bytes32{{{1, 1, 1, 1}}};
return example_block_hash;
}

Expand Down
173 changes: 173 additions & 0 deletions include/evmc/evmc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,150 @@
#include <evmc/evmc.h>
#include <evmc/helpers.h>

#include <functional>
#include <initializer_list>
#include <utility>

/// EVMC C++ API - wrappers and bindings for C++
/// @ingroup cpp
namespace evmc
{
/// The big-endian 160-bit hash suitable for keeping an Ethereum address.
///
/// This type wraps C ::evmc_address to make sure objects of this type are always initialized.
struct address : evmc_address
{
/// Default and converting constructor.
///
/// Initializes bytes to zeros if not other @p init value provided.
constexpr address(evmc_address init = {}) noexcept : evmc_address{init} {}

/// Explicit operator converting to bool.
constexpr inline explicit operator bool() const noexcept;
};

/// The fixed size array of 32 bytes for storing 256-bit EVM values.
///
/// This type wraps C ::evmc_bytes32 to make sure objects of this type are always initialized.
struct bytes32 : evmc_bytes32
{
/// Default and converting constructor.
///
/// Initializes bytes to zeros if not other @p init value provided.
constexpr bytes32(evmc_bytes32 init = {}) noexcept : evmc_bytes32{init} {}

/// Explicit operator converting to bool.
constexpr inline explicit operator bool() const noexcept;
};

/// The alias for evmc::bytes32 to represent a big-endian 256-bit integer.
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]};
}

/// Loads 32 bits / 4 bytes of data from the given @p bytes array in big-endian order.
constexpr inline uint32_t load32be(const uint8_t* bytes) noexcept
{
return (uint32_t{bytes[0]} << 24) | (uint32_t{bytes[1]} << 16) | (uint32_t{bytes[2]} << 8) |
uint32_t{bytes[3]};
}

namespace fnv
{
constexpr auto prime = 0x100000001b3; ///< The 64-bit FNV prime number.
constexpr auto offset_basis = 0xcbf29ce484222325; ///< The 64-bit FNV offset basis.

/// The hashing transformation for 64-bit inputs based on the FNV-1a formula.
constexpr inline uint64_t fnv1a_by64(uint64_t h, uint64_t x) noexcept
{
return (h ^ x) * prime;
}
} // namespace fnv


/// The "equal" 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.
return load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
load32be(&a.bytes[16]) == load32be(&b.bytes[16]);
}

/// The "not equal" 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.
constexpr bool operator<(const address& a, const address& b) noexcept
{
return load64be(&a.bytes[0]) < load64be(&b.bytes[0]) ||
(load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
load64be(&a.bytes[8]) < load64be(&b.bytes[8])) ||
(load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
load32be(&a.bytes[16]) < load32be(&b.bytes[16]));
}

/// The "equal" 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]) &&
load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
load64be(&a.bytes[16]) == load64be(&b.bytes[16]) &&
load64be(&a.bytes[24]) == load64be(&b.bytes[24]);
}

/// The "not equal" 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.
constexpr bool operator<(const bytes32& a, const bytes32& b) noexcept
{
return load64be(&a.bytes[0]) < load64be(&b.bytes[0]) ||
(load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
load64be(&a.bytes[8]) < load64be(&b.bytes[8])) ||
(load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
load64be(&a.bytes[16]) < load64be(&b.bytes[16])) ||
(load64be(&a.bytes[16]) == load64be(&b.bytes[16]) &&
load64be(&a.bytes[24]) < load64be(&b.bytes[24]));
}

/// Checks if the given address is the zero address.
constexpr inline bool is_zero(const address& a) noexcept
{
return a == address{};
}

constexpr address::operator bool() const noexcept
{
return !is_zero(*this);
}

/// Checks if the given bytes32 object has all zero bytes.
constexpr inline bool is_zero(const bytes32& a) noexcept
{
return a == bytes32{};
}

constexpr bytes32::operator bool() const noexcept
{
return !is_zero(*this);
}


/// @copydoc evmc_result
///
/// This is a RAII wrapper for evmc_result and objects of this type
Expand Down Expand Up @@ -387,3 +524,39 @@ constexpr evmc_host_interface interface{
inline Host::Host() noexcept : evmc_context{&internal::interface} {}

} // namespace evmc


namespace std
{
/// Hash operator template specialization for evmc::address. Needed for unordered containers.
template <>
struct hash<evmc::address>
{
/// Hash operator using FNV1a-based folding.
constexpr size_t operator()(const evmc::address& s) const noexcept
{
using namespace evmc;
using namespace fnv;
return static_cast<size_t>(fnv1a_by64(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this would truncate to the lower 32 bits on a 32-bit machine?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

fnv1a_by64(fnv1a_by64(fnv::offset_basis, load64be(&s.bytes[0])), load64be(&s.bytes[8])),
load32be(&s.bytes[16])));
}
};

/// Hash operator template specialization for evmc::bytes32. Needed for unordered containers.
template <>
struct hash<evmc::bytes32>
{
/// Hash operator using FNV1a-based folding.
constexpr size_t operator()(const evmc::bytes32& s) const noexcept
{
using namespace evmc;
using namespace fnv;
return static_cast<size_t>(
fnv1a_by64(fnv1a_by64(fnv1a_by64(fnv1a_by64(fnv::offset_basis, load64be(&s.bytes[0])),
load64be(&s.bytes[8])),
load64be(&s.bytes[16])),
load64be(&s.bytes[24])));
}
};
} // namespace std
Loading