fix bloat

This commit is contained in:
J. Nick Koston
2026-04-03 23:07:06 -10:00
parent 599f784a90
commit 72df34a507
4 changed files with 49 additions and 28 deletions
+1 -5
View File
@@ -1998,11 +1998,7 @@ bool APIConnection::send_message_(uint32_t payload_size, uint8_t message_type, M
}
// encode_to_buffer is defined inline in api_connection.h (ESPHOME_ALWAYS_INLINE)
// Noinline wrapper for zero-payload messages — single shared copy instead of
// duplicating encode_to_buffer at each cold call site.
uint16_t APIConnection::encode_empty_to_buffer(const void *msg, APIConnection *conn, uint32_t remaining_size) {
return encode_to_buffer(0, &encode_msg_noop, msg, conn, remaining_size);
}
// encode_empty_to_buffer is defined inline in api_connection.h
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
const bool is_log_message = (message_type == SubscribeLogsResponse::MESSAGE_TYPE);
+41 -23
View File
@@ -410,53 +410,71 @@ class APIConnection final : public APIServerConnectionBase {
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));
conn->log_send_message_(msg);
return 1;
}
#endif
// 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
size_t total_calculated_size = calculated_size + header_padding + footer_size;
// Check if it fits
if (total_calculated_size > remaining_size)
return 0; // Doesn't fit
auto &shared_buf = conn->parent_->get_shared_buffer_ref();
// First message: padding already in buffer, only add payload.
// Subsequent messages: use exact header size for gap-free packing.
// 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_ = header_padding;
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);
// Return total size (header + payload + footer)
return static_cast<uint16_t>(total_calculated_size);
return total_calculated_size;
}
// Noinline path for zero-payload messages (ping, disconnect, list_info_done).
// Avoids duplicating encode_to_buffer at each cold call site.
static uint16_t encode_empty_to_buffer(const void *msg, APIConnection *conn, uint32_t remaining_size);
// Specialized path for zero-payload messages (ping, disconnect, list_info_done).
// No payload means no encode callback and no payload resize needed.
// Noinline — the three callers share a single copy via a call.
__attribute__((noinline)) static uint16_t encode_empty_to_buffer(const void *msg, uint8_t message_type,
APIConnection *conn, uint32_t remaining_size) {
#ifdef HAS_PROTO_MESSAGE_DUMP
if (conn->flags_.log_only_mode) {
conn->log_send_message_(msg);
return 1;
}
#endif
const uint8_t footer_size = conn->helper_->frame_footer_size();
bool first = conn->flags_.batch_first_message;
if (first) {
conn->flags_.batch_first_message = false;
conn->batch_header_size_ = conn->helper_->frame_header_padding();
} else {
conn->batch_header_size_ = conn->helper_->frame_header_size(0, message_type);
}
uint16_t total = conn->batch_header_size_ + footer_size;
if (total > remaining_size)
return 0;
if (!first) {
// Non-first: grow buffer by header + footer (no payload)
auto &shared_buf = conn->parent_->get_shared_buffer_ref();
shared_buf.resize(shared_buf.size() + total);
}
// First message: padding already in buffer from prepare_first_message_buffer
return total;
}
// Thin template wrapper — computes size, delegates buffer work to non-template helper
template<typename T> static uint16_t encode_message_to_buffer(T &msg, APIConnection *conn, uint32_t remaining_size) {
if constexpr (T::ESTIMATED_SIZE == 0) {
return encode_empty_to_buffer(&msg, conn, remaining_size);
return encode_empty_to_buffer(&msg, T::MESSAGE_TYPE, conn, remaining_size);
} else {
return encode_to_buffer(msg.calculate_size(), &proto_encode_msg<T>, &msg, conn, remaining_size);
}
@@ -12,6 +12,11 @@ static const char *const TAG = "api.service";
void APIServerConnectionBase::log_send_message_(const LogString *name, const char *dump) {
ESP_LOGVV(TAG, "send_message %s: %s", LOG_STR_ARG(name), dump);
}
void APIServerConnectionBase::log_send_message_(const void *msg) {
auto *proto_msg = static_cast<const ProtoMessage *>(msg);
DumpBuffer dump_buf;
this->log_send_message_(proto_msg->message_name(), proto_msg->dump_to(dump_buf));
}
void APIServerConnectionBase::log_receive_message_(const LogString *name, const ProtoMessage &msg) {
DumpBuffer dump_buf;
ESP_LOGVV(TAG, "%s: %s", LOG_STR_ARG(name), msg.dump_to(dump_buf));
+2
View File
@@ -13,6 +13,8 @@ class APIServerConnectionBase {
#ifdef HAS_PROTO_MESSAGE_DUMP
protected:
void log_send_message_(const LogString *name, const char *dump);
// Log a message being sent from a void pointer (for use in encode_to_buffer paths)
void log_send_message_(const void *msg);
void log_receive_message_(const LogString *name, const ProtoMessage &msg);
void log_receive_message_(const LogString *name);