[helpers] Improve `format_hex_pretty` (#9380)

This commit is contained in:
Jesse Hills 2025-07-08 13:30:23 +12:00 committed by GitHub
parent a72905191a
commit 256f9f9943
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 178 additions and 42 deletions

View File

@ -258,53 +258,60 @@ std::string format_hex(const uint8_t *data, size_t length) {
std::string format_hex(const std::vector<uint8_t> &data) { return format_hex(data.data(), data.size()); } std::string format_hex(const std::vector<uint8_t> &data) { return format_hex(data.data(), data.size()); }
static char format_hex_pretty_char(uint8_t v) { return v >= 10 ? 'A' + (v - 10) : '0' + v; } static char format_hex_pretty_char(uint8_t v) { return v >= 10 ? 'A' + (v - 10) : '0' + v; }
std::string format_hex_pretty(const uint8_t *data, size_t length) { std::string format_hex_pretty(const uint8_t *data, size_t length, char separator, bool show_length) {
if (length == 0) if (data == nullptr || length == 0)
return ""; return "";
std::string ret; std::string ret;
ret.resize(3 * length - 1); uint8_t multiple = separator ? 3 : 2; // 3 if separator is not \0, 2 otherwise
ret.resize(multiple * length - 1);
for (size_t i = 0; i < length; i++) { for (size_t i = 0; i < length; i++) {
ret[3 * i] = format_hex_pretty_char((data[i] & 0xF0) >> 4); ret[multiple * i] = format_hex_pretty_char((data[i] & 0xF0) >> 4);
ret[3 * i + 1] = format_hex_pretty_char(data[i] & 0x0F); ret[multiple * i + 1] = format_hex_pretty_char(data[i] & 0x0F);
if (i != length - 1) if (separator && i != length - 1)
ret[3 * i + 2] = '.'; ret[multiple * i + 2] = separator;
} }
if (length > 4) if (show_length && length > 4)
return ret + " (" + to_string(length) + ")"; return ret + " (" + std::to_string(length) + ")";
return ret; return ret;
} }
std::string format_hex_pretty(const std::vector<uint8_t> &data) { return format_hex_pretty(data.data(), data.size()); } std::string format_hex_pretty(const std::vector<uint8_t> &data, char separator, bool show_length) {
return format_hex_pretty(data.data(), data.size(), separator, show_length);
}
std::string format_hex_pretty(const uint16_t *data, size_t length) { std::string format_hex_pretty(const uint16_t *data, size_t length, char separator, bool show_length) {
if (length == 0) if (data == nullptr || length == 0)
return ""; return "";
std::string ret; std::string ret;
ret.resize(5 * length - 1); uint8_t multiple = separator ? 5 : 4; // 5 if separator is not \0, 4 otherwise
ret.resize(multiple * length - 1);
for (size_t i = 0; i < length; i++) { for (size_t i = 0; i < length; i++) {
ret[5 * i] = format_hex_pretty_char((data[i] & 0xF000) >> 12); ret[multiple * i] = format_hex_pretty_char((data[i] & 0xF000) >> 12);
ret[5 * i + 1] = format_hex_pretty_char((data[i] & 0x0F00) >> 8); ret[multiple * i + 1] = format_hex_pretty_char((data[i] & 0x0F00) >> 8);
ret[5 * i + 2] = format_hex_pretty_char((data[i] & 0x00F0) >> 4); ret[multiple * i + 2] = format_hex_pretty_char((data[i] & 0x00F0) >> 4);
ret[5 * i + 3] = format_hex_pretty_char(data[i] & 0x000F); ret[multiple * i + 3] = format_hex_pretty_char(data[i] & 0x000F);
if (i != length - 1) if (separator && i != length - 1)
ret[5 * i + 2] = '.'; ret[multiple * i + 4] = separator;
} }
if (length > 4) if (show_length && length > 4)
return ret + " (" + to_string(length) + ")"; return ret + " (" + std::to_string(length) + ")";
return ret; return ret;
} }
std::string format_hex_pretty(const std::vector<uint16_t> &data) { return format_hex_pretty(data.data(), data.size()); } std::string format_hex_pretty(const std::vector<uint16_t> &data, char separator, bool show_length) {
std::string format_hex_pretty(const std::string &data) { return format_hex_pretty(data.data(), data.size(), separator, show_length);
}
std::string format_hex_pretty(const std::string &data, char separator, bool show_length) {
if (data.empty()) if (data.empty())
return ""; return "";
std::string ret; std::string ret;
ret.resize(3 * data.length() - 1); uint8_t multiple = separator ? 3 : 2; // 3 if separator is not \0, 2 otherwise
ret.resize(multiple * data.length() - 1);
for (size_t i = 0; i < data.length(); i++) { for (size_t i = 0; i < data.length(); i++) {
ret[3 * i] = format_hex_pretty_char((data[i] & 0xF0) >> 4); ret[multiple * i] = format_hex_pretty_char((data[i] & 0xF0) >> 4);
ret[3 * i + 1] = format_hex_pretty_char(data[i] & 0x0F); ret[multiple * i + 1] = format_hex_pretty_char(data[i] & 0x0F);
if (i != data.length() - 1) if (separator && i != data.length() - 1)
ret[3 * i + 2] = '.'; ret[multiple * i + 2] = separator;
} }
if (data.length() > 4) if (show_length && data.length() > 4)
return ret + " (" + std::to_string(data.length()) + ")"; return ret + " (" + std::to_string(data.length()) + ")";
return ret; return ret;
} }

View File

@ -344,20 +344,149 @@ template<std::size_t N> std::string format_hex(const std::array<uint8_t, N> &dat
return format_hex(data.data(), data.size()); return format_hex(data.data(), data.size());
} }
/// Format the byte array \p data of length \p len in pretty-printed, human-readable hex. /** Format a byte array in pretty-printed, human-readable hex format.
std::string format_hex_pretty(const uint8_t *data, size_t length); *
/// Format the word array \p data of length \p len in pretty-printed, human-readable hex. * Converts binary data to a hexadecimal string representation with customizable formatting.
std::string format_hex_pretty(const uint16_t *data, size_t length); * Each byte is displayed as a two-digit uppercase hex value, separated by the specified separator.
/// Format the vector \p data in pretty-printed, human-readable hex. * Optionally includes the total byte count in parentheses at the end.
std::string format_hex_pretty(const std::vector<uint8_t> &data); *
/// Format the vector \p data in pretty-printed, human-readable hex. * @param data Pointer to the byte array to format.
std::string format_hex_pretty(const std::vector<uint16_t> &data); * @param length Number of bytes in the array.
/// Format the string \p data in pretty-printed, human-readable hex. * @param separator Character to use between hex bytes (default: '.').
std::string format_hex_pretty(const std::string &data); * @param show_length Whether to append the byte count in parentheses (default: true).
/// Format an unsigned integer in pretty-printed, human-readable hex, starting with the most significant byte. * @return Formatted hex string, e.g., "A1.B2.C3.D4.E5 (5)" or "A1:B2:C3" depending on parameters.
template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> std::string format_hex_pretty(T val) { *
* @note Returns empty string if data is nullptr or length is 0.
* @note The length will only be appended if show_length is true AND the length is greater than 4.
*
* Example:
* @code
* uint8_t data[] = {0xA1, 0xB2, 0xC3};
* format_hex_pretty(data, 3); // Returns "A1.B2.C3" (no length shown for <= 4 parts)
* uint8_t data2[] = {0xA1, 0xB2, 0xC3, 0xD4, 0xE5};
* format_hex_pretty(data2, 5); // Returns "A1.B2.C3.D4.E5 (5)"
* format_hex_pretty(data2, 5, ':'); // Returns "A1:B2:C3:D4:E5 (5)"
* format_hex_pretty(data2, 5, '.', false); // Returns "A1.B2.C3.D4.E5"
* @endcode
*/
std::string format_hex_pretty(const uint8_t *data, size_t length, char separator = '.', bool show_length = true);
/** Format a 16-bit word array in pretty-printed, human-readable hex format.
*
* Similar to the byte array version, but formats 16-bit words as 4-digit hex values.
*
* @param data Pointer to the 16-bit word array to format.
* @param length Number of 16-bit words in the array.
* @param separator Character to use between hex words (default: '.').
* @param show_length Whether to append the word count in parentheses (default: true).
* @return Formatted hex string with 4-digit hex values per word.
*
* @note The length will only be appended if show_length is true AND the length is greater than 4.
*
* Example:
* @code
* uint16_t data[] = {0xA1B2, 0xC3D4};
* format_hex_pretty(data, 2); // Returns "A1B2.C3D4" (no length shown for <= 4 parts)
* uint16_t data2[] = {0xA1B2, 0xC3D4, 0xE5F6};
* format_hex_pretty(data2, 3); // Returns "A1B2.C3D4.E5F6 (3)"
* @endcode
*/
std::string format_hex_pretty(const uint16_t *data, size_t length, char separator = '.', bool show_length = true);
/** Format a byte vector in pretty-printed, human-readable hex format.
*
* Convenience overload for std::vector<uint8_t>. Formats each byte as a two-digit
* uppercase hex value with customizable separator.
*
* @param data Vector of bytes to format.
* @param separator Character to use between hex bytes (default: '.').
* @param show_length Whether to append the byte count in parentheses (default: true).
* @return Formatted hex string representation of the vector contents.
*
* @note The length will only be appended if show_length is true AND the vector size is greater than 4.
*
* Example:
* @code
* std::vector<uint8_t> data = {0xDE, 0xAD, 0xBE, 0xEF};
* format_hex_pretty(data); // Returns "DE.AD.BE.EF" (no length shown for <= 4 parts)
* std::vector<uint8_t> data2 = {0xDE, 0xAD, 0xBE, 0xEF, 0xCA};
* format_hex_pretty(data2); // Returns "DE.AD.BE.EF.CA (5)"
* format_hex_pretty(data2, '-'); // Returns "DE-AD-BE-EF-CA (5)"
* @endcode
*/
std::string format_hex_pretty(const std::vector<uint8_t> &data, char separator = '.', bool show_length = true);
/** Format a 16-bit word vector in pretty-printed, human-readable hex format.
*
* Convenience overload for std::vector<uint16_t>. Each 16-bit word is formatted
* as a 4-digit uppercase hex value in big-endian order.
*
* @param data Vector of 16-bit words to format.
* @param separator Character to use between hex words (default: '.').
* @param show_length Whether to append the word count in parentheses (default: true).
* @return Formatted hex string representation of the vector contents.
*
* @note The length will only be appended if show_length is true AND the vector size is greater than 4.
*
* Example:
* @code
* std::vector<uint16_t> data = {0x1234, 0x5678};
* format_hex_pretty(data); // Returns "1234.5678" (no length shown for <= 4 parts)
* std::vector<uint16_t> data2 = {0x1234, 0x5678, 0x9ABC};
* format_hex_pretty(data2); // Returns "1234.5678.9ABC (3)"
* @endcode
*/
std::string format_hex_pretty(const std::vector<uint16_t> &data, char separator = '.', bool show_length = true);
/** Format a string's bytes in pretty-printed, human-readable hex format.
*
* Treats each character in the string as a byte and formats it in hex.
* Useful for debugging binary data stored in std::string containers.
*
* @param data String whose bytes should be formatted as hex.
* @param separator Character to use between hex bytes (default: '.').
* @param show_length Whether to append the byte count in parentheses (default: true).
* @return Formatted hex string representation of the string's byte contents.
*
* @note The length will only be appended if show_length is true AND the string length is greater than 4.
*
* Example:
* @code
* std::string data = "ABC"; // ASCII: 0x41, 0x42, 0x43
* format_hex_pretty(data); // Returns "41.42.43" (no length shown for <= 4 parts)
* std::string data2 = "ABCDE";
* format_hex_pretty(data2); // Returns "41.42.43.44.45 (5)"
* @endcode
*/
std::string format_hex_pretty(const std::string &data, char separator = '.', bool show_length = true);
/** Format an unsigned integer in pretty-printed, human-readable hex format.
*
* Converts the integer to big-endian byte order and formats each byte as hex.
* The most significant byte appears first in the output string.
*
* @tparam T Unsigned integer type (uint8_t, uint16_t, uint32_t, uint64_t, etc.).
* @param val The unsigned integer value to format.
* @param separator Character to use between hex bytes (default: '.').
* @param show_length Whether to append the byte count in parentheses (default: true).
* @return Formatted hex string with most significant byte first.
*
* @note The length will only be appended if show_length is true AND sizeof(T) is greater than 4.
*
* Example:
* @code
* uint32_t value = 0x12345678;
* format_hex_pretty(value); // Returns "12.34.56.78" (no length shown for <= 4 parts)
* uint64_t value2 = 0x123456789ABCDEF0;
* format_hex_pretty(value2); // Returns "12.34.56.78.9A.BC.DE.F0 (8)"
* format_hex_pretty(value2, ':'); // Returns "12:34:56:78:9A:BC:DE:F0 (8)"
* format_hex_pretty<uint16_t>(0x1234); // Returns "12.34"
* @endcode
*/
template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
std::string format_hex_pretty(T val, char separator = '.', bool show_length = true) {
val = convert_big_endian(val); val = convert_big_endian(val);
return format_hex_pretty(reinterpret_cast<uint8_t *>(&val), sizeof(T)); return format_hex_pretty(reinterpret_cast<uint8_t *>(&val), sizeof(T), separator, show_length);
} }
/// Format the byte array \p data of length \p len in binary. /// Format the byte array \p data of length \p len in binary.