[api] Move APIConnection inlines into api_connection_buffer.h

Pull encode_to_buffer and get_batch_delay_ms_ out of the tail of
api_server.h into a dedicated api_connection_buffer.h so api_server.h no
longer carries APIConnection method bodies. api_connection.cpp includes
api_connection_buffer.h instead of api_server.h; the new header pulls
api_server.h in to keep APIServer complete at the inline-definition site.

No behavioral change; just file organisation on top of the previous
include-cycle break.
This commit is contained in:
J. Nick Koston
2026-05-21 14:03:26 -05:00
parent 8dd594afa2
commit 419fb99fdb
4 changed files with 70 additions and 50 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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<const ProtoMessage *>(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

View File

@@ -357,49 +357,5 @@ template<typename... Ts> class APIConnectedCondition : public Condition<Ts...> {
}
};
// 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<const ProtoMessage *>(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