mirror of
https://github.com/esphome/esphome.git
synced 2026-09-15 09:08:41 +00:00
Merge branch 'zwave_no_alloc_hex' into integration
This commit is contained in:
@@ -26,13 +26,7 @@ class HashBase {
|
||||
void get_bytes(uint8_t *output) { memcpy(output, this->digest_, this->get_size()); }
|
||||
|
||||
/// Retrieve the hash as hex characters
|
||||
void get_hex(char *output) {
|
||||
for (size_t i = 0; i < this->get_size(); i++) {
|
||||
uint8_t byte = this->digest_[i];
|
||||
output[i * 2] = format_hex_char(byte >> 4);
|
||||
output[i * 2 + 1] = format_hex_char(byte & 0x0F);
|
||||
}
|
||||
}
|
||||
void get_hex(char *output) { format_hex_to(output, this->get_size() * 2 + 1, this->digest_, this->get_size()); }
|
||||
|
||||
/// Compare the hash against a provided byte-encoded hash
|
||||
bool equals_bytes(const uint8_t *expected) { return memcmp(this->digest_, expected, this->get_size()) == 0; }
|
||||
|
||||
+27
-30
@@ -286,19 +286,40 @@ std::string format_mac_address_pretty(const uint8_t *mac) {
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
char *format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length) {
|
||||
size_t max_bytes = (buffer_size - 1) / 2;
|
||||
// Internal helper for hex formatting - base is 'a' for lowercase or 'A' for uppercase
|
||||
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++) {
|
||||
buffer[2 * i] = format_hex_char(data[i] >> 4);
|
||||
buffer[2 * i + 1] = format_hex_char(data[i] & 0x0F);
|
||||
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) {
|
||||
buffer[pos + 2] = separator;
|
||||
}
|
||||
}
|
||||
buffer[length * 2] = '\0';
|
||||
buffer[length * stride - (separator ? 1 : 0)] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char *format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length) {
|
||||
return format_hex_internal(buffer, buffer_size, data, length, 0, 'a');
|
||||
}
|
||||
|
||||
std::string format_hex(const uint8_t *data, size_t length) {
|
||||
std::string ret;
|
||||
ret.resize(length * 2);
|
||||
@@ -308,31 +329,7 @@ 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()); }
|
||||
|
||||
char *format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator) {
|
||||
if (length == 0) {
|
||||
buffer[0] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
// With separator: each byte needs 3 chars (XX + sep), last byte needs 2 + null = length*3
|
||||
// Without separator: each byte needs 2 chars + null = length*2 + 1
|
||||
uint8_t stride = separator ? 3 : 2;
|
||||
size_t max_bytes = (buffer_size - 1) / stride + (separator ? 1 : 0);
|
||||
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_pretty_char(data[i] >> 4);
|
||||
buffer[pos + 1] = format_hex_pretty_char(data[i] & 0x0F);
|
||||
if (separator && i < length - 1) {
|
||||
buffer[pos + 2] = separator;
|
||||
}
|
||||
}
|
||||
buffer[length * stride - (separator ? 1 : 0)] = '\0';
|
||||
return buffer;
|
||||
return format_hex_internal(buffer, buffer_size, data, length, separator, 'A');
|
||||
}
|
||||
|
||||
// Shared implementation for uint8_t and string hex formatting
|
||||
|
||||
@@ -694,12 +694,14 @@ constexpr uint8_t parse_hex_char(char c) {
|
||||
return 255;
|
||||
}
|
||||
|
||||
/// 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; }
|
||||
|
||||
/// 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; }
|
||||
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)
|
||||
/// 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; }
|
||||
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,212 @@
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
// Copy the implementations to test them in isolation
|
||||
|
||||
inline char format_hex_char(uint8_t v, char base) { return v >= 10 ? base + (v - 10) : '0' + v; }
|
||||
inline char format_hex_char(uint8_t v) { return format_hex_char(v, 'a'); }
|
||||
inline char format_hex_pretty_char(uint8_t v) { return format_hex_char(v, 'A'); }
|
||||
|
||||
constexpr size_t format_hex_pretty_size(size_t byte_count) { return byte_count * 3; }
|
||||
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
buffer[pos + 2] = separator;
|
||||
}
|
||||
}
|
||||
buffer[length * stride - (separator ? 1 : 0)] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char *format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length) {
|
||||
return format_hex_internal(buffer, buffer_size, data, length, 0, 'a');
|
||||
}
|
||||
|
||||
char *format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator) {
|
||||
return format_hex_internal(buffer, buffer_size, data, length, separator, 'A');
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
char *format_hex_pretty_to(char (&buffer)[N], const uint8_t *data, size_t length, char separator = ':') {
|
||||
return format_hex_pretty_to(buffer, N, data, length, separator);
|
||||
}
|
||||
|
||||
static constexpr size_t MAC_ADDRESS_SIZE = 6;
|
||||
static constexpr size_t MAC_ADDRESS_PRETTY_BUFFER_SIZE = format_hex_pretty_size(MAC_ADDRESS_SIZE);
|
||||
static constexpr size_t MAC_ADDRESS_BUFFER_SIZE = MAC_ADDRESS_SIZE * 2 + 1;
|
||||
|
||||
inline void format_mac_addr_upper(const uint8_t *mac, char *output) {
|
||||
format_hex_pretty_to(output, MAC_ADDRESS_PRETTY_BUFFER_SIZE, mac, MAC_ADDRESS_SIZE, ':');
|
||||
}
|
||||
|
||||
inline void format_mac_addr_lower_no_sep(const uint8_t *mac, char *output) {
|
||||
format_hex_to(output, MAC_ADDRESS_BUFFER_SIZE, mac, MAC_ADDRESS_SIZE);
|
||||
}
|
||||
|
||||
// Tests
|
||||
|
||||
void test_format_hex_char_base() {
|
||||
assert(format_hex_char(0, 'a') == '0');
|
||||
assert(format_hex_char(9, 'a') == '9');
|
||||
assert(format_hex_char(10, 'a') == 'a');
|
||||
assert(format_hex_char(15, 'a') == 'f');
|
||||
assert(format_hex_char(0, 'A') == '0');
|
||||
assert(format_hex_char(10, 'A') == 'A');
|
||||
assert(format_hex_char(15, 'A') == 'F');
|
||||
printf("✓ format_hex_char with base\n");
|
||||
}
|
||||
|
||||
void test_format_hex_char_lowercase() {
|
||||
assert(format_hex_char(0) == '0');
|
||||
assert(format_hex_char(10) == 'a');
|
||||
assert(format_hex_char(15) == 'f');
|
||||
printf("✓ format_hex_char lowercase\n");
|
||||
}
|
||||
|
||||
void test_format_hex_pretty_char_uppercase() {
|
||||
assert(format_hex_pretty_char(0) == '0');
|
||||
assert(format_hex_pretty_char(10) == 'A');
|
||||
assert(format_hex_pretty_char(15) == 'F');
|
||||
printf("✓ format_hex_pretty_char uppercase\n");
|
||||
}
|
||||
|
||||
void test_format_hex_to() {
|
||||
uint8_t data[] = {0xde, 0xad, 0xbe, 0xef};
|
||||
char buf[9];
|
||||
format_hex_to(buf, sizeof(buf), data, 4);
|
||||
assert(strcmp(buf, "deadbeef") == 0);
|
||||
printf("✓ format_hex_to lowercase\n");
|
||||
}
|
||||
|
||||
void test_format_hex_pretty_to_colon() {
|
||||
uint8_t data[] = {0xDE, 0xAD, 0xBE, 0xEF};
|
||||
char buf[12];
|
||||
format_hex_pretty_to(buf, sizeof(buf), data, 4, ':');
|
||||
assert(strcmp(buf, "DE:AD:BE:EF") == 0);
|
||||
printf("✓ format_hex_pretty_to with colon\n");
|
||||
}
|
||||
|
||||
void test_format_hex_pretty_to_dot() {
|
||||
uint8_t data[] = {0xAA, 0xBB, 0xCC};
|
||||
char buf[9];
|
||||
format_hex_pretty_to(buf, sizeof(buf), data, 3, '.');
|
||||
assert(strcmp(buf, "AA.BB.CC") == 0);
|
||||
printf("✓ format_hex_pretty_to with dot\n");
|
||||
}
|
||||
|
||||
void test_buffer_overflow_protection() {
|
||||
uint8_t data[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE};
|
||||
char buf[11];
|
||||
format_hex_pretty_to(buf, 11, data, 5, ':');
|
||||
assert(strcmp(buf, "AA:BB:CC") == 0);
|
||||
printf("✓ buffer overflow protection\n");
|
||||
}
|
||||
|
||||
void test_exact_fit() {
|
||||
uint8_t data[] = {0xAA, 0xBB, 0xCC, 0xDD};
|
||||
char buf[12];
|
||||
format_hex_pretty_to(buf, 12, data, 4, ':');
|
||||
assert(strcmp(buf, "AA:BB:CC:DD") == 0);
|
||||
printf("✓ exact fit buffer\n");
|
||||
}
|
||||
|
||||
void test_mac_addr_upper() {
|
||||
uint8_t mac[] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
|
||||
char buf[18];
|
||||
format_mac_addr_upper(mac, buf);
|
||||
assert(strcmp(buf, "AA:BB:CC:DD:EE:FF") == 0);
|
||||
printf("✓ format_mac_addr_upper\n");
|
||||
}
|
||||
|
||||
void test_mac_addr_lower_no_sep() {
|
||||
uint8_t mac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
|
||||
char buf[13];
|
||||
format_mac_addr_lower_no_sep(mac, buf);
|
||||
assert(strcmp(buf, "aabbccddeeff") == 0);
|
||||
printf("✓ format_mac_addr_lower_no_sep\n");
|
||||
}
|
||||
|
||||
void test_empty() {
|
||||
uint8_t data[] = {0xAA};
|
||||
char buf[12];
|
||||
format_hex_pretty_to(buf, sizeof(buf), data, 0, ':');
|
||||
assert(strcmp(buf, "") == 0);
|
||||
printf("✓ empty data\n");
|
||||
}
|
||||
|
||||
void test_single_byte() {
|
||||
uint8_t data[] = {0x42};
|
||||
char buf[3];
|
||||
format_hex_pretty_to(buf, sizeof(buf), data, 1, ':');
|
||||
assert(strcmp(buf, "42") == 0);
|
||||
printf("✓ single byte\n");
|
||||
}
|
||||
|
||||
void test_template() {
|
||||
uint8_t data[] = {0xDE, 0xAD, 0xBE, 0xEF};
|
||||
char buf[format_hex_pretty_size(4)];
|
||||
format_hex_pretty_to(buf, data, 4);
|
||||
assert(strcmp(buf, "DE:AD:BE:EF") == 0);
|
||||
printf("✓ template version\n");
|
||||
}
|
||||
|
||||
void test_no_separator() {
|
||||
uint8_t data[] = {0xDE, 0xAD};
|
||||
char buf[5];
|
||||
format_hex_pretty_to(buf, sizeof(buf), data, 2, 0);
|
||||
assert(strcmp(buf, "DEAD") == 0);
|
||||
printf("✓ no separator\n");
|
||||
}
|
||||
|
||||
void test_constexpr() {
|
||||
static_assert(format_hex_pretty_size(1) == 3, "");
|
||||
static_assert(format_hex_pretty_size(4) == 12, "");
|
||||
static_assert(format_hex_pretty_size(6) == 18, "");
|
||||
static_assert(MAC_ADDRESS_SIZE == 6, "");
|
||||
static_assert(MAC_ADDRESS_PRETTY_BUFFER_SIZE == 18, "");
|
||||
static_assert(MAC_ADDRESS_BUFFER_SIZE == 13, "");
|
||||
printf("✓ constexpr values\n");
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("Running hex formatting tests...\n\n");
|
||||
|
||||
test_format_hex_char_base();
|
||||
test_format_hex_char_lowercase();
|
||||
test_format_hex_pretty_char_uppercase();
|
||||
test_format_hex_to();
|
||||
test_format_hex_pretty_to_colon();
|
||||
test_format_hex_pretty_to_dot();
|
||||
test_buffer_overflow_protection();
|
||||
test_exact_fit();
|
||||
test_mac_addr_upper();
|
||||
test_mac_addr_lower_no_sep();
|
||||
test_empty();
|
||||
test_single_byte();
|
||||
test_template();
|
||||
test_no_separator();
|
||||
test_constexpr();
|
||||
|
||||
printf("\n✅ All 15 tests passed!\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user