// Copyright (c) 2019-2021, The Oxen Project // // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. // // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #include #include #include #include #include #include "byte_type.h" namespace oxenmq { namespace detail { /// Compile-time generated lookup tables hex conversion struct hex_table { char from_hex_lut[256]; char to_hex_lut[16]; constexpr hex_table() noexcept : from_hex_lut{}, to_hex_lut{} { for (unsigned char c = 0; c < 10; c++) { from_hex_lut[(unsigned char)('0' + c)] = 0 + c; to_hex_lut[ (unsigned char)( 0 + c)] = '0' + c; } for (unsigned char c = 0; c < 6; c++) { from_hex_lut[(unsigned char)('a' + c)] = 10 + c; from_hex_lut[(unsigned char)('A' + c)] = 10 + c; to_hex_lut[ (unsigned char)(10 + c)] = 'a' + c; } } constexpr char from_hex(unsigned char c) const noexcept { return from_hex_lut[c]; } constexpr char to_hex(unsigned char b) const noexcept { return to_hex_lut[b]; } } constexpr hex_lut; // This main point of this static assert is to force the compiler to compile-time build the constexpr tables. static_assert(hex_lut.from_hex('a') == 10 && hex_lut.from_hex('F') == 15 && hex_lut.to_hex(13) == 'd', ""); } // namespace detail /// Returns the number of characters required to encode a hex string from the given number of bytes. inline constexpr size_t to_hex_size(size_t byte_size) { return byte_size * 2; } /// Returns the number of bytes required to decode a hex string of the given size. inline constexpr size_t from_hex_size(size_t hex_size) { return hex_size / 2; } /// Iterable object for on-the-fly hex encoding. Used internally, but also particularly useful when /// converting from one encoding to another. template struct hex_encoder final { private: InputIt _it, _end; static_assert(sizeof(decltype(*_it)) == 1, "hex_encoder requires chars/bytes input iterator"); uint8_t c; bool second_half = false; public: using iterator_category = std::input_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = char; using reference = value_type; using pointer = void; hex_encoder(InputIt begin, InputIt end) : _it{std::move(begin)}, _end{std::move(end)} {} hex_encoder end() { return {_end, _end}; } bool operator==(const hex_encoder& i) { return _it == i._it && second_half == i.second_half; } bool operator!=(const hex_encoder& i) { return !(*this == i); } hex_encoder& operator++() { second_half = !second_half; if (!second_half) ++_it; return *this; } hex_encoder operator++(int) { hex_encoder copy{*this}; ++*this; return copy; } char operator*() { return detail::hex_lut.to_hex(second_half ? c & 0x0f : (c = static_cast(*_it)) >> 4); } }; /// Creates hex digits from a character sequence given by iterators, writes them starting at `out`. /// Returns the final value of out (i.e. the iterator positioned just after the last written /// hex character). template OutputIt to_hex(InputIt begin, InputIt end, OutputIt out) { static_assert(sizeof(decltype(*begin)) == 1, "to_hex requires chars/bytes"); auto it = hex_encoder{begin, end}; return std::copy(it, it.end(), out); } /// Creates a string of hex digits from a character sequence iterator pair template std::string to_hex(It begin, It end) { std::string hex; if constexpr (std::is_base_of_v::iterator_category>) { using std::distance; hex.reserve(to_hex_size(distance(begin, end))); } to_hex(begin, end, std::back_inserter(hex)); return hex; } /// Creates a hex string from an iterable, std::string-like object template std::string to_hex(std::basic_string_view s) { return to_hex(s.begin(), s.end()); } inline std::string to_hex(std::string_view s) { return to_hex<>(s); } /// Returns true if the given value is a valid hex digit. template constexpr bool is_hex_digit(CharT c) { static_assert(sizeof(CharT) == 1, "is_hex requires chars/bytes"); return detail::hex_lut.from_hex(static_cast(c)) != 0 || static_cast(c) == '0'; } /// Returns true if all elements in the range are hex characters *and* the string length is a /// multiple of 2, and thus suitable to pass to from_hex(). template constexpr bool is_hex(It begin, It end) { static_assert(sizeof(decltype(*begin)) == 1, "is_hex requires chars/bytes"); constexpr bool ra = std::is_base_of_v::iterator_category>; if constexpr (ra) { using std::distance; if (distance(begin, end) % 2 != 0) return false; } size_t count = 0; for (; begin != end; ++begin) { if constexpr (!ra) ++count; if (!is_hex_digit(*begin)) return false; } if constexpr (!ra) return count % 2 == 0; return true; } /// Returns true if all elements in the string-like value are hex characters template constexpr bool is_hex(std::basic_string_view s) { return is_hex(s.begin(), s.end()); } constexpr bool is_hex(std::string_view s) { return is_hex(s.begin(), s.end()); } /// Convert a hex digit into its numeric (0-15) value constexpr char from_hex_digit(unsigned char x) noexcept { return detail::hex_lut.from_hex(x); } /// Constructs a byte value from a pair of hex digits constexpr char from_hex_pair(unsigned char a, unsigned char b) noexcept { return (from_hex_digit(a) << 4) | from_hex_digit(b); } /// Iterable object for on-the-fly hex decoding. Used internally but also particularly useful when /// converting from one encoding to another. Undefined behaviour if the given iterator range is not /// a valid hex string with even length (i.e. is_hex() should return true). template struct hex_decoder final { private: InputIt _it, _end; static_assert(sizeof(decltype(*_it)) == 1, "hex_encoder requires chars/bytes input iterator"); char byte; public: using iterator_category = std::input_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = char; using reference = value_type; using pointer = void; hex_decoder(InputIt begin, InputIt end) : _it{std::move(begin)}, _end{std::move(end)} { if (_it != _end) load_byte(); } hex_decoder end() { return {_end, _end}; } bool operator==(const hex_decoder& i) { return _it == i._it; } bool operator!=(const hex_decoder& i) { return _it != i._it; } hex_decoder& operator++() { if (++_it != _end) load_byte(); return *this; } hex_decoder operator++(int) { hex_decoder copy{*this}; ++*this; return copy; } char operator*() const { return byte; } private: void load_byte() { auto a = *_it; auto b = *++_it; byte = from_hex_pair(static_cast(a), static_cast(b)); } }; /// Converts a sequence of hex digits to bytes. Undefined behaviour if any characters are not in /// [0-9a-fA-F] or if the input sequence length is not even: call `is_hex` first if you need to /// check. It is permitted for the input and output ranges to overlap as long as out is no later /// than begin. Returns the final value of out (that is, the iterator positioned just after the /// last written character). template OutputIt from_hex(InputIt begin, InputIt end, OutputIt out) { assert(is_hex(begin, end)); auto it = hex_decoder(begin, end); const auto hend = it.end(); while (it != hend) *out++ = static_cast>(*it++); return out; } /// Converts a sequence of hex digits to a string of bytes and returns it. Undefined behaviour if /// the input sequence is not an even-length sequence of [0-9a-fA-F] characters. template std::string from_hex(It begin, It end) { std::string bytes; if constexpr (std::is_base_of_v::iterator_category>) { using std::distance; bytes.reserve(from_hex_size(distance(begin, end))); } from_hex(begin, end, std::back_inserter(bytes)); return bytes; } /// Converts hex digits from a std::string-like object into a std::string of bytes. Undefined /// behaviour if any characters are not in [0-9a-fA-F] or if the input sequence length is not even. template std::string from_hex(std::basic_string_view s) { return from_hex(s.begin(), s.end()); } inline std::string from_hex(std::string_view s) { return from_hex<>(s); } }