Skip to content

Commit fe0d43f

Browse files
committed
C++: Rework hex literals parsing
1 parent a476d6e commit fe0d43f

File tree

3 files changed

+25
-58
lines changed

3 files changed

+25
-58
lines changed

include/evmc/evmc.hpp

Lines changed: 18 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <evmc/evmc.h>
77
#include <evmc/helpers.h>
8+
#include <evmc/hex.hpp>
89

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

281282
namespace literals
282283
{
283-
namespace internal
284-
{
285-
constexpr int from_hex(char c) noexcept
286-
{
287-
return (c >= 'a' && c <= 'f') ? c - ('a' - 10) :
288-
(c >= 'A' && c <= 'F') ? c - ('A' - 10) :
289-
c - '0';
290-
}
291-
292-
constexpr uint8_t byte(const char* s, size_t i) noexcept
293-
{
294-
return static_cast<uint8_t>((from_hex(s[2 * i]) << 4) | from_hex(s[2 * i + 1]));
295-
}
284+
/// Breaks compilation and reports error string in constexpr context.
285+
inline void error([[maybe_unused]] const char* message) noexcept {}
296286

287+
/// Converts a raw literal into value of type T.
297288
template <typename T>
298-
T from_hex(const char*) noexcept;
299-
300-
template <>
301-
constexpr bytes32 from_hex<bytes32>(const char* s) noexcept
302-
{
303-
return {
304-
{{byte(s, 0), byte(s, 1), byte(s, 2), byte(s, 3), byte(s, 4), byte(s, 5), byte(s, 6),
305-
byte(s, 7), byte(s, 8), byte(s, 9), byte(s, 10), byte(s, 11), byte(s, 12), byte(s, 13),
306-
byte(s, 14), byte(s, 15), byte(s, 16), byte(s, 17), byte(s, 18), byte(s, 19), byte(s, 20),
307-
byte(s, 21), byte(s, 22), byte(s, 23), byte(s, 24), byte(s, 25), byte(s, 26), byte(s, 27),
308-
byte(s, 28), byte(s, 29), byte(s, 30), byte(s, 31)}}};
309-
}
310-
311-
template <>
312-
constexpr address from_hex<address>(const char* s) noexcept
289+
constexpr T to(std::string_view s) noexcept
313290
{
314-
return {
315-
{{byte(s, 0), byte(s, 1), byte(s, 2), byte(s, 3), byte(s, 4), byte(s, 5), byte(s, 6),
316-
byte(s, 7), byte(s, 8), byte(s, 9), byte(s, 10), byte(s, 11), byte(s, 12), byte(s, 13),
317-
byte(s, 14), byte(s, 15), byte(s, 16), byte(s, 17), byte(s, 18), byte(s, 19)}}};
318-
}
291+
if (s == "0")
292+
return T{};
319293

320-
template <typename T, char... c>
321-
constexpr T from_literal() noexcept
322-
{
323-
constexpr auto size = sizeof...(c);
324-
constexpr char literal[] = {c...};
325-
constexpr bool is_simple_zero = size == 1 && literal[0] == '0';
294+
if (s[0] != '0' || s[1] != 'x')
295+
error("literal must be in hexadecimal notation");
326296

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

332-
return is_simple_zero ? T{} : from_hex<T>(&literal[2]);
300+
T r{};
301+
internal::from_hex(s, r.bytes);
302+
return r;
333303
}
334-
} // namespace internal
335304

336305
/// Literal for evmc::address.
337-
template <char... c>
338-
constexpr address operator""_address() noexcept
306+
constexpr address operator""_address(const char* s) noexcept
339307
{
340-
return internal::from_literal<address, c...>();
308+
return to<address>(s);
341309
}
342310

343311
/// Literal for evmc::bytes32.
344-
template <char... c>
345-
constexpr bytes32 operator""_bytes32() noexcept
312+
constexpr bytes32 operator""_bytes32(const char* s) noexcept
346313
{
347-
return internal::from_literal<bytes32, c...>();
314+
return to<bytes32>(s);
348315
}
349316
} // namespace literals
350317

include/evmc/hex.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ inline std::string hex(bytes_view bs)
3535
return str;
3636
}
3737

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

9090
return hi_nibble == empty_mark;
9191
}
92-
} // namespace internal_hex
92+
} // namespace internal
9393

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

106-
return internal_hex::from_hex(hex, noop_output_iterator{});
106+
return internal::from_hex(hex, noop_output_iterator{});
107107
}
108108

109109
/// Decodes hex encoded string to bytes.
@@ -115,7 +115,7 @@ inline std::optional<bytes> from_hex(std::string_view hex)
115115
{
116116
bytes bs;
117117
bs.reserve(hex.size() / 2);
118-
if (!internal_hex::from_hex(hex, std::back_inserter(bs)))
118+
if (!internal::from_hex(hex, std::back_inserter(bs)))
119119
return {};
120120
return bs;
121121
}

test/unittests/hex_test.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ TEST(hex, isspace)
8989
for (int i = int{std::numeric_limits<char>::min()}; i <= std::numeric_limits<char>::max(); ++i)
9090
{
9191
const auto c = static_cast<char>(i);
92-
EXPECT_EQ(evmc::internal_hex::isspace(c), (std::isspace(c) != 0));
92+
EXPECT_EQ(evmc::internal::isspace(c), (std::isspace(c) != 0));
9393
switch (c)
9494
{
9595
case ' ':
@@ -98,10 +98,10 @@ TEST(hex, isspace)
9898
case '\r':
9999
case '\t':
100100
case '\v':
101-
EXPECT_TRUE(evmc::internal_hex::isspace(c));
101+
EXPECT_TRUE(evmc::internal::isspace(c));
102102
break;
103103
default:
104-
EXPECT_FALSE(evmc::internal_hex::isspace(c));
104+
EXPECT_FALSE(evmc::internal::isspace(c));
105105
break;
106106
}
107107
}

0 commit comments

Comments
 (0)