Skip to content

Commit

Permalink
C++: Rework hex literals parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
chfast committed May 20, 2022
1 parent a476d6e commit fe0d43f
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 58 deletions.
69 changes: 18 additions & 51 deletions include/evmc/evmc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

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

#include <functional>
#include <initializer_list>
Expand Down Expand Up @@ -280,71 +281,37 @@ inline constexpr bytes32::operator bool() const noexcept

namespace literals
{
namespace internal
{
constexpr int from_hex(char c) noexcept
{
return (c >= 'a' && c <= 'f') ? c - ('a' - 10) :
(c >= 'A' && c <= 'F') ? c - ('A' - 10) :
c - '0';
}

constexpr uint8_t byte(const char* s, size_t i) noexcept
{
return static_cast<uint8_t>((from_hex(s[2 * i]) << 4) | from_hex(s[2 * i + 1]));
}
/// Breaks compilation and reports error string in constexpr context.
inline void error([[maybe_unused]] const char* message) noexcept {}

/// Converts a raw literal into value of type T.
template <typename T>
T from_hex(const char*) noexcept;

template <>
constexpr bytes32 from_hex<bytes32>(const char* s) noexcept
{
return {
{{byte(s, 0), byte(s, 1), byte(s, 2), byte(s, 3), byte(s, 4), byte(s, 5), byte(s, 6),
byte(s, 7), byte(s, 8), byte(s, 9), byte(s, 10), byte(s, 11), byte(s, 12), byte(s, 13),
byte(s, 14), byte(s, 15), byte(s, 16), byte(s, 17), byte(s, 18), byte(s, 19), byte(s, 20),
byte(s, 21), byte(s, 22), byte(s, 23), byte(s, 24), byte(s, 25), byte(s, 26), byte(s, 27),
byte(s, 28), byte(s, 29), byte(s, 30), byte(s, 31)}}};
}

template <>
constexpr address from_hex<address>(const char* s) noexcept
constexpr T to(std::string_view s) noexcept
{
return {
{{byte(s, 0), byte(s, 1), byte(s, 2), byte(s, 3), byte(s, 4), byte(s, 5), byte(s, 6),
byte(s, 7), byte(s, 8), byte(s, 9), byte(s, 10), byte(s, 11), byte(s, 12), byte(s, 13),
byte(s, 14), byte(s, 15), byte(s, 16), byte(s, 17), byte(s, 18), byte(s, 19)}}};
}
if (s == "0")
return T{};

template <typename T, char... c>
constexpr T from_literal() noexcept
{
constexpr auto size = sizeof...(c);
constexpr char literal[] = {c...};
constexpr bool is_simple_zero = size == 1 && literal[0] == '0';
if (s[0] != '0' || s[1] != 'x')
error("literal must be in hexadecimal notation");

static_assert(is_simple_zero || (literal[0] == '0' && literal[1] == 'x'),
"literal must be in hexadecimal notation");
static_assert(is_simple_zero || size == 2 * sizeof(T) + 2,
"literal must match the result type size");
if (s.length() != 2 * sizeof(T) + 2)
error("literal must match the result type size");

return is_simple_zero ? T{} : from_hex<T>(&literal[2]);
T r{};
internal::from_hex(s, r.bytes);
return r;
}
} // namespace internal

/// Literal for evmc::address.
template <char... c>
constexpr address operator""_address() noexcept
constexpr address operator""_address(const char* s) noexcept
{
return internal::from_literal<address, c...>();
return to<address>(s);
}

/// Literal for evmc::bytes32.
template <char... c>
constexpr bytes32 operator""_bytes32() noexcept
constexpr bytes32 operator""_bytes32(const char* s) noexcept
{
return internal::from_literal<bytes32, c...>();
return to<bytes32>(s);
}
} // namespace literals

Expand Down
8 changes: 4 additions & 4 deletions include/evmc/hex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ inline std::string hex(bytes_view bs)
return str;
}

namespace internal_hex
namespace internal
{
/// Extracts the nibble value out of a hex digit.
/// Returns -1 in case of invalid hex digit.
Expand Down Expand Up @@ -89,7 +89,7 @@ inline constexpr bool from_hex(std::string_view hex, OutputIt result) noexcept

return hi_nibble == empty_mark;
}
} // namespace internal_hex
} // namespace internal

/// Validates hex encoded string.
///
Expand All @@ -103,7 +103,7 @@ inline bool validate_hex(std::string_view hex) noexcept
noop_output_iterator operator++(int) noexcept { return *this; } // NOLINT(cert-dcl21-cpp)
};

return internal_hex::from_hex(hex, noop_output_iterator{});
return internal::from_hex(hex, noop_output_iterator{});
}

/// Decodes hex encoded string to bytes.
Expand All @@ -115,7 +115,7 @@ inline std::optional<bytes> from_hex(std::string_view hex)
{
bytes bs;
bs.reserve(hex.size() / 2);
if (!internal_hex::from_hex(hex, std::back_inserter(bs)))
if (!internal::from_hex(hex, std::back_inserter(bs)))
return {};
return bs;
}
Expand Down
6 changes: 3 additions & 3 deletions test/unittests/hex_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ TEST(hex, isspace)
for (int i = int{std::numeric_limits<char>::min()}; i <= std::numeric_limits<char>::max(); ++i)
{
const auto c = static_cast<char>(i);
EXPECT_EQ(evmc::internal_hex::isspace(c), (std::isspace(c) != 0));
EXPECT_EQ(evmc::internal::isspace(c), (std::isspace(c) != 0));
switch (c)
{
case ' ':
Expand All @@ -98,10 +98,10 @@ TEST(hex, isspace)
case '\r':
case '\t':
case '\v':
EXPECT_TRUE(evmc::internal_hex::isspace(c));
EXPECT_TRUE(evmc::internal::isspace(c));
break;
default:
EXPECT_FALSE(evmc::internal_hex::isspace(c));
EXPECT_FALSE(evmc::internal::isspace(c));
break;
}
}
Expand Down

0 comments on commit fe0d43f

Please sign in to comment.