From 635aebeb6646d5845ba24b1aa27d58762dfecebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 22 Jul 2019 13:40:14 +0200 Subject: [PATCH] cpp: Add std::hash specializations for basic types --- include/evmc/evmc.hpp | 49 +++++++++++++++++++++++++++++++++++++ test/unittests/test_cpp.cpp | 26 ++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/include/evmc/evmc.hpp b/include/evmc/evmc.hpp index 9975ecd..e38cc20 100644 --- a/include/evmc/evmc.hpp +++ b/include/evmc/evmc.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -52,6 +53,18 @@ constexpr inline uint32_t load32be(const uint8_t* bytes) noexcept 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 @@ -479,3 +492,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 +{ + /// 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(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 +{ + /// 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( + 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 diff --git a/test/unittests/test_cpp.cpp b/test/unittests/test_cpp.cpp index 469be2a..74c02dd 100644 --- a/test/unittests/test_cpp.cpp +++ b/test/unittests/test_cpp.cpp @@ -16,6 +16,7 @@ #include + TEST(cpp, address) { evmc::address a; @@ -46,6 +47,31 @@ TEST(cpp, bytes32) EXPECT_TRUE(std::equal(std::begin(b.bytes), std::end(b.bytes), std::begin(other.bytes))); } +TEST(cpp, std_hash) +{ +#pragma warning(push) +#pragma warning(disable : 4307 /* integral constant overflow */) +#pragma warning(disable : 4309 /* 'static_cast': truncation of constant value */) + +#if !defined(_MSC_VER) || (_MSC_VER >= 1910 /* Only for Visual Studio 2017+ */) + static_assert(std::hash{}({}) == static_cast(0xd94d12186c0f2fb7), ""); + static_assert(std::hash{}({}) == static_cast(0x4d25767f9dce13f5), ""); +#endif + + EXPECT_EQ(std::hash{}({}), static_cast(0xd94d12186c0f2fb7)); + EXPECT_EQ(std::hash{}({}), static_cast(0x4d25767f9dce13f5)); + + auto ea = evmc::address{}; + std::fill_n(ea.bytes, sizeof(ea), uint8_t{0xee}); + EXPECT_EQ(std::hash{}(ea), static_cast(0x41dc0178e01b7cd9)); + + auto eb = evmc::bytes32{}; + std::fill_n(eb.bytes, sizeof(eb), uint8_t{0xee}); + EXPECT_EQ(std::hash{}(eb), static_cast(0xbb14e5c56b477375)); + +#pragma warning(pop) +} + TEST(cpp, address_comparison) { const auto zero = evmc::address{};