mirror of
https://github.com/esphome/esphome.git
synced 2026-05-20 09:31:56 +08:00
[core] Optimize format_hex_internal by splitting separator loop (#15594)
This commit is contained in:
@@ -347,17 +347,18 @@ 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.
|
||||
// When separator is set, it is written unconditionally after each byte and the last
|
||||
// one is overwritten with '\0', eliminating the per-byte `i < length - 1` check.
|
||||
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';
|
||||
if (length == 0 || buffer_size == 0) {
|
||||
if (buffer_size > 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);
|
||||
size_t max_bytes = separator ? (buffer_size / 3) : ((buffer_size - 1) / 2);
|
||||
if (max_bytes == 0) {
|
||||
buffer[0] = '\0';
|
||||
return buffer;
|
||||
@@ -369,10 +370,12 @@ static char *format_hex_internal(char *buffer, size_t buffer_size, const uint8_t
|
||||
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) {
|
||||
buffer[pos + 2] = separator;
|
||||
}
|
||||
}
|
||||
// With separator: overwrite last separator with '\0'
|
||||
// Without: write '\0' after last hex char
|
||||
buffer[length * stride - (separator ? 1 : 0)] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -1263,13 +1263,13 @@ constexpr uint8_t parse_hex_char(char c) {
|
||||
}
|
||||
|
||||
/// Convert a nibble (0-15) to hex char with specified base ('a' for lowercase, 'A' for uppercase)
|
||||
inline char format_hex_char(uint8_t v, char base) { return v >= 10 ? base + (v - 10) : '0' + v; }
|
||||
ESPHOME_ALWAYS_INLINE inline char format_hex_char(uint8_t v, char base) { return v >= 10 ? base + (v - 10) : '0' + v; }
|
||||
|
||||
/// Convert a nibble (0-15) to lowercase hex char
|
||||
inline char format_hex_char(uint8_t v) { return format_hex_char(v, 'a'); }
|
||||
ESPHOME_ALWAYS_INLINE inline char format_hex_char(uint8_t v) { return format_hex_char(v, 'a'); }
|
||||
|
||||
/// Convert a nibble (0-15) to uppercase hex char (used for pretty printing)
|
||||
inline char format_hex_pretty_char(uint8_t v) { return format_hex_char(v, 'A'); }
|
||||
ESPHOME_ALWAYS_INLINE inline char format_hex_pretty_char(uint8_t v) { return format_hex_char(v, 'A'); }
|
||||
|
||||
/// Write int8 value to buffer without modulo operations.
|
||||
/// Buffer must have at least 4 bytes free. Returns pointer past last char written.
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <cstring>
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome::core::testing {
|
||||
|
||||
// --- format_hex_to() ---
|
||||
|
||||
TEST(FormatHexTo, Basic) {
|
||||
const uint8_t data[] = {0xAB, 0xCD, 0xEF};
|
||||
char buffer[7]; // 3 * 2 + 1
|
||||
format_hex_to(buffer, data, 3);
|
||||
EXPECT_STREQ(buffer, "abcdef");
|
||||
}
|
||||
|
||||
TEST(FormatHexTo, SingleByte) {
|
||||
const uint8_t data[] = {0x0F};
|
||||
char buffer[3];
|
||||
format_hex_to(buffer, data, 1);
|
||||
EXPECT_STREQ(buffer, "0f");
|
||||
}
|
||||
|
||||
TEST(FormatHexTo, ZeroLength) {
|
||||
char buffer[4] = "xxx";
|
||||
format_hex_to(buffer, static_cast<size_t>(sizeof(buffer)), static_cast<const uint8_t *>(nullptr), 0);
|
||||
EXPECT_STREQ(buffer, "");
|
||||
}
|
||||
|
||||
TEST(FormatHexTo, ZeroBufferSize) {
|
||||
char buffer[4] = "xxx";
|
||||
const uint8_t data[] = {0xAB};
|
||||
format_hex_to(buffer, static_cast<size_t>(0), data, 1);
|
||||
// Should not crash, buffer unchanged
|
||||
EXPECT_EQ(buffer[0], 'x');
|
||||
}
|
||||
|
||||
TEST(FormatHexTo, BufferTooSmall) {
|
||||
const uint8_t data[] = {0xAB, 0xCD, 0xEF};
|
||||
char buffer[5]; // only room for 2 bytes
|
||||
format_hex_to(buffer, data, 3);
|
||||
EXPECT_STREQ(buffer, "abcd");
|
||||
}
|
||||
|
||||
TEST(FormatHexTo, MacAddress) {
|
||||
const uint8_t mac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
|
||||
char buffer[13];
|
||||
format_hex_to(buffer, mac, 6);
|
||||
EXPECT_STREQ(buffer, "aabbccddeeff");
|
||||
}
|
||||
|
||||
// --- format_hex_pretty_to() ---
|
||||
|
||||
TEST(FormatHexPrettyTo, BasicColon) {
|
||||
const uint8_t data[] = {0xAB, 0xCD, 0xEF};
|
||||
char buffer[9]; // 3 * 3
|
||||
format_hex_pretty_to(buffer, data, 3);
|
||||
EXPECT_STREQ(buffer, "AB:CD:EF");
|
||||
}
|
||||
|
||||
TEST(FormatHexPrettyTo, SingleByte) {
|
||||
const uint8_t data[] = {0x0F};
|
||||
char buffer[3];
|
||||
format_hex_pretty_to(buffer, data, 1);
|
||||
EXPECT_STREQ(buffer, "0F");
|
||||
}
|
||||
|
||||
TEST(FormatHexPrettyTo, ZeroLength) {
|
||||
char buffer[4] = "xxx";
|
||||
format_hex_pretty_to(buffer, static_cast<size_t>(sizeof(buffer)), static_cast<const uint8_t *>(nullptr), 0);
|
||||
EXPECT_STREQ(buffer, "");
|
||||
}
|
||||
|
||||
TEST(FormatHexPrettyTo, ZeroBufferSize) {
|
||||
char buffer[4] = "xxx";
|
||||
const uint8_t data[] = {0xAB};
|
||||
format_hex_pretty_to(buffer, static_cast<size_t>(0), data, 1);
|
||||
EXPECT_EQ(buffer[0], 'x');
|
||||
}
|
||||
|
||||
TEST(FormatHexPrettyTo, CustomSeparator) {
|
||||
const uint8_t data[] = {0xAA, 0xBB, 0xCC};
|
||||
char buffer[9];
|
||||
format_hex_pretty_to(buffer, data, 3, '-');
|
||||
EXPECT_STREQ(buffer, "AA-BB-CC");
|
||||
}
|
||||
|
||||
// --- format_mac_addr_upper() ---
|
||||
|
||||
TEST(FormatMacAddrUpper, Basic) {
|
||||
const uint8_t mac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
|
||||
char buffer[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
|
||||
format_mac_addr_upper(mac, buffer);
|
||||
EXPECT_STREQ(buffer, "AA:BB:CC:DD:EE:FF");
|
||||
}
|
||||
|
||||
TEST(FormatMacAddrUpper, AllZeros) {
|
||||
const uint8_t mac[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
char buffer[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
|
||||
format_mac_addr_upper(mac, buffer);
|
||||
EXPECT_STREQ(buffer, "00:00:00:00:00:00");
|
||||
}
|
||||
|
||||
// --- format_hex_char() ---
|
||||
|
||||
TEST(FormatHexChar, LowercaseDigits) {
|
||||
EXPECT_EQ(format_hex_char(0), '0');
|
||||
EXPECT_EQ(format_hex_char(9), '9');
|
||||
EXPECT_EQ(format_hex_char(10), 'a');
|
||||
EXPECT_EQ(format_hex_char(15), 'f');
|
||||
}
|
||||
|
||||
TEST(FormatHexChar, UppercaseDigits) {
|
||||
EXPECT_EQ(format_hex_pretty_char(0), '0');
|
||||
EXPECT_EQ(format_hex_pretty_char(9), '9');
|
||||
EXPECT_EQ(format_hex_pretty_char(10), 'A');
|
||||
EXPECT_EQ(format_hex_pretty_char(15), 'F');
|
||||
}
|
||||
|
||||
} // namespace esphome::core::testing
|
||||
Reference in New Issue
Block a user