| #pragma once |
| |
| #include <algorithm> |
| #include <array> |
| #include <cstddef> |
| #include <cstdint> |
| #include <string> |
| #include <vector> |
| |
| static constexpr std::array<char, 16> digitsArray = { |
| '0', '1', '2', '3', '4', '5', '6', '7', |
| '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; |
| |
| inline std::string intToHexString(uint64_t value, size_t digits) { |
| std::string rc(digits, '0'); |
| size_t bitIndex = (digits - 1) * 4; |
| for (size_t digitIndex = 0; digitIndex < digits; digitIndex++) { |
| rc[digitIndex] = digitsArray[(value >> bitIndex) & 0x0f]; |
| bitIndex -= 4; |
| } |
| return rc; |
| } |
| |
| inline std::string bytesToHexString(const std::vector<uint8_t>& bytes) { |
| std::string rc(bytes.size() * 2, '0'); |
| for (size_t i = 0; i < bytes.size(); ++i) { |
| rc[i * 2] = digitsArray[(bytes[i] & 0xf0) >> 4]; |
| rc[i * 2 + 1] = digitsArray[bytes[i] & 0x0f]; |
| } |
| return rc; |
| } |
| |
| // Returns nibble. |
| inline uint8_t hexCharToNibble(char ch) { |
| uint8_t rc = 16; |
| if (ch >= '0' && ch <= '9') { |
| rc = static_cast<uint8_t>(ch - '0'); |
| } else if (ch >= 'A' && ch <= 'F') { |
| rc = static_cast<uint8_t>(ch - 'A' + 10); |
| } else if (ch >= 'a' && ch <= 'f') { |
| rc = static_cast<uint8_t>(ch - 'a' + 10); |
| } |
| |
| return rc; |
| } |
| |
| // Returns empty vector in case of malformed hex-string. |
| inline std::vector<uint8_t> hexStringToBytes(const std::string& str) { |
| std::vector<uint8_t> rc(str.size() / 2, 0); |
| for (size_t i = 0; i < str.length(); i += 2) { |
| uint8_t hi = hexCharToNibble(str[i]); |
| if (i == str.length() - 1) { |
| return {}; |
| } |
| uint8_t lo = hexCharToNibble(str[i + 1]); |
| if (lo == 16 || hi == 16) { |
| return {}; |
| } |
| |
| rc[i / 2] = static_cast<uint8_t>(hi << 4) | lo; |
| } |
| return rc; |
| } |
| |
| inline std::string bytesToHexDump(const std::vector<uint8_t>& bytes) { |
| std::string hex_dump; |
| if (bytes.empty()) { |
| return hex_dump; |
| } |
| const size_t rowLength = 16; |
| for (size_t i = 0; i < bytes.size(); i += rowLength) { |
| // Offset |
| hex_dump += intToHexString(i, 4); |
| hex_dump += ": "; |
| |
| // Hex bytes |
| size_t chunkLength = std::min(rowLength, bytes.size() - i); |
| for (size_t j = 0; j < rowLength; ++j) { |
| if (j < chunkLength) { |
| uint8_t byte = bytes[i + j]; |
| // Bitwise AND 0xf0 extracts the top 4 bits. |
| // Shifting right by 4 puts those bits in the 0-15 range. |
| // digitsArray maps 0-15 to the corresponding hex char '0'-'F'. |
| hex_dump += digitsArray[(byte & 0xf0) >> 4]; |
| // Bitwise AND 0x0f extracts the bottom 4 bits. |
| hex_dump += digitsArray[byte & 0x0f]; |
| } else { |
| // If we don't have bytes remaining to pad to the end of the line, |
| // we print two spaces to match the two hex characters of a printed byte |
| // and preserve the ASCII representation alignment. |
| hex_dump += " "; |
| } |
| |
| // Formatting spaces. |
| if (j == 7) { |
| // For readability, add two spaces in the middle of each 16-byte row. |
| hex_dump += " "; |
| } else if (j < rowLength - 1) { |
| // Space between bytes to separate them. |
| hex_dump += ' '; |
| } |
| } |
| |
| // Separator between the hex and ASCII representation blocks. |
| hex_dump += " | "; |
| |
| // ASCII representation snippet mapping byte values to characters |
| for (size_t j = 0; j < chunkLength; ++j) { |
| uint8_t byte = bytes[i + j]; |
| // Range 32 to 126 contains the standard printable ASCII characters. |
| // Control characters (0-31, 127) and extended ASCII (128+) |
| // are omitted since they will either not print correctly or will mess up |
| // the terminal output. |
| if (byte >= 32 && byte <= 126) { |
| // Cast the uint8_t byte to char. This is required so that the string's |
| // `operator+=` appends the character representation instead of possibly |
| // picking an overload that formats the integer. |
| hex_dump += static_cast<char>(byte); |
| } else { |
| // If the character is not printable, we append a safe placeholder '.' |
| // to maintain the alignment and clearly show unprintable data. |
| hex_dump += '.'; |
| } |
| } |
| hex_dump += " |\n"; |
| } |
| return hex_dump; |
| } |