mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
159 lines
4.8 KiB
C++
159 lines
4.8 KiB
C++
|
// EVMC: Ethereum Client-VM Connector API.
|
||
|
// Copyright 2021 The EVMC Authors.
|
||
|
// Licensed under the Apache License, Version 2.0.
|
||
|
#pragma once
|
||
|
|
||
|
#include <evmc/filter_iterator.hpp>
|
||
|
#include <cstdint>
|
||
|
#include <optional>
|
||
|
#include <string>
|
||
|
#include <string_view>
|
||
|
|
||
|
namespace evmc
|
||
|
{
|
||
|
/// String of uint8_t chars.
|
||
|
using bytes = std::basic_string<uint8_t>;
|
||
|
|
||
|
/// String view of uint8_t chars.
|
||
|
using bytes_view = std::basic_string_view<uint8_t>;
|
||
|
|
||
|
|
||
|
/// Encode a byte to a hex string.
|
||
|
inline std::string hex(uint8_t b) noexcept
|
||
|
{
|
||
|
static constexpr auto hex_digits = "0123456789abcdef";
|
||
|
return {hex_digits[b >> 4], hex_digits[b & 0xf]};
|
||
|
}
|
||
|
|
||
|
/// Encodes bytes as hex string.
|
||
|
inline std::string hex(bytes_view bs)
|
||
|
{
|
||
|
std::string str;
|
||
|
str.reserve(bs.size() * 2);
|
||
|
for (const auto b : bs)
|
||
|
str += hex(b);
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
namespace internal
|
||
|
{
|
||
|
/// Extracts the nibble value out of a hex digit.
|
||
|
/// Returns -1 in case of invalid hex digit.
|
||
|
inline constexpr int from_hex_digit(char h) noexcept
|
||
|
{
|
||
|
if (h >= '0' && h <= '9')
|
||
|
return h - '0';
|
||
|
else if (h >= 'a' && h <= 'f')
|
||
|
return h - 'a' + 10;
|
||
|
else if (h >= 'A' && h <= 'F')
|
||
|
return h - 'A' + 10;
|
||
|
else
|
||
|
return -1;
|
||
|
}
|
||
|
} // namespace internal
|
||
|
|
||
|
/// Decodes hex-encoded sequence of characters.
|
||
|
///
|
||
|
/// It is guaranteed that the output will not be longer than half of the input length.
|
||
|
///
|
||
|
/// @param begin The input begin iterator. It only must satisfy input iterator concept.
|
||
|
/// @param end The input end iterator. It only must satisfy input iterator concept.
|
||
|
/// @param out The output iterator. It must satisfy output iterator concept.
|
||
|
/// @return True if successful, false if input is invalid hex.
|
||
|
template <typename InputIt, typename OutputIt>
|
||
|
inline constexpr bool from_hex(InputIt begin, InputIt end, OutputIt out) noexcept
|
||
|
{
|
||
|
int hi_nibble = -1; // Init with invalid value, should never be used.
|
||
|
size_t i = 0;
|
||
|
for (auto it = begin; it != end; ++it, ++i)
|
||
|
{
|
||
|
const auto h = *it;
|
||
|
const int v = evmc::internal::from_hex_digit(h);
|
||
|
if (v < 0)
|
||
|
{
|
||
|
if (i == 1 && hi_nibble == 0 && h == 'x') // 0x prefix
|
||
|
continue;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (i % 2 == 0)
|
||
|
hi_nibble = v << 4;
|
||
|
else
|
||
|
*out++ = static_cast<uint8_t>(hi_nibble | v);
|
||
|
}
|
||
|
|
||
|
return i % 2 == 0;
|
||
|
}
|
||
|
|
||
|
/// Validates hex encoded string.
|
||
|
///
|
||
|
/// @return True if the input is valid hex.
|
||
|
inline bool validate_hex(std::string_view hex) noexcept
|
||
|
{
|
||
|
struct noop_output_iterator
|
||
|
{
|
||
|
uint8_t sink = {};
|
||
|
uint8_t& operator*() noexcept { return sink; }
|
||
|
noop_output_iterator operator++(int) noexcept { return *this; } // NOLINT(cert-dcl21-cpp)
|
||
|
};
|
||
|
|
||
|
return from_hex(hex.begin(), hex.end(), noop_output_iterator{});
|
||
|
}
|
||
|
|
||
|
/// Decodes hex encoded string to bytes.
|
||
|
///
|
||
|
/// In case the input is invalid the returned value is std::nullopt.
|
||
|
/// This can happen if a non-hex digit or odd number of digits is encountered.
|
||
|
inline std::optional<bytes> from_hex(std::string_view hex)
|
||
|
{
|
||
|
bytes bs;
|
||
|
bs.reserve(hex.size() / 2);
|
||
|
if (!from_hex(hex.begin(), hex.end(), std::back_inserter(bs)))
|
||
|
return {};
|
||
|
return bs;
|
||
|
}
|
||
|
|
||
|
/// Decodes hex-encoded string into custom type T with .bytes array of uint8_t.
|
||
|
///
|
||
|
/// When the input is smaller than the result type, the result is padded with zeros on the left
|
||
|
/// (the result bytes of lowest indices are filled with zeros).
|
||
|
/// TODO: Support optional left alignment.
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/// Decodes hex encoded string to bytes. The whitespace in the input is ignored.
|
||
|
///
|
||
|
/// In case the input is invalid the returned value is std::nullopt.
|
||
|
/// This can happen if a non-hex digit or odd number of digits is encountered.
|
||
|
/// The whitespace (as defined by std::isspace) in the input is ignored.
|
||
|
template <typename InputIterator>
|
||
|
std::optional<bytes> from_spaced_hex(InputIterator begin, InputIterator end) noexcept
|
||
|
{
|
||
|
bytes bs;
|
||
|
if (!from_hex(skip_space_iterator{begin, end}, skip_space_iterator{end, end},
|
||
|
std::back_inserter(bs)))
|
||
|
return {};
|
||
|
return bs;
|
||
|
}
|
||
|
|
||
|
/// @copydoc from_spaced_hex
|
||
|
inline std::optional<bytes> from_spaced_hex(std::string_view hex) noexcept
|
||
|
{
|
||
|
return from_spaced_hex(hex.begin(), hex.end());
|
||
|
}
|
||
|
} // namespace evmc
|