This is library for fast converting "HEX strings" to unsigned numbers and vice versa.
The result is not defined, if input is incorrect, e.g. string "ZZZ1" will be converted to number 1.
Special attention is made the conversion to string to work inside existing buffer (see ">>>:0000:<<<"
). In this case the programmer is responsible to provide enough space in the buffer.
#include <cstdint>
#include <type_traits>
#include <array>
#include <string_view>
namespace hex_convert{
namespace options{
constexpr uint8_t nonterminate = 0b0000;
constexpr uint8_t terminate = 0b0001;
constexpr uint8_t lowercase = 0b0000;
constexpr uint8_t uppercase = 0b0010;
constexpr uint8_t defaults = terminate | uppercase;
}
template <typename T, uint8_t opt = options::defaults>
std::string_view toHex(T const number, char *buffer){
static_assert(
std::is_same_v<T, uint8_t > ||
std::is_same_v<T, uint16_t> ||
std::is_same_v<T, uint32_t> ||
std::is_same_v<T, uint64_t>
);
constexpr const char *digits = [](){
if constexpr(opt & options::uppercase)
return "0123456789ABCDEF";
else
return "0123456789abcdef";
}();
constexpr std::size_t size = sizeof(T) * 2;
if constexpr(opt & options::terminate)
buffer[size] = '\0';
for (std::size_t i = 0; i < size; ++i){
auto const index = (number >> (4 * i)) & 0xF;
buffer[size - 1 - i] = digits[index];
}
return { buffer, size };
}
template <typename T, uint8_t opt = options::defaults, size_t N>
std::string_view toHex(T const number, std::array<char, N> &buffer){
static_assert(
std::is_same_v<T, uint8_t > ||
std::is_same_v<T, uint16_t> ||
std::is_same_v<T, uint32_t> ||
std::is_same_v<T, uint64_t>
);
static_assert(N > sizeof(T) + (opt & options::terminate ? 1 : 0));
return toHex<T, opt>(number, buffer.data());
}
template <typename T>
auto fromHex(std::string_view const hex){
static_assert(
std::is_same_v<T, uint8_t > ||
std::is_same_v<T, uint16_t> ||
std::is_same_v<T, uint32_t> ||
std::is_same_v<T, uint64_t>
);
auto _ = [](char c) -> T{
switch(c){
default:
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'A': case 'a': return 10;
case 'B': case 'b': return 11;
case 'C': case 'c': return 12;
case 'D': case 'd': return 13;
case 'E': case 'e': return 14;
case 'F': case 'f': return 15;
}
};
T val = 0;
for(auto const &c : hex){
T const nibble = _(c);
val = (val << 4) | nibble;
}
return val;
}
}
#include <iostream>
constexpr size_t to_string_buffer_t_size = 32; // largest uint64_t is 20 digits.
using to_string_buffer_t = std::array<char, to_string_buffer_t_size>;
int main(){
namespace о = hex_convert::options;
if constexpr(1){
to_string_buffer_t buffer;
printf("%s\n", hex_convert::toHex<uint64_t, о::terminate | о::lowercase>(0x00DEADBEEF, buffer).data());
printf("%s\n", hex_convert::toHex<uint64_t, о::terminate | о::uppercase>(0x00DEADBEEF, buffer).data());
printf("%s\n", hex_convert::toHex<uint32_t, о::terminate | о::lowercase>(0x00DEADBEEF, buffer).data());
printf("%s\n", hex_convert::toHex<uint32_t, о::terminate | о::uppercase>(0x00DEADBEEF, buffer).data());
}
if constexpr(1){
char x[200] = { ">>>:0000:<<<" };
hex_convert::toHex<uint16_t, о::nonterminate | о::lowercase>(0xABBA, x + 4);
printf("%s\n", x);
hex_convert::toHex<uint16_t, о::nonterminate | о::uppercase>(0xABBA, x + 4);
printf("%s\n", x);
}
if constexpr(1){
printf("0x%16X\n", hex_convert::fromHex<uint8_t >("F1"));
printf("0x%16X\n", hex_convert::fromHex<uint16_t>("ABBA"));
printf("0x%16X\n", hex_convert::fromHex<uint32_t>("DEADBEEF"));
printf("0x%16lX\n", hex_convert::fromHex<uint64_t>("1122334455667788"));
}
}