From 0b422509006464d19a13d9df3f11a8cfcc334916 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 14 Sep 2025 13:49:11 -0500 Subject: [PATCH 1/3] [core] Optimize MAC address formatting to eliminate sprintf dependency --- .../ethernet/ethernet_component.cpp | 4 ++- esphome/core/helpers.cpp | 17 ++++++----- esphome/core/helpers.h | 29 +++++++++++++++++++ 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 844a30bd8b..57f7a13d39 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -638,7 +638,9 @@ void EthernetComponent::get_eth_mac_address_raw(uint8_t *mac) { std::string EthernetComponent::get_eth_mac_address_pretty() { uint8_t mac[6]; get_eth_mac_address_raw(mac); - return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + char buf[18]; + format_mac_addr_upper(mac, buf); + return std::string(buf); } eth_duplex_t EthernetComponent::get_duplex_mode() { diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 43d6f1153c..471fc79ecf 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -255,23 +255,22 @@ size_t parse_hex(const char *str, size_t length, uint8_t *data, size_t count) { } std::string format_mac_address_pretty(const uint8_t *mac) { - return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + char buf[18]; + format_mac_addr_upper(mac, buf); + return std::string(buf); } -static char format_hex_char(uint8_t v) { return v >= 10 ? 'a' + (v - 10) : '0' + v; } std::string format_hex(const uint8_t *data, size_t length) { std::string ret; ret.resize(length * 2); for (size_t i = 0; i < length; i++) { - ret[2 * i] = format_hex_char((data[i] & 0xF0) >> 4); - ret[2 * i + 1] = format_hex_char(data[i] & 0x0F); + ret[2 * i] = format_hex_char_lower(data[i] >> 4); + ret[2 * i + 1] = format_hex_char_lower(data[i] & 0x0F); } return ret; } std::string format_hex(const std::vector &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; } - // Shared implementation for uint8_t and string hex formatting static std::string format_hex_pretty_uint8(const uint8_t *data, size_t length, char separator, bool show_length) { if (data == nullptr || length == 0) @@ -280,7 +279,7 @@ static std::string format_hex_pretty_uint8(const uint8_t *data, size_t length, c uint8_t multiple = separator ? 3 : 2; // 3 if separator is not \0, 2 otherwise ret.resize(multiple * length - (separator ? 1 : 0)); for (size_t i = 0; i < length; i++) { - ret[multiple * i] = format_hex_pretty_char((data[i] & 0xF0) >> 4); + ret[multiple * i] = format_hex_pretty_char(data[i] >> 4); ret[multiple * i + 1] = format_hex_pretty_char(data[i] & 0x0F); if (separator && i != length - 1) ret[multiple * i + 2] = separator; @@ -591,7 +590,9 @@ bool HighFrequencyLoopRequester::is_high_frequency() { return num_requests > 0; std::string get_mac_address() { uint8_t mac[6]; get_mac_address_raw(mac); - return str_snprintf("%02x%02x%02x%02x%02x%02x", 12, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + char buf[13]; + format_mac_addr_lower_no_sep(mac, buf); + return std::string(buf); } std::string get_mac_address_pretty() { diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index a6741925d0..f79f8824f1 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -380,6 +380,35 @@ template::value, int> = 0> optional< return parse_hex(str.c_str(), str.length()); } +/// Convert a nibble (0-15) to lowercase hex char +inline char format_hex_char_lower(uint8_t v) { return v >= 10 ? 'a' + (v - 10) : '0' + v; } + +/// Convert a nibble (0-15) to uppercase hex char (used for pretty printing) +/// This always uses uppercase (A-F) for pretty/human-readable output +inline char format_hex_pretty_char(uint8_t v) { return v >= 10 ? 'A' + (v - 10) : '0' + v; } + +/// Format MAC address as XX:XX:XX:XX:XX:XX (uppercase) +inline void format_mac_addr_upper(const uint8_t *mac, char *output) { + for (size_t i = 0; i < 6; i++) { + uint8_t byte = mac[i]; + output[i * 3] = format_hex_pretty_char(byte >> 4); + output[i * 3 + 1] = format_hex_pretty_char(byte & 0x0F); + if (i < 5) + output[i * 3 + 2] = ':'; + } + output[17] = '\0'; +} + +/// Format MAC address as xxxxxxxxxxxxxx (lowercase, no separators) +inline void format_mac_addr_lower_no_sep(const uint8_t *mac, char *output) { + for (size_t i = 0; i < 6; i++) { + uint8_t byte = mac[i]; + output[i * 2] = format_hex_char_lower(byte >> 4); + output[i * 2 + 1] = format_hex_char_lower(byte & 0x0F); + } + output[12] = '\0'; +} + /// Format the six-byte array \p mac into a MAC address. std::string format_mac_address_pretty(const uint8_t mac[6]); /// Format the byte array \p data of length \p len in lowercased hex. From 22c91dfadc5ba2bcf0c0c291eaf8b21c4d285274 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 14 Sep 2025 13:51:10 -0500 Subject: [PATCH 2/3] cleanup --- esphome/components/md5/md5.cpp | 6 ++---- esphome/core/helpers.cpp | 1 - esphome/core/helpers.h | 3 +++ 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/md5/md5.cpp b/esphome/components/md5/md5.cpp index fb0d9f401f..21bd2e1cab 100644 --- a/esphome/components/md5/md5.cpp +++ b/esphome/components/md5/md5.cpp @@ -44,10 +44,8 @@ void MD5Digest::get_bytes(uint8_t *output) { memcpy(output, this->digest_, 16); void MD5Digest::get_hex(char *output) { for (size_t i = 0; i < 16; i++) { uint8_t byte = this->digest_[i]; - uint8_t high = byte >> 4; - uint8_t low = byte & 0x0F; - output[i * 2] = high < 10 ? '0' + high : 'a' + (high - 10); - output[i * 2 + 1] = low < 10 ? '0' + low : 'a' + (low - 10); + output[i * 2] = format_hex_char(byte >> 4); + output[i * 2 + 1] = format_hex_char(byte & 0x0F); } } diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 43d6f1153c..7f977c5d40 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -258,7 +258,6 @@ std::string format_mac_address_pretty(const uint8_t *mac) { return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } -static char format_hex_char(uint8_t v) { return v >= 10 ? 'a' + (v - 10) : '0' + v; } std::string format_hex(const uint8_t *data, size_t length) { std::string ret; ret.resize(length * 2); diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index a6741925d0..a2bac19b25 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -380,6 +380,9 @@ template::value, int> = 0> optional< return parse_hex(str.c_str(), str.length()); } +/// Convert a nibble (0-15) to lowercase hex char +inline char format_hex_char(uint8_t v) { return v >= 10 ? 'a' + (v - 10) : '0' + v; } + /// Format the six-byte array \p mac into a MAC address. std::string format_mac_address_pretty(const uint8_t mac[6]); /// Format the byte array \p data of length \p len in lowercased hex. From 16b77149906823997336363e150b10c1eb3ffdbc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 14 Sep 2025 13:53:36 -0500 Subject: [PATCH 3/3] preen --- esphome/core/helpers.cpp | 4 ++-- esphome/core/helpers.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 471fc79ecf..f1560711ef 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -264,8 +264,8 @@ std::string format_hex(const uint8_t *data, size_t length) { std::string ret; ret.resize(length * 2); for (size_t i = 0; i < length; i++) { - ret[2 * i] = format_hex_char_lower(data[i] >> 4); - ret[2 * i + 1] = format_hex_char_lower(data[i] & 0x0F); + ret[2 * i] = format_hex_char(data[i] >> 4); + ret[2 * i + 1] = format_hex_char(data[i] & 0x0F); } return ret; } diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index f79f8824f1..21aa159b25 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -381,7 +381,7 @@ template::value, int> = 0> optional< } /// Convert a nibble (0-15) to lowercase hex char -inline char format_hex_char_lower(uint8_t v) { return v >= 10 ? 'a' + (v - 10) : '0' + v; } +inline char format_hex_char(uint8_t v) { return v >= 10 ? 'a' + (v - 10) : '0' + v; } /// Convert a nibble (0-15) to uppercase hex char (used for pretty printing) /// This always uses uppercase (A-F) for pretty/human-readable output @@ -403,8 +403,8 @@ inline void format_mac_addr_upper(const uint8_t *mac, char *output) { inline void format_mac_addr_lower_no_sep(const uint8_t *mac, char *output) { for (size_t i = 0; i < 6; i++) { uint8_t byte = mac[i]; - output[i * 2] = format_hex_char_lower(byte >> 4); - output[i * 2 + 1] = format_hex_char_lower(byte & 0x0F); + output[i * 2] = format_hex_char(byte >> 4); + output[i * 2 + 1] = format_hex_char(byte & 0x0F); } output[12] = '\0'; }