Skip to content

Commit

Permalink
hex: Add from_hex<T> for user types
Browse files Browse the repository at this point in the history
  • Loading branch information
chfast committed Jun 1, 2022
1 parent 5cec7bd commit 95805a2
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 0 deletions.
20 changes: 20 additions & 0 deletions include/evmc/hex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,24 @@ inline std::optional<bytes> from_hex(std::string_view hex)
return {};
return bs;
}

/// Decodes hex-encoded string into custom type T with .bytes array of uint8_t.
///
/// The decoded bytes are right aligned in case the input is smaller than the result type.
template <typename T>
constexpr std::optional<T> from_hex(std::string_view s) noexcept
{
// Omit the optional 0x prefix.
if (s.size() >= 2 && s[0] == '0' && s[1] == 'x')
s.remove_prefix(2);

T r{}; // The T must have .bytes array. This may be lifted if std::bit_cast is available.
constexpr auto num_out_bytes = std::size(r.bytes);
const auto num_in_bytes = s.length() / 2;
if (num_in_bytes > num_out_bytes)
return {};
if (!from_hex(s.begin(), s.end(), &r.bytes[num_out_bytes - num_in_bytes]))
return {};
return r;
}
} // namespace evmc
48 changes: 48 additions & 0 deletions test/unittests/hex_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,16 @@ TEST(hex, from_hex_0x_prefix)
EXPECT_EQ(from_hex("0x01020304"), (bytes{0x01, 0x02, 0x03, 0x04}));
EXPECT_EQ(from_hex("0x123"), std::nullopt);
EXPECT_EQ(from_hex("00x"), std::nullopt);
EXPECT_EQ(from_hex("000s"), std::nullopt);
EXPECT_EQ(from_hex("00x0"), std::nullopt);
EXPECT_EQ(from_hex("0x001x"), std::nullopt);
EXPECT_EQ(from_hex("0x001y"), std::nullopt);
EXPECT_EQ(from_hex("1x"), std::nullopt);
EXPECT_EQ(from_hex("1x00"), std::nullopt);
EXPECT_EQ(from_hex("0y"), std::nullopt);
EXPECT_EQ(from_hex("0y00"), std::nullopt);
EXPECT_EQ(from_hex("1y"), std::nullopt);
EXPECT_EQ(from_hex("1y00"), std::nullopt);
}

TEST(hex, validate_hex)
Expand Down Expand Up @@ -90,3 +98,43 @@ TEST(hex, from_hex_skip_space)
EXPECT_EQ(from_hex_skip_space(" 0 x 0 1 0 2 0 3 "), (bytes{0x01, 0x02, 0x03}));
EXPECT_EQ(from_hex_skip_space("\f 0\r x 0 1\t 0 2 \v0 3 \n"), (bytes{0x01, 0x02, 0x03}));
}

TEST(hex, from_hex_to_custom_type)
{
struct X
{
uint8_t bytes[4];
};
constexpr auto test = [](std::string_view in) {
return evmc::hex({evmc::from_hex<X>(in).value().bytes, sizeof(X)});
};

static_assert(evmc::from_hex<X>("01").value().bytes[3] == 0x01); // Works in constexpr.

EXPECT_EQ(test("f1f2f3f4"), "f1f2f3f4");
EXPECT_EQ(test("01020304"), "01020304");
EXPECT_EQ(test("010203"), "00010203");
EXPECT_EQ(test("0102"), "00000102");
EXPECT_EQ(test("01"), "00000001");
EXPECT_EQ(test(""), "00000000");

EXPECT_EQ(test("0x01020304"), "01020304");
EXPECT_EQ(test("0x010203"), "00010203");
EXPECT_EQ(test("0x0102"), "00000102");
EXPECT_EQ(test("0x01"), "00000001");
EXPECT_EQ(test("0x"), "00000000");

EXPECT_FALSE(evmc::from_hex<X>("0"));
EXPECT_FALSE(evmc::from_hex<X>("1"));
EXPECT_FALSE(evmc::from_hex<X>("0x "));
EXPECT_FALSE(evmc::from_hex<X>("0xf"));
EXPECT_FALSE(evmc::from_hex<X>("0x 00"));
EXPECT_FALSE(evmc::from_hex<X>("1x"));
EXPECT_FALSE(evmc::from_hex<X>("1x00"));
EXPECT_FALSE(evmc::from_hex<X>("fx"));
EXPECT_FALSE(evmc::from_hex<X>("fx00"));

// The result type is too small for the input.
EXPECT_FALSE(evmc::from_hex<X>("0000000000"));
EXPECT_FALSE(evmc::from_hex<X>("0x0000000000"));
}

0 comments on commit 95805a2

Please sign in to comment.