From b8e4343d862515f3e71e5f9576c36dc681ab9012 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 8 Apr 2026 20:04:44 -1000 Subject: [PATCH] [core] Optimize format_hex_internal by splitting separator loop Split the single loop into two paths (with/without separator) to eliminate the per-byte branch on separator. In the separator path, write the separator unconditionally and overwrite the last one with the null terminator. This also lets the compiler use constant stride values (2 or 3) instead of a runtime variable. Benchmarks show ~16-20% improvement on the separator path (format_hex_pretty_to, format_mac_addr_upper). --- esphome/core/helpers.cpp | 52 ++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 5940f6ec985..108d4488614 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -347,33 +347,49 @@ std::string format_mac_address_pretty(const uint8_t *mac) { return std::string(buf); } -// Internal helper for hex formatting - base is 'a' for lowercase or 'A' for uppercase +// Internal helper for hex formatting - base is 'a' for lowercase or 'A' for uppercase. +// Splits into two loops to eliminate per-byte branch on separator. static char *format_hex_internal(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator, char base) { if (length == 0) { buffer[0] = '\0'; return buffer; } - // With separator: total length is 3*length (2*length hex chars, (length-1) separators, 1 null terminator) - // Without separator: total length is 2*length + 1 (2*length hex chars, 1 null terminator) - uint8_t stride = separator ? 3 : 2; - size_t max_bytes = separator ? (buffer_size / stride) : ((buffer_size - 1) / stride); - if (max_bytes == 0) { - buffer[0] = '\0'; - return buffer; - } - if (length > max_bytes) { - length = max_bytes; - } - for (size_t i = 0; i < length; i++) { - size_t pos = i * stride; - buffer[pos] = format_hex_char(data[i] >> 4, base); - buffer[pos + 1] = format_hex_char(data[i] & 0x0F, base); - if (separator && i < length - 1) { + if (separator) { + // With separator: "XX:XX:...:XX\0" = length * 3 bytes total + size_t max_bytes = buffer_size / 3; + if (max_bytes == 0) { + buffer[0] = '\0'; + return buffer; + } + if (length > max_bytes) { + length = max_bytes; + } + for (size_t i = 0; i < length; i++) { + size_t pos = i * 3; + buffer[pos] = format_hex_char(data[i] >> 4, base); + buffer[pos + 1] = format_hex_char(data[i] & 0x0F, base); buffer[pos + 2] = separator; } + // Overwrite last separator with null terminator + buffer[length * 3 - 1] = '\0'; + } else { + // Without separator: "XXXX...XX\0" = length * 2 + 1 bytes total + size_t max_bytes = (buffer_size - 1) / 2; + if (max_bytes == 0) { + buffer[0] = '\0'; + return buffer; + } + if (length > max_bytes) { + length = max_bytes; + } + for (size_t i = 0; i < length; i++) { + size_t pos = i * 2; + buffer[pos] = format_hex_char(data[i] >> 4, base); + buffer[pos + 1] = format_hex_char(data[i] & 0x0F, base); + } + buffer[length * 2] = '\0'; } - buffer[length * stride - (separator ? 1 : 0)] = '\0'; return buffer; }