From 67b681854e99df490d2b813132badd1621b1fb24 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 13 Jun 2025 18:20:01 -0500 Subject: [PATCH] Fix API message encoding to return actual size instead of calculated size --- esphome/components/api/api_connection.cpp | 32 +++++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index d09b1107d2..9d6a6363b5 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -248,25 +248,41 @@ void APIConnection::on_disconnect_response(const DisconnectResponse &value) { uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn, uint32_t remaining_size, bool is_single) { // Calculate size - uint32_t size = 0; - msg.calculate_size(size); + uint32_t calculated_size = 0; + msg.calculate_size(calculated_size); + + // Cache frame sizes to avoid repeated virtual calls + const uint8_t header_padding = conn->helper_->frame_header_padding(); + const uint8_t footer_size = conn->helper_->frame_footer_size(); // Calculate total size with padding for buffer allocation - uint16_t total_size = - static_cast(size) + conn->helper_->frame_header_padding() + conn->helper_->frame_footer_size(); + uint16_t total_calculated_size = static_cast(calculated_size) + header_padding + footer_size; // Check if it fits - if (total_size > remaining_size) { + if (total_calculated_size > remaining_size) { return 0; // Doesn't fit } // Allocate buffer space - pass payload size, allocation functions add header/footer space - ProtoWriteBuffer buffer = - is_single ? conn->allocate_single_message_buffer(size) : conn->allocate_batch_message_buffer(size); + ProtoWriteBuffer buffer = is_single ? conn->allocate_single_message_buffer(calculated_size) + : conn->allocate_batch_message_buffer(calculated_size); + + // Get buffer size after allocation (which includes header padding) + std::vector &shared_buf = conn->parent_->get_shared_buffer_ref(); + size_t size_before_encode = shared_buf.size(); // Encode directly into buffer msg.encode(buffer); - return total_size; + + // Calculate actual encoded size (not including header that was already added) + size_t actual_payload_size = shared_buf.size() - size_before_encode; + + // Return actual total size (header + actual payload + footer) + uint16_t actual_total_size = header_padding + static_cast(actual_payload_size) + footer_size; + + // Verify that calculate_size() returned the correct value + assert(calculated_size == actual_payload_size); + return actual_total_size; } #ifdef USE_BINARY_SENSOR