diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 32be9dbfef..5cf8e686e5 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1778,7 +1778,7 @@ void APIConnection::process_batch_() { } // Pre-allocate storage for packet info - std::vector> packet_info; + std::vector packet_info; packet_info.reserve(num_items); // Cache these values to avoid repeated virtual calls @@ -1787,7 +1787,7 @@ void APIConnection::process_batch_() { // Initialize buffer and tracking variables this->proto_write_buffer_.clear(); - // Reserve based on typical message size (24 bytes) + overhead per message + // Reserve based on typical message size + overhead per message this->proto_write_buffer_.reserve((24 + header_padding + footer_size) * num_items); this->batch_first_message_ = true; diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index eff2f26216..c75e03cebf 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -612,14 +612,13 @@ APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuf raw_buffer->resize(raw_buffer->size() + frame_footer_size_); // Use write_protobuf_packets with a single packet - std::vector> packets; + std::vector packets; packets.emplace_back(type, 0, payload_len); return write_protobuf_packets(buffer, packets); } -APIError APINoiseFrameHelper::write_protobuf_packets( - ProtoWriteBuffer buffer, const std::vector> &packets) { +APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector &packets) { APIError aerr = state_action_(); if (aerr != APIError::OK) { return aerr; @@ -639,9 +638,9 @@ APIError APINoiseFrameHelper::write_protobuf_packets( // We need to encrypt each packet in place for (const auto &packet : packets) { - uint16_t type = std::get<0>(packet); - uint32_t offset = std::get<1>(packet); - uint16_t payload_len = std::get<2>(packet); + uint16_t type = packet.message_type; + uint16_t offset = packet.offset; + uint16_t payload_len = packet.payload_size; uint16_t msg_len = 4 + payload_len; // type(2) + data_len(2) + payload // The buffer already has padding at offset @@ -1031,14 +1030,14 @@ APIError APIPlaintextFrameHelper::write_protobuf_packet(uint16_t type, ProtoWrit uint16_t payload_len = static_cast(raw_buffer->size() - frame_header_padding_); // Use write_protobuf_packets with a single packet - std::vector> packets; + std::vector packets; packets.emplace_back(type, 0, payload_len); return write_protobuf_packets(buffer, packets); } -APIError APIPlaintextFrameHelper::write_protobuf_packets( - ProtoWriteBuffer buffer, const std::vector> &packets) { +APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, + const std::vector &packets) { if (state_ != State::DATA) { return APIError::BAD_STATE; } @@ -1052,9 +1051,9 @@ APIError APIPlaintextFrameHelper::write_protobuf_packets( iovs.reserve(packets.size()); for (const auto &packet : packets) { - uint16_t type = std::get<0>(packet); - uint32_t offset = std::get<1>(packet); - uint16_t payload_len = std::get<2>(packet); + uint16_t type = packet.message_type; + uint16_t offset = packet.offset; + uint16_t payload_len = packet.payload_size; // Calculate varint sizes for header components uint8_t size_varint_len = api::ProtoSize::varint(static_cast(payload_len)); diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index 87c4c000a0..323e4741b3 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -27,6 +27,17 @@ struct ReadPacketBuffer { uint16_t data_len; }; +// Packed packet info structure to minimize memory usage +struct PacketInfo { + uint16_t message_type; // 2 bytes + uint16_t offset; // 2 bytes (sufficient for packet size ~1460 bytes) + uint16_t payload_size; // 2 bytes (up to 65535 bytes) + uint16_t padding; // 2 bytes (for alignment) + + PacketInfo(uint16_t type, uint16_t off, uint16_t size) + : message_type(type), offset(off), payload_size(size), padding(0) {} +}; + enum class APIError : int { OK = 0, WOULD_BLOCK = 1001, @@ -90,8 +101,7 @@ class APIFrameHelper { // Write multiple protobuf packets in a single operation // packets contains (message_type, offset, length) for each message in the buffer // The buffer contains all messages with appropriate padding before each - virtual APIError write_protobuf_packets(ProtoWriteBuffer buffer, - const std::vector> &packets) = 0; + virtual APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector &packets) = 0; // Get the frame header padding required by this protocol virtual uint8_t frame_header_padding() = 0; // Get the frame footer size required by this protocol @@ -189,8 +199,7 @@ class APINoiseFrameHelper : public APIFrameHelper { APIError loop() override; APIError read_packet(ReadPacketBuffer *buffer) override; APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override; - APIError write_protobuf_packets(ProtoWriteBuffer buffer, - const std::vector> &packets) override; + APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector &packets) override; // Get the frame header padding required by this protocol uint8_t frame_header_padding() override { return frame_header_padding_; } // Get the frame footer size required by this protocol @@ -240,8 +249,7 @@ class APIPlaintextFrameHelper : public APIFrameHelper { APIError loop() override; APIError read_packet(ReadPacketBuffer *buffer) override; APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override; - APIError write_protobuf_packets(ProtoWriteBuffer buffer, - const std::vector> &packets) override; + APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector &packets) override; uint8_t frame_header_padding() override { return frame_header_padding_; } // Get the frame footer size required by this protocol uint8_t frame_footer_size() override { return frame_footer_size_; }