Skip to content

Commit

Permalink
Merge pull request #357 from ethereum/cpp_basic_types
Browse files Browse the repository at this point in the history
C++: Add basic types based on C types
  • Loading branch information
chfast authored Jul 23, 2019
2 parents 83d2acf + ab4ea82 commit 5c55ac5
Show file tree
Hide file tree
Showing 6 changed files with 366 additions and 40 deletions.
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(
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

0 comments on commit 5c55ac5

Please sign in to comment.