From 62cb08c3dce9d2579c70dcad157c6634f471f29e Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Fri, 9 Jan 2026 04:05:47 -0600 Subject: [PATCH] [api] Add methods supporting efficient packed repeated sint32 field encoding for #12985 (#13094) --- esphome/components/api/proto.h | 63 +++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 2d93adbb47..a336a9493d 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -39,6 +39,24 @@ inline constexpr int64_t decode_zigzag64(uint64_t value) { return (value & 1) ? static_cast(~(value >> 1)) : static_cast(value >> 1); } +/// Count number of varints in a packed buffer +inline uint16_t count_packed_varints(const uint8_t *data, size_t len) { + uint16_t count = 0; + while (len > 0) { + // Skip varint bytes until we find one without continuation bit + while (len > 0 && (*data & 0x80)) { + data++; + len--; + } + if (len > 0) { + data++; + len--; + count++; + } + } + return count; +} + /* * StringRef Ownership Model for API Protocol Messages * =================================================== @@ -180,9 +198,10 @@ class ProtoVarInt { uint64_t value_; }; -// Forward declaration for decode_to_message and encode_to_writer -class ProtoMessage; +// Forward declarations for decode_to_message, encode_message and encode_packed_sint32 class ProtoDecodableMessage; +class ProtoMessage; +class ProtoSize; class ProtoLengthDelimited { public: @@ -334,6 +353,8 @@ class ProtoWriteBuffer { void encode_sint64(uint32_t field_id, int64_t value, bool force = false) { this->encode_uint64(field_id, encode_zigzag64(value), force); } + /// Encode a packed repeated sint32 field (zero-copy from vector) + void encode_packed_sint32(uint32_t field_id, const std::vector &values); void encode_message(uint32_t field_id, const ProtoMessage &value); std::vector *get_buffer() const { return buffer_; } @@ -341,9 +362,6 @@ class ProtoWriteBuffer { std::vector *buffer_; }; -// Forward declaration -class ProtoSize; - class ProtoMessage { public: virtual ~ProtoMessage() = default; @@ -792,8 +810,43 @@ class ProtoSize { } } } + + /** + * @brief Calculate size of a packed repeated sint32 field + */ + inline void add_packed_sint32(uint32_t field_id_size, const std::vector &values) { + if (values.empty()) + return; + + size_t packed_size = 0; + for (int value : values) { + packed_size += varint(encode_zigzag32(value)); + } + + // field_id + length varint + packed data + total_size_ += field_id_size + varint(static_cast(packed_size)) + static_cast(packed_size); + } }; +// Implementation of encode_packed_sint32 - must be after ProtoSize is defined +inline void ProtoWriteBuffer::encode_packed_sint32(uint32_t field_id, const std::vector &values) { + if (values.empty()) + return; + + // Calculate packed size + size_t packed_size = 0; + for (int value : values) { + packed_size += ProtoSize::varint(encode_zigzag32(value)); + } + + // Write tag (LENGTH_DELIMITED) + length + all zigzag-encoded values + this->encode_field_raw(field_id, WIRE_TYPE_LENGTH_DELIMITED); + this->encode_varint_raw(packed_size); + for (int value : values) { + this->encode_varint_raw(encode_zigzag32(value)); + } +} + // Implementation of encode_message - must be after ProtoMessage is defined inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessage &value) { this->encode_field_raw(field_id, 2); // type 2: Length-delimited message