diff options
Diffstat (limited to 'src/util/strencodings.h')
-rw-r--r-- | src/util/strencodings.h | 81 |
1 files changed, 77 insertions, 4 deletions
diff --git a/src/util/strencodings.h b/src/util/strencodings.h index e5c2d3ddf2..1543de03ab 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -13,6 +13,8 @@ #include <span.h> #include <util/string.h> +#include <array> +#include <bit> #include <charconv> #include <cstddef> #include <cstdint> @@ -70,10 +72,6 @@ std::vector<Byte> ParseHex(std::string_view hex_str) /* Returns true if each character in str is a hex character, and has an even * number of hex digits.*/ bool IsHex(std::string_view str); -/** -* Return true if the string is a hex number, optionally prefixed with "0x" -*/ -bool IsHexNumber(std::string_view str); std::optional<std::vector<unsigned char>> DecodeBase64(std::string_view str); std::string EncodeBase64(Span<const unsigned char> input); inline std::string EncodeBase64(Span<const std::byte> input) { return EncodeBase64(MakeUCharSpan(input)); } @@ -369,4 +367,79 @@ std::string Capitalize(std::string str); */ std::optional<uint64_t> ParseByteUnits(std::string_view str, ByteUnit default_multiplier); +namespace util { +/** consteval version of HexDigit() without the lookup table. */ +consteval uint8_t ConstevalHexDigit(const char c) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 0xa; + + throw "Only lowercase hex digits are allowed, for consistency"; +} + +/** + * ""_hex is a compile-time user-defined literal returning a + * `std::array<std::byte>`, equivalent to ParseHex(). Variants provided: + * + * - ""_hex_v: Returns `std::vector<std::byte>`, useful for heap allocation or + * variable-length serialization. + * + * - ""_hex_u8: Returns `std::array<uint8_t>`, for cases where `std::byte` is + * incompatible. + * + * - ""_hex_v_u8: Returns `std::vector<uint8_t>`, combining heap allocation with + * `uint8_t`. + * + * @warning It could be necessary to use vector instead of array variants when + * serializing, or vice versa, because vectors are assumed to be variable- + * length and serialized with a size prefix, while arrays are considered fixed + * length and serialized with no prefix. + * + * @warning It may be preferable to use vector variants to save stack space when + * declaring local variables if hex strings are large. Alternatively variables + * could be declared constexpr to avoid using stack space. + * + * @warning Avoid `uint8_t` variants when not necessary, as the codebase + * migrates to use `std::byte` instead of `unsigned char` and `uint8_t`. + * + * @note One reason ""_hex uses `std::array` instead of `std::vector` like + * ParseHex() does is because heap-based containers cannot cross the compile- + * time/runtime barrier. + */ +inline namespace hex_literals { +namespace detail { + +template <size_t N> +struct Hex { + std::array<std::byte, N / 2> bytes{}; + consteval Hex(const char (&hex_str)[N]) + // 2 hex digits required per byte + implicit null terminator + requires(N % 2 == 1) + { + if (hex_str[N - 1]) throw "null terminator required"; + for (std::size_t i = 0; i < bytes.size(); ++i) { + bytes[i] = static_cast<std::byte>( + (ConstevalHexDigit(hex_str[2 * i]) << 4) | + ConstevalHexDigit(hex_str[2 * i + 1])); + } + } +}; + +} // namespace detail + +template <util::detail::Hex str> +constexpr auto operator""_hex() { return str.bytes; } + +template <util::detail::Hex str> +constexpr auto operator""_hex_u8() { return std::bit_cast<std::array<uint8_t, str.bytes.size()>>(str.bytes); } + +template <util::detail::Hex str> +constexpr auto operator""_hex_v() { return std::vector<std::byte>{str.bytes.begin(), str.bytes.end()}; } + +template <util::detail::Hex str> +inline auto operator""_hex_v_u8() { return std::vector<uint8_t>{UCharCast(str.bytes.data()), UCharCast(str.bytes.data() + str.bytes.size())}; } + +} // inline namespace hex_literals +} // namespace util + #endif // BITCOIN_UTIL_STRENCODINGS_H |