From e55a8aeabe97e21cac878f8933d132a0c95e1b8b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 26 Aug 2026 21:49:12 -0500 Subject: [PATCH] [api] Drop connection instead of crashing when buffer allocation fails (#18803) --- esphome/components/api/api_buffer.cpp | 11 ++++- esphome/components/api/api_buffer.h | 32 +++++------- esphome/components/api/api_connection.cpp | 49 ++++++++++++++----- esphome/components/api/api_connection.h | 22 +++------ .../components/api/api_connection_buffer.h | 26 ++++++++-- .../components/api/api_frame_helper_noise.cpp | 27 +++++++--- .../api/api_frame_helper_plaintext.cpp | 5 +- .../components/api/bench_list_entities.cpp | 12 ++--- .../components/api/bench_log_response.cpp | 8 +-- .../components/api/bench_plaintext_frame.cpp | 8 +-- .../components/api/bench_proto_decode.cpp | 2 +- .../components/api/bench_proto_encode.cpp | 24 ++++----- .../components/api/bench_proto_proxy.cpp | 10 ++-- .../components/api/bench_proto_varint.cpp | 6 +-- .../components/api/test_proto_mac_varint.cpp | 2 +- 15 files changed, 148 insertions(+), 96 deletions(-) diff --git a/esphome/components/api/api_buffer.cpp b/esphome/components/api/api_buffer.cpp index 6db18b0365..fc45a4e971 100644 --- a/esphome/components/api/api_buffer.cpp +++ b/esphome/components/api/api_buffer.cpp @@ -1,13 +1,20 @@ #include "api_buffer.h" +#include namespace esphome::api { -void APIBuffer::grow_(size_t n) { - auto new_data = make_buffer(n); +bool APIBuffer::grow_(size_t n) { + // nothrow (no zero-fill) so OOM is reportable; plain new aborts instead + // (NEW_OOM_ABORT on ESP8266 Arduino, exception stub on ESP-IDF). + // RAMAllocator is no fit here: unique_ptr needs delete[]-compatible memory. + std::unique_ptr new_data(new (std::nothrow) uint8_t[n]); + if (new_data == nullptr) + return false; if (this->size_) std::memcpy(new_data.get(), this->data_.get(), this->size_); this->data_ = std::move(new_data); this->capacity_ = n; + return true; } } // namespace esphome::api diff --git a/esphome/components/api/api_buffer.h b/esphome/components/api/api_buffer.h index 1d0cccf61c..396dadbe58 100644 --- a/esphome/components/api/api_buffer.h +++ b/esphome/components/api/api_buffer.h @@ -9,16 +9,6 @@ namespace esphome::api { -/// Helper to use make_unique_for_overwrite where available (skips zero-fill), -/// falling back to make_unique on older GCC (ESP8266, LibreTiny). -inline std::unique_ptr make_buffer(size_t n) { -#if defined(USE_ESP8266) || defined(USE_LIBRETINY) - return std::make_unique(n); -#else - return std::make_unique_for_overwrite(n); -#endif -} - /// Byte buffer that skips zero-initialization on resize(). /// /// std::vector::resize() zero-fills new bytes via memset. For the @@ -36,23 +26,23 @@ inline std::unique_ptr make_buffer(size_t n) { class APIBuffer { public: void clear() { this->size_ = 0; } - inline void reserve(size_t n) ESPHOME_ALWAYS_INLINE { - if (n > this->capacity_) - this->grow_(n); - } - inline void resize(size_t n) ESPHOME_ALWAYS_INLINE { - this->reserve(n); - this->size_ = n; // no zero-fill - } + /// Returns false if allocation fails; the buffer is left unchanged. + [[nodiscard]] inline bool reserve(size_t n) ESPHOME_ALWAYS_INLINE { return n <= this->capacity_ || this->grow_(n); } + /// Returns false if allocation fails; the buffer is left unchanged. No zero-fill. + [[nodiscard]] inline bool resize(size_t n) ESPHOME_ALWAYS_INLINE { return this->reserve_and_resize(n, n); } /// Reserve capacity for max(reserve_size, new_size) bytes, then set size to new_size. /// Single grow_ check regardless of argument order. - inline void reserve_and_resize(size_t reserve_size, size_t new_size) ESPHOME_ALWAYS_INLINE { - this->reserve(std::max(reserve_size, new_size)); + /// Returns false if allocation fails; the buffer is left unchanged. + [[nodiscard]] inline bool reserve_and_resize(size_t reserve_size, size_t new_size) ESPHOME_ALWAYS_INLINE { + if (!this->reserve(std::max(reserve_size, new_size))) + return false; this->size_ = new_size; + return true; } uint8_t *data() { return this->data_.get(); } const uint8_t *data() const { return this->data_.get(); } size_t size() const { return this->size_; } + size_t capacity() const { return this->capacity_; } bool empty() const { return this->size_ == 0; } uint8_t &operator[](size_t i) { return this->data_[i]; } const uint8_t &operator[](size_t i) const { return this->data_[i]; } @@ -64,7 +54,7 @@ class APIBuffer { } protected: - void grow_(size_t n); + bool grow_(size_t n); std::unique_ptr data_; size_t size_{0}; size_t capacity_{0}; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 7b0cb7069e..bc088ca473 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1,6 +1,6 @@ #include "api_connection.h" #ifdef USE_API -#include "api_connection_buffer.h" // for encode_to_buffer / get_batch_delay_ms_ inlines +#include "api_connection_buffer.h" // for the APIServer-dependent APIConnection inlines #ifdef USE_API_NOISE #include "api_frame_helper_noise.h" #endif @@ -2239,10 +2239,17 @@ bool APIConnection::send_message_(uint32_t payload_size, uint16_t message_type, this->log_send_message_(proto_msg->message_name(), proto_msg->dump_to(dump_buf)); } #endif + if (!this->prepare_first_message_buffer(payload_size)) [[unlikely]] { + this->fatal_out_of_memory_(); + return false; + } auto &shared_buf = this->parent_->get_shared_buffer_ref(); - this->prepare_first_message_buffer(shared_buf, payload_size); size_t write_start = shared_buf.size(); - shared_buf.resize(write_start + payload_size); +#ifdef ESPHOME_DEBUG_API + assert(shared_buf.capacity() >= write_start + payload_size); +#endif + // Capacity reserved above, cannot fail + (void) shared_buf.resize(write_start + payload_size); ProtoWriteBuffer buffer{&shared_buf, write_start}; encode_fn(msg, buffer PROTO_ENCODE_DEBUG_INIT(&shared_buf)); return this->send_buffer(ProtoWriteBuffer{&shared_buf}, message_type); @@ -2278,6 +2285,9 @@ void APIConnection::on_no_setup_connection() { this->on_fatal_error(); this->log_client_(ESPHOME_LOG_LEVEL_DEBUG, LOG_STR("no connection setup")); } +void APIConnection::fatal_out_of_memory_() { + this->fatal_error_with_log_(LOG_STR("Out of memory"), APIError::OUT_OF_MEMORY); +} void APIConnection::on_fatal_error() { // Don't close socket here - keep it open so getpeername() works for logging // Socket will be closed when client is removed from the list in APIServer::loop() @@ -2292,16 +2302,25 @@ bool APIConnection::schedule_message_front_(EntityBase *entity, uint16_t message bool APIConnection::send_message_smart_(EntityBase *entity, uint16_t message_type, uint8_t estimated_size, uint8_t aux_data_index) { if (this->should_send_immediately_(message_type) && this->helper_->can_write_without_blocking()) { - auto &shared_buf = this->parent_->get_shared_buffer_ref(); - this->prepare_first_message_buffer(shared_buf, estimated_size); + // No local for the shared buffer here: keeping it live across + // dispatch_message_ costs a register and spills message_type into the + // batching path's dedup loop (measured on x86 GCC -Os) + if (!this->prepare_first_message_buffer(estimated_size)) [[unlikely]] { + this->fatal_out_of_memory_(); + return false; + } DeferredBatch::BatchItem item{entity, message_type, estimated_size, aux_data_index}; if (this->dispatch_message_(item, MAX_BATCH_PACKET_SIZE, true) && - this->send_buffer(ProtoWriteBuffer{&shared_buf}, message_type)) { + this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, message_type)) { #ifdef HAS_PROTO_MESSAGE_DUMP this->log_batch_item_(item); #endif return true; } + // An OOM during the immediate attempt marks the connection for removal; + // don't queue more work (schedule_message_'s push_back may allocate again) + if (this->flags_.remove) [[unlikely]] + return false; } return this->schedule_message_(entity, message_type, estimated_size, aux_data_index); } @@ -2351,7 +2370,11 @@ void APIConnection::process_batch_() { total_estimated_size = MAX_BATCH_PACKET_SIZE; } - this->prepare_first_message_buffer(shared_buf, header_padding, total_estimated_size); + if (!this->prepare_first_message_buffer(header_padding, total_estimated_size)) [[unlikely]] { + this->fatal_out_of_memory_(); + this->clear_batch_(); + return; + } // Fast path for single message - buffer already allocated above if (num_items == 1) { @@ -2366,8 +2389,10 @@ void APIConnection::process_batch_() { #endif this->clear_batch_(); } else if (payload_size == 0) { - // Message too large to fit in available space - ESP_LOGW(TAG, "Message too large to send: type=%u", item.message_type); + // payload_size == 0 with remove set means encoding hit OOM and the + // connection is being dropped; warn only for a genuinely oversized message + if (!this->flags_.remove) + ESP_LOGW(TAG, "Message too large to send: type=%u", item.message_type); this->clear_batch_(); } return; @@ -2430,8 +2455,10 @@ void APIConnection::process_batch_multi_(APIBuffer &shared_buf, size_t num_items if (items_processed > 0) { // Add footer space for the last message (for Noise protocol MAC) - if (footer_size > 0) { - shared_buf.resize(shared_buf.size() + footer_size); + if (footer_size > 0 && !shared_buf.resize(shared_buf.size() + footer_size)) [[unlikely]] { + this->fatal_out_of_memory_(); + this->clear_batch_(); + return; } // Send all collected messages diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 5a554f4857..a4c49dccf4 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -352,22 +352,13 @@ class APIConnection final : public APIServerConnectionBase { } } - void prepare_first_message_buffer(APIBuffer &shared_buf, size_t header_padding, size_t total_size) { - shared_buf.clear(); - // Reserve space for header padding + message + footer - // - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext) - // - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext) - // Reserve full size but only set initial size to header padding - // so message encoding starts at the correct position - shared_buf.reserve_and_resize(total_size, header_padding); - } + /// Clear the shared write buffer and reserve space for the first message. + /// Returns false if the allocation fails (out of memory). + /// Defined in api_connection_buffer.h (needs APIServer complete). + [[nodiscard]] bool prepare_first_message_buffer(size_t header_padding, size_t total_size); // Convenience overload - computes frame overhead internally - void prepare_first_message_buffer(APIBuffer &shared_buf, size_t payload_size) { - const uint8_t header_padding = this->helper_->frame_header_padding(); - const uint8_t footer_size = this->helper_->frame_footer_size(); - this->prepare_first_message_buffer(shared_buf, header_padding, payload_size + header_padding + footer_size); - } + [[nodiscard]] bool prepare_first_message_buffer(size_t payload_size); bool try_to_clear_buffer(bool log_out_of_space) { if (this->flags_.remove) @@ -853,6 +844,9 @@ class APIConnection final : public APIServerConnectionBase { this->on_fatal_error(); this->log_warning_(message, err); } + // Shared cold path for buffer allocation failures — noinline keeps the + // OOM handling out of the hot send paths + void __attribute__((noinline)) fatal_out_of_memory_(); }; } // namespace esphome::api diff --git a/esphome/components/api/api_connection_buffer.h b/esphome/components/api/api_connection_buffer.h index 1dd8a162e4..08520249bf 100644 --- a/esphome/components/api/api_connection_buffer.h +++ b/esphome/components/api/api_connection_buffer.h @@ -3,8 +3,8 @@ #include "esphome/core/defines.h" #ifdef USE_API -// Inline APIConnection methods that need APIServer complete. Include this -// instead of api_connection.h when calling encode_to_buffer or get_batch_delay_ms_. +// Inline APIConnection members that need APIServer complete. Include this +// instead of api_connection.h when calling them. #include "api_connection.h" #include "api_server.h" @@ -41,7 +41,10 @@ inline uint16_t ESPHOME_ALWAYS_INLINE APIConnection::encode_to_buffer(uint32_t c return 0; auto &shared_buf = conn->parent_->get_shared_buffer_ref(); - shared_buf.resize(shared_buf.size() + to_add); + if (!shared_buf.resize(shared_buf.size() + to_add)) [[unlikely]] { + conn->fatal_out_of_memory_(); + return 0; + } ProtoWriteBuffer buffer{&shared_buf, shared_buf.size() - calculated_size}; encode_fn(msg, buffer PROTO_ENCODE_DEBUG_INIT(&shared_buf)); @@ -50,5 +53,22 @@ inline uint16_t ESPHOME_ALWAYS_INLINE APIConnection::encode_to_buffer(uint32_t c inline uint32_t APIConnection::get_batch_delay_ms_() const { return this->parent_->get_batch_delay(); } +inline bool APIConnection::prepare_first_message_buffer(size_t header_padding, size_t total_size) { + auto &shared_buf = this->parent_->get_shared_buffer_ref(); + shared_buf.clear(); + // Reserve space for header padding + message + footer + // - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext) + // - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext) + // Reserve full size but only set initial size to header padding + // so message encoding starts at the correct position + return shared_buf.reserve_and_resize(total_size, header_padding); +} + +inline bool APIConnection::prepare_first_message_buffer(size_t payload_size) { + const uint8_t header_padding = this->helper_->frame_header_padding(); + const uint8_t footer_size = this->helper_->frame_footer_size(); + return this->prepare_first_message_buffer(header_padding, payload_size + header_padding + footer_size); +} + } // namespace esphome::api #endif diff --git a/esphome/components/api/api_frame_helper_noise.cpp b/esphome/components/api/api_frame_helper_noise.cpp index 9c4cc2aa78..138dbdddba 100644 --- a/esphome/components/api/api_frame_helper_noise.cpp +++ b/esphome/components/api/api_frame_helper_noise.cpp @@ -68,7 +68,10 @@ APIError APINoiseFrameHelper::init() { // init prologue size_t old_size = prologue_.size(); - prologue_.resize(old_size + PROLOGUE_INIT_LEN); + if (!prologue_.resize(old_size + PROLOGUE_INIT_LEN)) [[unlikely]] { + state_ = State::FAILED; + return APIError::OUT_OF_MEMORY; + } #ifdef USE_ESP8266 memcpy_P(prologue_.data() + old_size, PROLOGUE_INIT, PROLOGUE_INIT_LEN); #else @@ -202,7 +205,10 @@ APIError APINoiseFrameHelper::try_read_frame_() { // During handshake, rx_buf_.size() is used in prologue construction, so // the buffer must be exactly msg_size to avoid prologue mismatch.) uint16_t alloc_size = msg_size + (is_data ? RX_BUF_NULL_TERMINATOR : 0); - this->rx_buf_.resize(alloc_size); + if (!this->rx_buf_.resize(alloc_size)) [[unlikely]] { + state_ = State::FAILED; + return APIError::OUT_OF_MEMORY; + } if (rx_buf_len_ < msg_size) { // more data to read @@ -269,7 +275,10 @@ APIError APINoiseFrameHelper::state_action_client_hello_() { // Resize for: existing prologue + 2 size bytes + frame data size_t old_size = this->prologue_.size(); size_t rx_size = this->rx_buf_.size(); - this->prologue_.resize(old_size + 2 + rx_size); + if (!this->prologue_.resize(old_size + 2 + rx_size)) [[unlikely]] { + state_ = State::FAILED; + return APIError::OUT_OF_MEMORY; + } this->prologue_[old_size] = (uint8_t) (rx_size >> 8); this->prologue_[old_size + 1] = (uint8_t) rx_size; if (rx_size > 0) { @@ -477,13 +486,15 @@ APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuf assert(this->state_ == State::DATA); #endif + APIBuffer *buf = buffer.get_buffer(); // Resize buffer to include footer space for Noise MAC - if (this->frame_footer_size_) - buffer.get_buffer()->resize(buffer.get_buffer()->size() + this->frame_footer_size_); + if (this->frame_footer_size_ && !buf->resize(buf->size() + this->frame_footer_size_)) [[unlikely]] { + state_ = State::FAILED; + return APIError::OUT_OF_MEMORY; + } - uint16_t payload_size = - static_cast(buffer.get_buffer()->size() - HEADER_PADDING - this->frame_footer_size_); - uint8_t *buf_start = buffer.get_buffer()->data(); + uint16_t payload_size = static_cast(buf->size() - HEADER_PADDING - this->frame_footer_size_); + uint8_t *buf_start = buf->data(); uint16_t encrypted_len; APIError aerr = this->encrypt_noise_message_(buf_start, payload_size, type, encrypted_len); if (aerr != APIError::OK) diff --git a/esphome/components/api/api_frame_helper_plaintext.cpp b/esphome/components/api/api_frame_helper_plaintext.cpp index 09ace7294a..d4e3354fa0 100644 --- a/esphome/components/api/api_frame_helper_plaintext.cpp +++ b/esphome/components/api/api_frame_helper_plaintext.cpp @@ -172,7 +172,10 @@ APIError APIPlaintextFrameHelper::try_read_frame_() { // Reserve space for body (+ null terminator so protobuf StringRef fields // can be safely null-terminated in-place after decode) - this->rx_buf_.resize(this->rx_header_parsed_len_ + RX_BUF_NULL_TERMINATOR); + if (!this->rx_buf_.resize(this->rx_header_parsed_len_ + RX_BUF_NULL_TERMINATOR)) [[unlikely]] { + state_ = State::FAILED; + return APIError::OUT_OF_MEMORY; + } if (rx_buf_len_ < rx_header_parsed_len_) { // more data to read diff --git a/tests/benchmarks/components/api/bench_list_entities.cpp b/tests/benchmarks/components/api/bench_list_entities.cpp index 02cef50d70..4c445c2bb6 100644 --- a/tests/benchmarks/components/api/bench_list_entities.cpp +++ b/tests/benchmarks/components/api/bench_list_entities.cpp @@ -49,7 +49,7 @@ static void Encode_ListEntitiesSensorResponse(benchmark::State &state) { auto msg = make_sensor_response(); APIBuffer buffer; uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -69,7 +69,7 @@ static void CalcAndEncode_ListEntitiesSensorResponse(benchmark::State &state) { for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); ProtoWriteBuffer writer(&buffer, 0); msg.encode(writer); } @@ -117,7 +117,7 @@ static void Encode_ListEntitiesBinarySensorResponse(benchmark::State &state) { auto msg = make_binary_sensor_response(); APIBuffer buffer; uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -137,7 +137,7 @@ static void CalcAndEncode_ListEntitiesBinarySensorResponse(benchmark::State &sta for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); ProtoWriteBuffer writer(&buffer, 0); msg.encode(writer); } @@ -202,7 +202,7 @@ static void Encode_ListEntitiesLightResponse(benchmark::State &state) { auto msg = make_light_response(); APIBuffer buffer; uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -222,7 +222,7 @@ static void CalcAndEncode_ListEntitiesLightResponse(benchmark::State &state) { for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); ProtoWriteBuffer writer(&buffer, 0); msg.encode(writer); } diff --git a/tests/benchmarks/components/api/bench_log_response.cpp b/tests/benchmarks/components/api/bench_log_response.cpp index 4ef57987be..f9060af65c 100644 --- a/tests/benchmarks/components/api/bench_log_response.cpp +++ b/tests/benchmarks/components/api/bench_log_response.cpp @@ -23,7 +23,7 @@ static void Encode_LogResponse_Typical(benchmark::State &state) { msg.level = enums::LOG_LEVEL_DEBUG; msg.set_message(reinterpret_cast(kTypicalLogLine), strlen(kTypicalLogLine)); uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -42,7 +42,7 @@ static void Encode_LogResponse_Short(benchmark::State &state) { msg.level = enums::LOG_LEVEL_INFO; msg.set_message(reinterpret_cast(kShortLogLine), strlen(kShortLogLine)); uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -84,7 +84,7 @@ static void CalcAndEncode_LogResponse_Typical(benchmark::State &state) { for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); ProtoWriteBuffer writer(&buffer, 0); msg.encode(writer); } @@ -105,7 +105,7 @@ static void CalcAndEncode_LogResponse_Typical_Fresh(benchmark::State &state) { for (int i = 0; i < kInnerIterations; i++) { APIBuffer buffer; uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); ProtoWriteBuffer writer(&buffer, 0); msg.encode(writer); benchmark::DoNotOptimize(buffer.data()); diff --git a/tests/benchmarks/components/api/bench_plaintext_frame.cpp b/tests/benchmarks/components/api/bench_plaintext_frame.cpp index 74c640a093..07b479290c 100644 --- a/tests/benchmarks/components/api/bench_plaintext_frame.cpp +++ b/tests/benchmarks/components/api/bench_plaintext_frame.cpp @@ -33,7 +33,7 @@ static void PlaintextFrame_WriteSensorState(benchmark::State &state) { // Pre-init buffer to typical TCP MSS size to avoid benchmarking // heap allocation — in real use the buffer is reused across writes. APIBuffer buffer; - buffer.reserve(1460); + (void) buffer.reserve(1460); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -44,7 +44,7 @@ static void PlaintextFrame_WriteSensorState(benchmark::State &state) { msg.missing_state = false; uint32_t size = msg.calculate_size(); - buffer.resize(padding + size); + (void) buffer.resize(padding + size); ProtoWriteBuffer writer(&buffer, padding); msg.encode(writer); @@ -70,7 +70,7 @@ static void PlaintextFrame_WriteBatch5(benchmark::State &state) { // Pre-init buffer to typical TCP MSS size to avoid benchmarking // heap allocation — in real use the buffer is reused across writes. APIBuffer buffer; - buffer.reserve(1460); + (void) buffer.reserve(1460); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -85,7 +85,7 @@ static void PlaintextFrame_WriteBatch5(benchmark::State &state) { msg.missing_state = false; uint32_t size = msg.calculate_size(); - buffer.resize(offset + padding + size + footer); + (void) buffer.resize(offset + padding + size + footer); ProtoWriteBuffer writer(&buffer, offset + padding); msg.encode(writer); diff --git a/tests/benchmarks/components/api/bench_proto_decode.cpp b/tests/benchmarks/components/api/bench_proto_decode.cpp index 961c629f2a..0268e98035 100644 --- a/tests/benchmarks/components/api/bench_proto_decode.cpp +++ b/tests/benchmarks/components/api/bench_proto_decode.cpp @@ -16,7 +16,7 @@ static constexpr int kInnerIterations = 2000; template static APIBuffer encode_message(const T &msg) { APIBuffer buffer; uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); ProtoWriteBuffer writer(&buffer, 0); msg.encode(writer); return buffer; diff --git a/tests/benchmarks/components/api/bench_proto_encode.cpp b/tests/benchmarks/components/api/bench_proto_encode.cpp index 1e2efcd281..e1383e8990 100644 --- a/tests/benchmarks/components/api/bench_proto_encode.cpp +++ b/tests/benchmarks/components/api/bench_proto_encode.cpp @@ -19,7 +19,7 @@ static void Encode_SensorStateResponse(benchmark::State &state) { msg.state = 23.5f; msg.missing_state = false; uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -60,7 +60,7 @@ static void CalcAndEncode_SensorStateResponse(benchmark::State &state) { for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); ProtoWriteBuffer writer(&buffer, 0); msg.encode(writer); } @@ -84,7 +84,7 @@ static void CalcAndEncode_SensorStateResponse_Fresh(benchmark::State &state) { for (int i = 0; i < kInnerIterations; i++) { APIBuffer buffer; uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); ProtoWriteBuffer writer(&buffer, 0); msg.encode(writer); benchmark::DoNotOptimize(buffer.data()); @@ -103,7 +103,7 @@ static void Encode_BinarySensorStateResponse(benchmark::State &state) { msg.state = true; msg.missing_state = false; uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -126,7 +126,7 @@ static void Encode_HelloResponse(benchmark::State &state) { msg.server_info = StringRef::from_lit("esphome v2026.3.0"); msg.name = StringRef::from_lit("living-room-sensor"); uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -158,7 +158,7 @@ static void Encode_LightStateResponse(benchmark::State &state) { msg.warm_white = 0.0f; msg.effect = StringRef::from_lit("rainbow"); uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -243,7 +243,7 @@ static void Encode_DeviceInfoResponse(benchmark::State &state) { auto msg = make_device_info_response(); APIBuffer buffer; uint32_t total_size = msg.calculate_size(); - buffer.resize(total_size); + (void) buffer.resize(total_size); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -264,7 +264,7 @@ static void CalcAndEncode_DeviceInfoResponse(benchmark::State &state) { for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); ProtoWriteBuffer writer(&buffer, 0); msg.encode(writer); } @@ -285,7 +285,7 @@ static void CalcAndEncode_DeviceInfoResponse_Fresh(benchmark::State &state) { for (int i = 0; i < kInnerIterations; i++) { APIBuffer buffer; uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); ProtoWriteBuffer writer(&buffer, 0); msg.encode(writer); benchmark::DoNotOptimize(buffer.data()); @@ -335,7 +335,7 @@ static void Encode_BLERawAdvs12(benchmark::State &state) { auto msg = make_ble_raw_advs_12(); APIBuffer buffer; uint32_t total_size = msg.calculate_size(); - buffer.resize(total_size); + (void) buffer.resize(total_size); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -355,7 +355,7 @@ static void CalcAndEncode_BLERawAdvs12(benchmark::State &state) { for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); ProtoWriteBuffer writer(&buffer, 0); msg.encode(writer); } @@ -372,7 +372,7 @@ static void CalcAndEncode_BLERawAdvs12_Fresh(benchmark::State &state) { for (int i = 0; i < kInnerIterations; i++) { APIBuffer buffer; uint32_t size = msg.calculate_size(); - buffer.resize(size); + (void) buffer.resize(size); ProtoWriteBuffer writer(&buffer, 0); msg.encode(writer); benchmark::DoNotOptimize(buffer.data()); diff --git a/tests/benchmarks/components/api/bench_proto_proxy.cpp b/tests/benchmarks/components/api/bench_proto_proxy.cpp index fa3191a969..05bbcc73dd 100644 --- a/tests/benchmarks/components/api/bench_proto_proxy.cpp +++ b/tests/benchmarks/components/api/bench_proto_proxy.cpp @@ -16,7 +16,7 @@ static constexpr int kInnerIterations = 2000; // Encodes `src` into `out`. Caller owns `out` and must keep it alive across // the decode loop (decoded messages may store pointers back into its bytes). template static void encode_into(APIBuffer &out, const T &src) { - out.resize(src.calculate_size()); + (void) out.resize(src.calculate_size()); ProtoWriteBuffer writer(&out, 0); src.encode(writer); } @@ -33,7 +33,7 @@ static void Encode_ZWaveProxyFrame(benchmark::State &state) { msg.data = kZWaveFrameData; msg.data_len = sizeof(kZWaveFrameData); APIBuffer buffer; - buffer.resize(msg.calculate_size()); + (void) buffer.resize(msg.calculate_size()); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -111,7 +111,7 @@ static void Encode_SerialProxyDataReceived(benchmark::State &state) { msg.instance = 0; msg.set_data(kSerialPayload, kSerialPayloadSize); APIBuffer buffer; - buffer.resize(msg.calculate_size()); + (void) buffer.resize(msg.calculate_size()); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -171,7 +171,7 @@ static void Encode_InfraredRFReceiveEvent(benchmark::State &state) { msg.key = 0xDEADBEEF; msg.timings = &get_ir_timings_100(); APIBuffer buffer; - buffer.resize(msg.calculate_size()); + (void) buffer.resize(msg.calculate_size()); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -254,7 +254,7 @@ static APIBuffer build_infrared_rf_transmit_wire() { put_varint(1); APIBuffer buf; - buf.resize(len); + (void) buf.resize(len); std::memcpy(buf.data(), bytes, len); return buf; } diff --git a/tests/benchmarks/components/api/bench_proto_varint.cpp b/tests/benchmarks/components/api/bench_proto_varint.cpp index 0b5ccc2b7d..ea7fd99aa5 100644 --- a/tests/benchmarks/components/api/bench_proto_varint.cpp +++ b/tests/benchmarks/components/api/bench_proto_varint.cpp @@ -58,7 +58,7 @@ BENCHMARK(ProtoVarInt_Parse_FiveByte); static void Encode_Varint_Small(benchmark::State &state) { APIBuffer buffer; - buffer.resize(16); + (void) buffer.resize(16); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -73,7 +73,7 @@ BENCHMARK(Encode_Varint_Small); static void Encode_Varint_Large(benchmark::State &state) { APIBuffer buffer; - buffer.resize(16); + (void) buffer.resize(16); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { @@ -88,7 +88,7 @@ BENCHMARK(Encode_Varint_Large); static void Encode_Varint_MaxUint32(benchmark::State &state) { APIBuffer buffer; - buffer.resize(16); + (void) buffer.resize(16); for (auto _ : state) { for (int i = 0; i < kInnerIterations; i++) { diff --git a/tests/components/api/test_proto_mac_varint.cpp b/tests/components/api/test_proto_mac_varint.cpp index f2a63e96f6..9ea6ce1cd9 100644 --- a/tests/components/api/test_proto_mac_varint.cpp +++ b/tests/components/api/test_proto_mac_varint.cpp @@ -54,7 +54,7 @@ static void verify_mac(uint64_t mac, size_t expected_bytes) { size_t ref_len = reference_encode(mac, ref_buf); APIBuffer api_buf; - api_buf.resize(16); + ASSERT_TRUE(api_buf.resize(16)); uint8_t *pos = api_buf.data(); #ifdef ESPHOME_DEBUG_API uint8_t *proto_debug_end_ = api_buf.data() + api_buf.size();