/src/solidity/test/evmc/hex.hpp
Line | Count | Source |
1 | | // EVMC: Ethereum Client-VM Connector API. |
2 | | // Copyright 2021 The EVMC Authors. |
3 | | // Licensed under the Apache License, Version 2.0. |
4 | | #pragma once |
5 | | |
6 | | #include <evmc/bytes.hpp> |
7 | | #include <evmc/filter_iterator.hpp> |
8 | | #include <cstdint> |
9 | | #include <optional> |
10 | | #include <string> |
11 | | #include <string_view> |
12 | | |
13 | | namespace evmc |
14 | | { |
15 | | /// Encode a byte to a hex string. |
16 | | inline std::string hex(uint8_t b) noexcept |
17 | 0 | { |
18 | 0 | static constexpr auto hex_digits = "0123456789abcdef"; |
19 | 0 | return {hex_digits[b >> 4], hex_digits[b & 0xf]}; |
20 | 0 | } |
21 | | |
22 | | /// Encodes bytes as hex string. |
23 | | inline std::string hex(bytes_view bs) |
24 | 0 | { |
25 | 0 | std::string str; |
26 | 0 | str.reserve(bs.size() * 2); |
27 | 0 | for (const auto b : bs) |
28 | 0 | str += hex(b); |
29 | 0 | return str; |
30 | 0 | } |
31 | | |
32 | | namespace internal |
33 | | { |
34 | | /// Extracts the nibble value out of a hex digit. |
35 | | /// Returns -1 in case of invalid hex digit. |
36 | | inline constexpr int from_hex_digit(char h) noexcept |
37 | 97.0M | { |
38 | 97.0M | if (h >= '0' && h <= '9') |
39 | 89.8M | return h - '0'; |
40 | 7.25M | else if (h >= 'a' && h <= 'f') |
41 | 7.25M | return h - 'a' + 10; |
42 | 0 | else if (h >= 'A' && h <= 'F') |
43 | 0 | return h - 'A' + 10; |
44 | 0 | else |
45 | 0 | return -1; |
46 | 97.0M | } |
47 | | } // namespace internal |
48 | | |
49 | | /// Decodes hex-encoded sequence of characters. |
50 | | /// |
51 | | /// It is guaranteed that the output will not be longer than half of the input length. |
52 | | /// |
53 | | /// @param begin The input begin iterator. It only must satisfy input iterator concept. |
54 | | /// @param end The input end iterator. It only must satisfy input iterator concept. |
55 | | /// @param out The output iterator. It must satisfy output iterator concept. |
56 | | /// @return True if successful, false if input is invalid hex. |
57 | | template <typename InputIt, typename OutputIt> |
58 | | inline constexpr bool from_hex(InputIt begin, InputIt end, OutputIt out) noexcept |
59 | 2.22M | { |
60 | 2.22M | int hi_nibble = -1; // Init with invalid value, should never be used. |
61 | 2.22M | size_t i = 0; |
62 | 99.2M | for (auto it = begin; it != end; ++it, ++i) |
63 | 97.0M | { |
64 | 97.0M | const auto h = *it; |
65 | 97.0M | const int v = evmc::internal::from_hex_digit(h); |
66 | 97.0M | if (v < 0) |
67 | 0 | { |
68 | 0 | if (i == 1 && hi_nibble == 0 && h == 'x') // 0x prefix |
69 | 0 | continue; |
70 | 0 | return false; |
71 | 0 | } |
72 | | |
73 | 97.0M | if (i % 2 == 0) |
74 | 48.5M | hi_nibble = v << 4; |
75 | 48.5M | else |
76 | 48.5M | *out++ = static_cast<uint8_t>(hi_nibble | v); |
77 | 97.0M | } |
78 | | |
79 | 2.22M | return i % 2 == 0; |
80 | 2.22M | } bool evmc::from_hex<char const*, unsigned char*>(char const*, char const*, unsigned char*) Line | Count | Source | 59 | 2.22M | { | 60 | 2.22M | int hi_nibble = -1; // Init with invalid value, should never be used. | 61 | 2.22M | size_t i = 0; | 62 | 99.2M | for (auto it = begin; it != end; ++it, ++i) | 63 | 97.0M | { | 64 | 97.0M | const auto h = *it; | 65 | 97.0M | const int v = evmc::internal::from_hex_digit(h); | 66 | 97.0M | if (v < 0) | 67 | 0 | { | 68 | 0 | if (i == 1 && hi_nibble == 0 && h == 'x') // 0x prefix | 69 | 0 | continue; | 70 | 0 | return false; | 71 | 0 | } | 72 | | | 73 | 97.0M | if (i % 2 == 0) | 74 | 48.5M | hi_nibble = v << 4; | 75 | 48.5M | else | 76 | 48.5M | *out++ = static_cast<uint8_t>(hi_nibble | v); | 77 | 97.0M | } | 78 | | | 79 | 2.22M | return i % 2 == 0; | 80 | 2.22M | } |
Unexecuted instantiation: bool evmc::from_hex<char const*, evmc::validate_hex(std::__1::basic_string_view<char, std::__1::char_traits<char> >)::noop_output_iterator>(char const*, char const*, evmc::validate_hex(std::__1::basic_string_view<char, std::__1::char_traits<char> >)::noop_output_iterator) Unexecuted instantiation: bool evmc::from_hex<char const*, std::__1::back_insert_iterator<std::__1::basic_string<unsigned char, evmc::byte_traits<unsigned char>, std::__1::allocator<unsigned char> > > >(char const*, char const*, std::__1::back_insert_iterator<std::__1::basic_string<unsigned char, evmc::byte_traits<unsigned char>, std::__1::allocator<unsigned char> > >) Unexecuted instantiation: bool evmc::from_hex<evmc::skip_space_iterator<char const*>, std::__1::back_insert_iterator<std::__1::basic_string<unsigned char, evmc::byte_traits<unsigned char>, std::__1::allocator<unsigned char> > > >(evmc::skip_space_iterator<char const*>, evmc::skip_space_iterator<char const*>, std::__1::back_insert_iterator<std::__1::basic_string<unsigned char, evmc::byte_traits<unsigned char>, std::__1::allocator<unsigned char> > >) |
81 | | |
82 | | /// Validates hex encoded string. |
83 | | /// |
84 | | /// @return True if the input is valid hex. |
85 | | inline bool validate_hex(std::string_view hex) noexcept |
86 | 0 | { |
87 | 0 | struct noop_output_iterator |
88 | 0 | { |
89 | 0 | uint8_t sink = {}; |
90 | 0 | uint8_t& operator*() noexcept { return sink; } |
91 | 0 | noop_output_iterator operator++(int) noexcept { return *this; } // NOLINT(cert-dcl21-cpp) |
92 | 0 | }; |
93 | 0 |
|
94 | 0 | return from_hex(hex.begin(), hex.end(), noop_output_iterator{}); |
95 | 0 | } |
96 | | |
97 | | /// Decodes hex encoded string to bytes. |
98 | | /// |
99 | | /// In case the input is invalid the returned value is std::nullopt. |
100 | | /// This can happen if a non-hex digit or odd number of digits is encountered. |
101 | | inline std::optional<bytes> from_hex(std::string_view hex) |
102 | 0 | { |
103 | 0 | bytes bs; |
104 | 0 | bs.reserve(hex.size() / 2); |
105 | 0 | if (!from_hex(hex.begin(), hex.end(), std::back_inserter(bs))) |
106 | 0 | return {}; |
107 | 0 | return bs; |
108 | 0 | } |
109 | | |
110 | | /// Decodes hex-encoded string into custom type T with .bytes array of uint8_t. |
111 | | /// |
112 | | /// When the input is smaller than the result type, the result is padded with zeros on the left |
113 | | /// (the result bytes of lowest indices are filled with zeros). |
114 | | /// TODO: Support optional left alignment. |
115 | | template <typename T> |
116 | | constexpr std::optional<T> from_hex(std::string_view s) noexcept |
117 | 2.22M | { |
118 | | // Omit the optional 0x prefix. |
119 | 2.22M | if (s.size() >= 2 && s[0] == '0' && s[1] == 'x') |
120 | 2.22M | s.remove_prefix(2); |
121 | | |
122 | 2.22M | T r{}; // The T must have .bytes array. This may be lifted if std::bit_cast is available. |
123 | 2.22M | constexpr auto num_out_bytes = std::size(r.bytes); |
124 | 2.22M | const auto num_in_bytes = s.length() / 2; |
125 | 2.22M | if (num_in_bytes > num_out_bytes) |
126 | 0 | return {}; |
127 | 2.22M | if (!from_hex(s.begin(), s.end(), &r.bytes[num_out_bytes - num_in_bytes])) |
128 | 0 | return {}; |
129 | 2.22M | return r; |
130 | 2.22M | } std::__1::optional<evmc::bytes32> evmc::from_hex<evmc::bytes32>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 117 | 327k | { | 118 | | // Omit the optional 0x prefix. | 119 | 327k | if (s.size() >= 2 && s[0] == '0' && s[1] == 'x') | 120 | 327k | s.remove_prefix(2); | 121 | | | 122 | 327k | T r{}; // The T must have .bytes array. This may be lifted if std::bit_cast is available. | 123 | 327k | constexpr auto num_out_bytes = std::size(r.bytes); | 124 | 327k | const auto num_in_bytes = s.length() / 2; | 125 | 327k | if (num_in_bytes > num_out_bytes) | 126 | 0 | return {}; | 127 | 327k | if (!from_hex(s.begin(), s.end(), &r.bytes[num_out_bytes - num_in_bytes])) | 128 | 0 | return {}; | 129 | 327k | return r; | 130 | 327k | } |
std::__1::optional<evmc::address> evmc::from_hex<evmc::address>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 117 | 1.90M | { | 118 | | // Omit the optional 0x prefix. | 119 | 1.90M | if (s.size() >= 2 && s[0] == '0' && s[1] == 'x') | 120 | 1.90M | s.remove_prefix(2); | 121 | | | 122 | 1.90M | T r{}; // The T must have .bytes array. This may be lifted if std::bit_cast is available. | 123 | 1.90M | constexpr auto num_out_bytes = std::size(r.bytes); | 124 | 1.90M | const auto num_in_bytes = s.length() / 2; | 125 | 1.90M | if (num_in_bytes > num_out_bytes) | 126 | 0 | return {}; | 127 | 1.90M | if (!from_hex(s.begin(), s.end(), &r.bytes[num_out_bytes - num_in_bytes])) | 128 | 0 | return {}; | 129 | 1.90M | return r; | 130 | 1.90M | } |
|
131 | | |
132 | | /// Decodes hex encoded string to bytes. The whitespace in the input is ignored. |
133 | | /// |
134 | | /// In case the input is invalid the returned value is std::nullopt. |
135 | | /// This can happen if a non-hex digit or odd number of digits is encountered. |
136 | | /// The whitespace (as defined by std::isspace) in the input is ignored. |
137 | | template <typename InputIterator> |
138 | | std::optional<bytes> from_spaced_hex(InputIterator begin, InputIterator end) noexcept |
139 | 0 | { |
140 | 0 | bytes bs; |
141 | 0 | if (!from_hex(skip_space_iterator{begin, end}, skip_space_iterator{end, end}, |
142 | 0 | std::back_inserter(bs))) |
143 | 0 | return {}; |
144 | 0 | return bs; |
145 | 0 | } |
146 | | |
147 | | /// @copydoc from_spaced_hex |
148 | | inline std::optional<bytes> from_spaced_hex(std::string_view hex) noexcept |
149 | 0 | { |
150 | 0 | return from_spaced_hex(hex.begin(), hex.end()); |
151 | 0 | } |
152 | | } // namespace evmc |