#include <iostream>
#include <vector>
#include <iomanip>
#include <numeric>
template<typename byte_type = std::uint8_t, typename container_type = std::vector<std::vector<byte_type>>>
container_type arrange_bytes(const byte_type* buffer, const std::size_t size, const std::size_t w = 16) {
return std::accumulate(buffer, buffer + size, container_type{{}}, [w](auto& acc, const byte_type byte) {
if(acc.back().size() >= w) {
acc.push_back({});
}
acc.back().push_back(byte);
return acc;
});
}
std::string init_text_row(const int offset) {
std::ostringstream ost{};
ost << std::hex << std::setfill('0') << std::setw(8) << offset;
return ost.str();
}
template<typename byte_type = std::uint8_t>
std::string format_row(const std::vector<byte_type>& bytes, const int offset) {
auto init = init_text_row(offset);
return std::accumulate(bytes.begin(), bytes.end(), init, [](auto& acc, const auto& byte) {
std::ostringstream ost{};
ost << ' ' << std::hex << std::setfill('0') << static_cast<unsigned>(byte);
return acc + ost.str();
});
}
template<typename byte_type = std::uint8_t, typename container_type = std::vector<std::vector<byte_type>>>
std::string format_bytes(const container_type& bytes) {
struct memory {
std::string data = {};
int offset = 0;
};
return std::accumulate(bytes.begin(), bytes.end(), memory{}, [](auto& acc, const auto& row) {
acc.data += format_row(row, acc.offset) + '\n';
acc.offset += row.size();
return acc;
}).data;
}
template<typename byte_type = std::uint8_t>
std::string hexdump(const byte_type* buffer, std::size_t size) {
return format_bytes(arrange_bytes(buffer, size));
}
#include <cstring>
int main() {
const auto* message = "Hello, world! I am Simon the Sourcerer and I am a mighty pirate!";
const auto len = std::strlen(message);
std::cout << hexdump(message, len) << std::endl;
return 0;
}