From e29523e248a7709aac9062bca47dc6f47ef37c5b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 7 Jan 2026 10:31:19 -1000 Subject: [PATCH] [abbwelcome] Use stack-based formatting to eliminate heap allocations (#12799) --- .../remote_base/abbwelcome_protocol.cpp | 14 +++-- .../remote_base/abbwelcome_protocol.h | 52 +++++++++++++------ 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/esphome/components/remote_base/abbwelcome_protocol.cpp b/esphome/components/remote_base/abbwelcome_protocol.cpp index 88f928901b..352ae10ed7 100644 --- a/esphome/components/remote_base/abbwelcome_protocol.cpp +++ b/esphome/components/remote_base/abbwelcome_protocol.cpp @@ -51,7 +51,8 @@ void ABBWelcomeProtocol::encode(RemoteTransmitData *dst, const ABBWelcomeData &s dst->reserve(reserve_count); for (size_t i = 0; i < src.size(); i++) this->encode_byte_(dst, src[i]); - ESP_LOGD(TAG, "Transmitting: %s", src.to_string().c_str()); + char buf[ABBWelcomeData::FORMAT_BUFFER_SIZE]; + ESP_LOGD(TAG, "Transmitting: %s", src.format_to(buf)); } bool ABBWelcomeProtocol::decode_byte_(RemoteReceiveData &src, bool &done, uint8_t &data) { @@ -94,7 +95,8 @@ optional ABBWelcomeProtocol::decode(RemoteReceiveData src) { for (; (received_bytes < length) && !done; received_bytes++) { uint8_t data = 0; if (!this->decode_byte_(src, done, data)) { - ESP_LOGW(TAG, "Received incomplete packet: %s", out.to_string(received_bytes).c_str()); + char buf[ABBWelcomeData::FORMAT_BUFFER_SIZE]; + ESP_LOGW(TAG, "Received incomplete packet: %s", out.format_to(buf, received_bytes)); return {}; } if (received_bytes == 2) { @@ -106,17 +108,19 @@ optional ABBWelcomeProtocol::decode(RemoteReceiveData src) { ESP_LOGVV(TAG, "Received Byte: 0x%02X", data); out[received_bytes] = data; } + char buf[ABBWelcomeData::FORMAT_BUFFER_SIZE]; if (out.is_valid()) { - ESP_LOGI(TAG, "Received: %s", out.to_string().c_str()); + ESP_LOGI(TAG, "Received: %s", out.format_to(buf)); return out; } - ESP_LOGW(TAG, "Received malformed packet: %s", out.to_string(received_bytes).c_str()); + ESP_LOGW(TAG, "Received malformed packet: %s", out.format_to(buf, received_bytes)); } return {}; } void ABBWelcomeProtocol::dump(const ABBWelcomeData &data) { - ESP_LOGD(TAG, "Received ABBWelcome: %s", data.to_string().c_str()); + char buf[ABBWelcomeData::FORMAT_BUFFER_SIZE]; + ESP_LOGD(TAG, "Received ABBWelcome: %s", data.format_to(buf)); } } // namespace remote_base diff --git a/esphome/components/remote_base/abbwelcome_protocol.h b/esphome/components/remote_base/abbwelcome_protocol.h index b8d9293c11..1dddedf8ce 100644 --- a/esphome/components/remote_base/abbwelcome_protocol.h +++ b/esphome/components/remote_base/abbwelcome_protocol.h @@ -136,22 +136,12 @@ class ABBWelcomeData { this->data_[1] = 0xff; this->data_[this->size() - 1] = this->calc_cs_(); } - std::string to_string(uint8_t max_print_bytes = 255) const { - std::string info; - if (this->is_valid()) { - info = str_sprintf(this->get_three_byte_address() ? "[%06" PRIX32 " %s %06" PRIX32 "] Type: %02X" - : "[%04" PRIX32 " %s %04" PRIX32 "] Type: %02X", - this->get_source_address(), this->get_retransmission() ? "»" : ">", - this->get_destination_address(), this->get_message_type()); - if (this->get_data_size()) - info += str_sprintf(", Data: %s", format_hex_pretty(this->get_data()).c_str()); - } else { - info = "[Invalid]"; - } - uint8_t print_bytes = std::min(this->size(), max_print_bytes); - if (print_bytes) - info = str_sprintf("%s %s", format_hex_pretty(this->data_.data(), print_bytes).c_str(), info.c_str()); - return info; + // Buffer size: max raw hex output (27*3-1=80) + space(1) + type_info(27) + data(52) + null(1) = 161, rounded up + static constexpr size_t FORMAT_BUFFER_SIZE = 192; + + template char *format_to(char (&buffer)[N], uint8_t max_print_bytes = 255) const { + static_assert(N >= FORMAT_BUFFER_SIZE, "Buffer too small for format_to()"); + return this->format_to_internal_(buffer, max_print_bytes); } bool operator==(const ABBWelcomeData &rhs) const { if (std::equal(this->data_.begin(), this->data_.begin() + this->size(), rhs.data_.begin())) @@ -168,6 +158,36 @@ class ABBWelcomeData { std::array data_; // Calculate checksum uint8_t calc_cs_() const; + // Internal format implementation - buffer guaranteed >= FORMAT_BUFFER_SIZE by caller + char *format_to_internal_(char *buffer, uint8_t max_print_bytes) const { + char *ptr = buffer; + char *end = buffer + FORMAT_BUFFER_SIZE; + + uint8_t print_bytes = std::min(this->size(), max_print_bytes); + if (print_bytes) { + char raw_hex[format_hex_pretty_size(12 + MAX_DATA_LENGTH)]; + format_hex_pretty_to(raw_hex, this->data_.data(), print_bytes, '.'); + ptr += snprintf(ptr, end - ptr, "%s ", raw_hex); + } + + if (this->is_valid()) { + ptr += snprintf(ptr, end - ptr, + this->get_three_byte_address() ? "[%06" PRIX32 " %s %06" PRIX32 "] Type: %02X" + : "[%04" PRIX32 " %s %04" PRIX32 "] Type: %02X", + this->get_source_address(), this->get_retransmission() ? "»" : ">", + this->get_destination_address(), this->get_message_type()); + if (this->get_data_size()) { + char data_hex[format_hex_pretty_size(MAX_DATA_LENGTH)]; + format_hex_pretty_to(data_hex, this->data_.data() + 5 + 2 * this->get_address_length(), this->get_data_size(), + '.'); + snprintf(ptr, end - ptr, ", Data: %s", data_hex); + } + } else { + snprintf(ptr, end - ptr, "[Invalid]"); + } + + return buffer; + } }; class ABBWelcomeProtocol : public RemoteProtocol {