Example
#include <bit>
int main() {
constexpr auto value = std::uint16_t(0xCAFE);
std::cout << std::hex << value; // pritns cafe
std::cout << std::hex << std::byteswap(value); // prints feca
}
Puzzle
- Can you implement
bytes_to_string
(using std::byteswap) which converts given value to its string bytes respresntation?
[[nodiscard]] constexpr auto bytes_to_string(std::integral auto value) -> std::string{
return {}; // TODO
}
int main(){
using namespace boost::ut;
using std::string_literals::operator""s;
"bytes to string"_test = [] {
expect("00000000 : 00 00 00 00 "s == bytes_to_string(0));
expect("00000001 : 00 00 00 01 "s == bytes_to_string(1));
expect("0000CAFE : 00 00 CA FE "s == bytes_to_string(0xCAFE));
expect("0000BABA : 00 00 BA BA "s == bytes_to_string(0xBABA));
};
}
Solutions
[[nodiscard]] constexpr auto bytes_to_string(std::integral auto value) -> std::string {
constexpr auto size = sizeof(value) * 2 + 3 + sizeof(value) * 3;
std::array<char, size> output{};
output.fill(' ');
output[sizeof(value) * 2 + 1] = ':';
constexpr auto to_hex = [] (const unsigned char nybble) -> char {
return "0123456789ABCDEF"[nybble];
};
const auto bs_value = std::byteswap(value);
const auto first = reinterpret_cast<const unsigned char*>(std::addressof(bs_value));
const auto last = first + sizeof(bs_value);
auto dest1 = std::begin(output);
auto dest2 = std::next(dest1, sizeof(bs_value) * 2 + 3);
for (auto f = first; f != last; ++f) {
*dest1 = to_hex(*f >> 4);
*dest2++ = *dest1++;
*dest1 = to_hex(*f & 0xf);
*dest2++ = *dest1++;
++dest2;
}
return std::string{std::data(output), size};
}
[[nodiscard]] auto bytes_to_string(std::integral auto value) -> std::string {
std::stringstream str{};
str << std::hex << std::uppercase << std::setfill('0')
<< std::setw(sizeof(value) * 2) << value << " : ";
value = std::byteswap(value);
for (auto i = 0u; i != sizeof(value); ++i, value >>= 8) {
str << std::setw(2) << (0xFFu & value) << ' ';
}
return str.str();
}
[[nodiscard]] constexpr auto bytes_to_string(std::integral auto value) -> std::string{
char digits[] = "0123456789ABCDEF";
char ret[] = "00000000 : 00 00 00 00 ";
for(int i=0; i<8; i++) {
ret[21-(i+i/2)] = ret[7-i] = digits[value & 0xF];
value >>= 4;
}
return ret;
}
[[nodiscard]] auto convert_to_string(std::integral auto value) -> std::string {
static constexpr auto MAX_BYTES = 4;
static_assert(sizeof(value) <= MAX_BYTES,
"This function is not setup to print 64b values");
std::array<uint8_t, MAX_BYTES> bytes{};
std::memcpy(&bytes, &value, sizeof(value));
return fmt::format("{1:0{0}X} : {2:02X} {3:02X} {4:02X} {5:02X}",
MAX_BYTES * 2, value, bytes[3], bytes[2], bytes[1],
bytes[0]);
}
} // namespace detail
[[nodiscard]] constexpr auto bytes_to_string(std::integral auto value)
-> std::string {
if constexpr (std::endian::native == std::endian::big) {
return detail::convert_to_string(std::byteswap(value));
} else {
return detail::convert_to_string(value);
}
}
[[nodiscard]] constexpr auto bytes_to_string(std::integral auto value) -> std::string{
value = std::byteswap(value);
const BYTE d = (value & 0xFF); //extract first byte
const BYTE c = ((value >> 8) & 0xFF); //extract second byte
const BYTE b = ((value >> 16) & 0xFF); //extract third byte
const BYTE a = ((value >> 24) & 0xFF); //extract fourth byte
char buffer[23];
unsigned int len = sprintf(buffer,"%02X%02X%02X%02X : %02X %02X %02X %02X", d, c, b, a, d, c, b, a);
buffer[len] = ' ';
std::string out_str(buffer);
return out_str;
}
[[nodiscard]] constexpr auto bytes_to_string(std::integral auto value)
-> std::string {
return [=]<auto... Is>(std::index_sequence<Is...>) {
constexpr auto to_hex = [](std::integral auto value) {
return fmt::format("{:0>{}X} ", value, 2 * sizeof(value));
};
const auto array = std::bit_cast<std::array<std::uint8_t, sizeof(value)>>(
std::byteswap(value));
return ((to_hex(value) + ": ") + ... + to_hex(array[Is]));
}
(std::make_index_sequence<sizeof(value)>{});
}
[[nodiscard]] constexpr auto bytes_to_string(std::integral auto value) -> std::string{
std::string str{"00000000 : 00 00 00 00 "};
auto nibblesToChars = [](unsigned char byte) {
byte &= 0x0F;
byte += (byte < 0x0A) ? '0' : '7';
return byte;
};
auto insertCharIntoOutputString = [&](auto index, auto nibble_offset, char ch){
str[(index*2)+nibble_offset ] = ch;
str[(index*3)+nibble_offset+11] = ch;
};
auto bytesToChars = [&](std::integral auto value){
for(auto ii = 0; ii < 4; ii++){
insertCharIntoOutputString(ii, 0, nibblesToChars(value>>4));
insertCharIntoOutputString(ii, 1, nibblesToChars(value ));
value >>= 8;
}
};
bytesToChars(std::byteswap(value));
return str;
}
template<typename InputIt>
std::string join(InputIt first, InputIt last, const std::string& separator = ",") {
std::ostringstream result;
if (first != last) {
result << *first;
while (++first != last) {
result << separator << *first;
}
}
return result.str();
}
auto extract_byte(auto number, auto n){
int x = (number >> (8*n)) & 0xff;
return x;
}
auto to_hex(auto value) {
std::string s = fmt::format("{:02X}", value);
return s;
}
auto swap_bytes = [](const auto x) {return std::optional{ std::byteswap(x) };};
template<int size=2>
auto split_bytes = [](auto x){
std::array<std::string, size> bytes{};
for( auto i = 0; i<size; ++i) {
bytes[i] = to_hex(extract_byte(x, i));
}
return std::optional{bytes};
};
auto make_output = [](auto x){
std::string combined_bytes = join(std::begin(x), std::end(x), "");
std::string split_bytes = join(std::begin(x), std::end(x), " ");
return std::optional{ fmt::format("{} : {}", combined_bytes, split_bytes) };
};
[[nodiscard]] constexpr auto bytes_to_string(std::integral auto value) -> std::string{
return std::optional{value}
.and_then(swap_bytes)
.and_then(split_bytes<sizeof(value)>)
.and_then(make_output).value();
}
[[nodiscard]] constexpr auto bytes_to_string(std::integral auto value) -> std::string{
uint8_t bytes[sizeof(value)];
for(int i=0; i < sizeof(value); i++){
bytes[i] = value%16 << 4;
value = value/16;
bytes[i] |= value%16;
value = value/16;
}
decltype(value) *x = reinterpret_cast<decltype(value) *>(bytes);
*x = std::byteswap(*x);
std::string s1{""};
std::string s2{""};
const char* digits = "0123456789ABCDEF";
for(int i=0; i < sizeof(value); i++){
s1 += digits[bytes[i] & 0x0F];
s1 += digits[bytes[i] >> 4 & 0x0F];
s2 += digits[bytes[i] & 0x0F];
s2 += digits[bytes[i] >> 4 & 0x0F];
s2 += " ";
}
return s1 + " : " + s2;
}
[[nodiscard]] constexpr auto bytes_to_string(std::integral auto value) -> std::string {
constexpr auto n_bytes = sizeof(value);
const auto bytes = std::bit_cast<std::array<std::uint8_t, n_bytes>>(std::byteswap(value));
return fmt::format("{:02X} : {:02X} ", fmt::join(bytes, ""), fmt::join(bytes, " "));
}