diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 79123ec893..2dcb94aac1 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1,6 +1,8 @@ #include "api_connection.h" #ifdef USE_API -#include "api_server.h" // brings APIServer complete for encode_to_buffer/get_batch_delay_ms_ +// Pulls in api_server.h (APIServer complete) and the inline encode_to_buffer +// and get_batch_delay_ms_ definitions that need it. +#include "api_connection_buffer.h" #ifdef USE_API_NOISE #include "api_frame_helper_noise.h" #endif diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 61d099c27d..7204acbce8 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -38,8 +38,8 @@ class ComponentIterator; namespace esphome::api { // Forward declaration to break the api_connection.h <-> api_server.h include cycle. -// APIServer is only used through pointers/references in this header; methods that -// need the complete type are defined inline at the bottom of api_server.h. +// APIServer is only used through pointers/references in this header; the few +// inline methods that need the complete type are defined in api_connection_buffer.h. class APIServer; // Keepalive timeout in milliseconds @@ -419,8 +419,9 @@ class APIConnection final : public APIServerConnectionBase { // Core batch encoding logic. Computes header size, checks fit, resizes buffer, encodes. // ALWAYS_INLINE so the compiler can devirtualize encode_fn at hot call sites. - // Definition is at the bottom of api_server.h, where APIServer is a complete type - // (needed for conn->parent_->get_shared_buffer_ref()). Callers must include api_server.h. + // Definition lives in api_connection_buffer.h, where APIServer is a complete type + // (needed for conn->parent_->get_shared_buffer_ref()). Callers must include + // api_connection_buffer.h. static uint16_t ESPHOME_ALWAYS_INLINE encode_to_buffer(uint32_t calculated_size, MessageEncodeFn encode_fn, const void *msg, APIConnection *conn, uint32_t remaining_size); @@ -766,7 +767,7 @@ class APIConnection final : public APIServerConnectionBase { // Read by process_batch_multi_ to pass into MessageInfo. uint8_t batch_header_size_{0}; - // Definition at the bottom of api_server.h (needs APIServer complete). + // Definition in api_connection_buffer.h (needs APIServer complete). uint32_t get_batch_delay_ms_() const; // Message will use 8 more bytes than the minimum size, and typical // MTU is 1500. Sometimes users will see as low as 1460 MTU. diff --git a/esphome/components/api/api_connection_buffer.h b/esphome/components/api/api_connection_buffer.h new file mode 100644 index 0000000000..1921dbb5a5 --- /dev/null +++ b/esphome/components/api/api_connection_buffer.h @@ -0,0 +1,61 @@ +#pragma once + +#include "esphome/core/defines.h" +#ifdef USE_API + +// Inline definitions for APIConnection members that need the complete +// APIServer type (currently encode_to_buffer for the hot batch-send path and +// get_batch_delay_ms_). Pulled into a dedicated header so api_server.h does +// not have to carry APIConnection method bodies and api_connection.h can +// keep just a forward declaration of APIServer, breaking the include cycle +// that previously forced a custom std::unique_ptr deleter on libc++. +// +// Include this header from translation units that call encode_to_buffer or +// any other APIConnection inline that touches APIServer. + +#include "api_connection.h" +#include "api_server.h" + +namespace esphome::api { + +inline uint16_t ESPHOME_ALWAYS_INLINE APIConnection::encode_to_buffer(uint32_t calculated_size, + MessageEncodeFn encode_fn, const void *msg, + APIConnection *conn, uint32_t remaining_size) { +#ifdef HAS_PROTO_MESSAGE_DUMP + if (conn->flags_.log_only_mode) { + auto *proto_msg = static_cast(msg); + DumpBuffer dump_buf; + conn->log_send_message_(proto_msg->message_name(), proto_msg->dump_to(dump_buf)); + return 1; + } +#endif + const uint8_t footer_size = conn->helper_->frame_footer_size(); + + // First message uses max padding (already in buffer), subsequent use exact header size + size_t to_add; + if (conn->flags_.batch_first_message) { + conn->flags_.batch_first_message = false; + conn->batch_header_size_ = conn->helper_->frame_header_padding(); + to_add = calculated_size; + } else { + conn->batch_header_size_ = conn->helper_->frame_header_size(calculated_size, conn->batch_message_type_); + to_add = calculated_size + conn->batch_header_size_ + footer_size; + } + + // Check if it fits (using actual header size, not max padding) + uint16_t total_calculated_size = calculated_size + conn->batch_header_size_ + footer_size; + if (total_calculated_size > remaining_size) + return 0; + + auto &shared_buf = conn->parent_->get_shared_buffer_ref(); + shared_buf.resize(shared_buf.size() + to_add); + ProtoWriteBuffer buffer{&shared_buf, shared_buf.size() - calculated_size}; + encode_fn(msg, buffer PROTO_ENCODE_DEBUG_INIT(&shared_buf)); + + return total_calculated_size; +} + +inline uint32_t APIConnection::get_batch_delay_ms_() const { return this->parent_->get_batch_delay(); } + +} // namespace esphome::api +#endif diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index d7f3e66288..58bc2c3479 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -357,49 +357,5 @@ template class APIConnectedCondition : public Condition { } }; -// Inline definitions for APIConnection members declared in api_connection.h -// that need the complete APIServer type. Defined here, after APIServer is -// complete, so api_connection.h can break the include cycle by only -// forward-declaring APIServer. - -inline uint16_t ESPHOME_ALWAYS_INLINE APIConnection::encode_to_buffer(uint32_t calculated_size, - MessageEncodeFn encode_fn, const void *msg, - APIConnection *conn, uint32_t remaining_size) { -#ifdef HAS_PROTO_MESSAGE_DUMP - if (conn->flags_.log_only_mode) { - auto *proto_msg = static_cast(msg); - DumpBuffer dump_buf; - conn->log_send_message_(proto_msg->message_name(), proto_msg->dump_to(dump_buf)); - return 1; - } -#endif - const uint8_t footer_size = conn->helper_->frame_footer_size(); - - // First message uses max padding (already in buffer), subsequent use exact header size - size_t to_add; - if (conn->flags_.batch_first_message) { - conn->flags_.batch_first_message = false; - conn->batch_header_size_ = conn->helper_->frame_header_padding(); - to_add = calculated_size; - } else { - conn->batch_header_size_ = conn->helper_->frame_header_size(calculated_size, conn->batch_message_type_); - to_add = calculated_size + conn->batch_header_size_ + footer_size; - } - - // Check if it fits (using actual header size, not max padding) - uint16_t total_calculated_size = calculated_size + conn->batch_header_size_ + footer_size; - if (total_calculated_size > remaining_size) - return 0; - - auto &shared_buf = conn->parent_->get_shared_buffer_ref(); - shared_buf.resize(shared_buf.size() + to_add); - ProtoWriteBuffer buffer{&shared_buf, shared_buf.size() - calculated_size}; - encode_fn(msg, buffer PROTO_ENCODE_DEBUG_INIT(&shared_buf)); - - return total_calculated_size; -} - -inline uint32_t APIConnection::get_batch_delay_ms_() const { return this->parent_->get_batch_delay(); } - } // namespace esphome::api #endif