ESPHome 2026.3.0-dev
Loading...
Searching...
No Matches
proto.h
Go to the documentation of this file.
1#pragma once
2
3#include "api_pb2_defines.h"
6#include "esphome/core/log.h"
8
9#include <cassert>
10#include <cstring>
11#include <vector>
12
13#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
14#define HAS_PROTO_MESSAGE_DUMP
15#endif
16
17namespace esphome::api {
18
19// Protocol Buffer wire type constants
20// See https://protobuf.dev/programming-guides/encoding/#structure
21constexpr uint8_t WIRE_TYPE_VARINT = 0; // int32, int64, uint32, uint64, sint32, sint64, bool, enum
22constexpr uint8_t WIRE_TYPE_LENGTH_DELIMITED = 2; // string, bytes, embedded messages, packed repeated fields
23constexpr uint8_t WIRE_TYPE_FIXED32 = 5; // fixed32, sfixed32, float
24constexpr uint8_t WIRE_TYPE_MASK = 0b111; // Mask to extract wire type from tag
25
26// Helper functions for ZigZag encoding/decoding
27inline constexpr uint32_t encode_zigzag32(int32_t value) {
28 return (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
29}
30
31inline constexpr uint64_t encode_zigzag64(int64_t value) {
32 return (static_cast<uint64_t>(value) << 1) ^ (static_cast<uint64_t>(value >> 63));
33}
34
35inline constexpr int32_t decode_zigzag32(uint32_t value) {
36 return (value & 1) ? static_cast<int32_t>(~(value >> 1)) : static_cast<int32_t>(value >> 1);
37}
38
39inline constexpr int64_t decode_zigzag64(uint64_t value) {
40 return (value & 1) ? static_cast<int64_t>(~(value >> 1)) : static_cast<int64_t>(value >> 1);
41}
42
44inline uint16_t count_packed_varints(const uint8_t *data, size_t len) {
45 uint16_t count = 0;
46 while (len > 0) {
47 // Skip varint bytes until we find one without continuation bit
48 while (len > 0 && (*data & 0x80)) {
49 data++;
50 len--;
51 }
52 if (len > 0) {
53 data++;
54 len--;
55 count++;
56 }
57 }
58 return count;
59}
60
63inline void encode_varint_to_buffer(uint32_t val, uint8_t *buffer) {
64 while (val > 0x7F) {
65 *buffer++ = static_cast<uint8_t>(val | 0x80);
66 val >>= 7;
67 }
68 *buffer = static_cast<uint8_t>(val);
69}
70
71/*
72 * StringRef Ownership Model for API Protocol Messages
73 * ===================================================
74 *
75 * StringRef is used for zero-copy string handling in outgoing (SOURCE_SERVER) messages.
76 * It holds a pointer and length to existing string data without copying.
77 *
78 * CRITICAL: The referenced string data MUST remain valid until message encoding completes.
79 *
80 * Safe StringRef Patterns:
81 * 1. String literals: StringRef("literal") - Always safe (static storage duration)
82 * 2. Member variables: StringRef(this->member_string_) - Safe if object outlives encoding
83 * 3. Global/static strings: StringRef(GLOBAL_CONSTANT) - Always safe
84 * 4. Local variables: Safe ONLY if encoding happens before function returns:
85 * std::string temp = compute_value();
86 * msg.field = StringRef(temp);
87 * return this->send_message(msg); // temp is valid during encoding
88 *
89 * Unsafe Patterns (WILL cause crashes/corruption):
90 * 1. Temporaries: msg.field = StringRef(obj.get_string()) // get_string() returns by value
91 * 2. Concatenation: msg.field = StringRef(str1 + str2) // Result is temporary
92 *
93 * For unsafe patterns, store in a local variable first:
94 * std::string temp = get_string(); // or str1 + str2
95 * msg.field = StringRef(temp);
96 *
97 * The send_*_response pattern ensures proper lifetime management by encoding
98 * within the same function scope where temporaries are created.
99 */
100
103 public:
105 explicit ProtoVarInt(uint64_t value) : value_(value) {}
106
108 static optional<ProtoVarInt> parse(const uint8_t *buffer, uint32_t len, uint32_t *consumed) {
109#ifdef ESPHOME_DEBUG_API
110 assert(consumed != nullptr);
111#endif
112 if (len == 0)
113 return {};
114 // Fast path: single-byte varints (0-127) are the most common case
115 // (booleans, small enums, field tags). Avoid loop overhead entirely.
116 if ((buffer[0] & 0x80) == 0) {
117 *consumed = 1;
118 return ProtoVarInt(buffer[0]);
119 }
120 // 32-bit phase: process remaining bytes with native 32-bit shifts.
121 // Without USE_API_VARINT64: cover bytes 1-4 (shifts 7, 14, 21, 28) — the uint32_t
122 // shift at byte 4 (shift by 28) may lose bits 32-34, but those are always zero for valid uint32 values.
123 // With USE_API_VARINT64: cover bytes 1-3 (shifts 7, 14, 21) so parse_wide handles
124 // byte 4+ with full 64-bit arithmetic (avoids truncating values > UINT32_MAX).
125 uint32_t result32 = buffer[0] & 0x7F;
126#ifdef USE_API_VARINT64
127 uint32_t limit = std::min(len, uint32_t(4));
128#else
129 uint32_t limit = std::min(len, uint32_t(5));
130#endif
131 for (uint32_t i = 1; i < limit; i++) {
132 uint8_t val = buffer[i];
133 result32 |= uint32_t(val & 0x7F) << (i * 7);
134 if ((val & 0x80) == 0) {
135 *consumed = i + 1;
136 return ProtoVarInt(result32);
137 }
138 }
139 // 64-bit phase for remaining bytes (BLE addresses etc.)
140#ifdef USE_API_VARINT64
141 return parse_wide(buffer, len, consumed, result32);
142#else
143 return {};
144#endif
145 }
146
147#ifdef USE_API_VARINT64
148 protected:
151 static optional<ProtoVarInt> parse_wide(const uint8_t *buffer, uint32_t len, uint32_t *consumed, uint32_t result32)
152 __attribute__((noinline));
153
154 public:
155#endif
156
157 constexpr uint16_t as_uint16() const { return this->value_; }
158 constexpr uint32_t as_uint32() const { return this->value_; }
159 constexpr bool as_bool() const { return this->value_; }
160 constexpr int32_t as_int32() const {
161 // Not ZigZag encoded
162 return static_cast<int32_t>(this->value_);
163 }
164 constexpr int32_t as_sint32() const {
165 // with ZigZag encoding
166 return decode_zigzag32(static_cast<uint32_t>(this->value_));
167 }
168#ifdef USE_API_VARINT64
169 constexpr uint64_t as_uint64() const { return this->value_; }
170 constexpr int64_t as_int64() const {
171 // Not ZigZag encoded
172 return static_cast<int64_t>(this->value_);
173 }
174 constexpr int64_t as_sint64() const {
175 // with ZigZag encoding
176 return decode_zigzag64(this->value_);
177 }
178#endif
179
180 protected:
181#ifdef USE_API_VARINT64
182 uint64_t value_;
183#else
184 uint32_t value_;
185#endif
186};
187
188// Forward declarations for decode_to_message, encode_message and encode_packed_sint32
190class ProtoMessage;
191class ProtoSize;
192
194 public:
195 explicit ProtoLengthDelimited(const uint8_t *value, size_t length) : value_(value), length_(length) {}
196 std::string as_string() const { return std::string(reinterpret_cast<const char *>(this->value_), this->length_); }
197
198 // Direct access to raw data without string allocation
199 const uint8_t *data() const { return this->value_; }
200 size_t size() const { return this->length_; }
201
212
213 protected:
214 const uint8_t *const value_;
215 const size_t length_;
216};
217
219 public:
220 explicit Proto32Bit(uint32_t value) : value_(value) {}
221 uint32_t as_fixed32() const { return this->value_; }
222 int32_t as_sfixed32() const { return static_cast<int32_t>(this->value_); }
223 float as_float() const {
224 union {
225 uint32_t raw;
226 float value;
227 } s{};
228 s.raw = this->value_;
229 return s.value;
230 }
231
232 protected:
233 const uint32_t value_;
234};
235
236// NOTE: Proto64Bit class removed - wire type 1 (64-bit fixed) not supported
237
239 public:
240 ProtoWriteBuffer(std::vector<uint8_t> *buffer) : buffer_(buffer), pos_(buffer->data() + buffer->size()) {}
241 ProtoWriteBuffer(std::vector<uint8_t> *buffer, size_t write_pos)
242 : buffer_(buffer), pos_(buffer->data() + write_pos) {}
243 void encode_varint_raw(uint32_t value) {
244 while (value > 0x7F) {
245 this->debug_check_bounds_(1);
246 *this->pos_++ = static_cast<uint8_t>(value | 0x80);
247 value >>= 7;
248 }
249 this->debug_check_bounds_(1);
250 *this->pos_++ = static_cast<uint8_t>(value);
251 }
253 while (value > 0x7F) {
254 this->debug_check_bounds_(1);
255 *this->pos_++ = static_cast<uint8_t>(value | 0x80);
256 value >>= 7;
257 }
258 this->debug_check_bounds_(1);
259 *this->pos_++ = static_cast<uint8_t>(value);
260 }
273 void encode_field_raw(uint32_t field_id, uint32_t type) { this->encode_varint_raw((field_id << 3) | type); }
274 void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) {
275 if (len == 0 && !force)
276 return;
277
278 this->encode_field_raw(field_id, 2); // type 2: Length-delimited string
279 this->encode_varint_raw(len);
280 // Direct memcpy into pre-sized buffer — avoids push_back() per-byte capacity checks
281 // and vector::insert() iterator overhead. ~10-11x faster for 16-32 byte strings.
282 this->debug_check_bounds_(len);
283 std::memcpy(this->pos_, string, len);
284 this->pos_ += len;
285 }
286 void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
287 this->encode_string(field_id, value.data(), value.size(), force);
288 }
289 void encode_string(uint32_t field_id, const StringRef &ref, bool force = false) {
290 this->encode_string(field_id, ref.c_str(), ref.size(), force);
291 }
292 void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) {
293 this->encode_string(field_id, reinterpret_cast<const char *>(data), len, force);
294 }
295 void encode_uint32(uint32_t field_id, uint32_t value, bool force = false) {
296 if (value == 0 && !force)
297 return;
298 this->encode_field_raw(field_id, 0); // type 0: Varint - uint32
299 this->encode_varint_raw(value);
300 }
301 void encode_uint64(uint32_t field_id, uint64_t value, bool force = false) {
302 if (value == 0 && !force)
303 return;
304 this->encode_field_raw(field_id, 0); // type 0: Varint - uint64
305 this->encode_varint_raw_64(value);
306 }
307 void encode_bool(uint32_t field_id, bool value, bool force = false) {
308 if (!value && !force)
309 return;
310 this->encode_field_raw(field_id, 0); // type 0: Varint - bool
311 this->debug_check_bounds_(1);
312 *this->pos_++ = value ? 0x01 : 0x00;
313 }
314 // noinline: 51 call sites; inlining causes net code growth vs a single out-of-line copy
315 __attribute__((noinline)) void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) {
316 if (value == 0 && !force)
317 return;
318
319 this->encode_field_raw(field_id, 5); // type 5: 32-bit fixed32
320 this->debug_check_bounds_(4);
321#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
322 // Protobuf fixed32 is little-endian, so direct copy works
323 std::memcpy(this->pos_, &value, 4);
324 this->pos_ += 4;
325#else
326 *this->pos_++ = (value >> 0) & 0xFF;
327 *this->pos_++ = (value >> 8) & 0xFF;
328 *this->pos_++ = (value >> 16) & 0xFF;
329 *this->pos_++ = (value >> 24) & 0xFF;
330#endif
331 }
332 // NOTE: Wire type 1 (64-bit fixed: double, fixed64, sfixed64) is intentionally
333 // not supported to reduce overhead on embedded systems. All ESPHome devices are
334 // 32-bit microcontrollers where 64-bit operations are expensive. If 64-bit support
335 // is needed in the future, the necessary encoding/decoding functions must be added.
336 void encode_float(uint32_t field_id, float value, bool force = false) {
337 if (value == 0.0f && !force)
338 return;
339
340 union {
341 float value;
342 uint32_t raw;
343 } val{};
344 val.value = value;
345 this->encode_fixed32(field_id, val.raw);
346 }
347 void encode_int32(uint32_t field_id, int32_t value, bool force = false) {
348 if (value < 0) {
349 // negative int32 is always 10 byte long
350 this->encode_int64(field_id, value, force);
351 return;
352 }
353 this->encode_uint32(field_id, static_cast<uint32_t>(value), force);
354 }
355 void encode_int64(uint32_t field_id, int64_t value, bool force = false) {
356 this->encode_uint64(field_id, static_cast<uint64_t>(value), force);
357 }
358 void encode_sint32(uint32_t field_id, int32_t value, bool force = false) {
359 this->encode_uint32(field_id, encode_zigzag32(value), force);
360 }
361 void encode_sint64(uint32_t field_id, int64_t value, bool force = false) {
362 this->encode_uint64(field_id, encode_zigzag64(value), force);
363 }
365 void encode_packed_sint32(uint32_t field_id, const std::vector<int32_t> &values);
367 void encode_message(uint32_t field_id, const ProtoMessage &value, bool force = true);
368 std::vector<uint8_t> *get_buffer() const { return buffer_; }
369
370 protected:
371#ifdef ESPHOME_DEBUG_API
372 void debug_check_bounds_(size_t bytes, const char *caller = __builtin_FUNCTION());
373 void debug_check_encode_size_(uint32_t field_id, uint32_t expected, ptrdiff_t actual);
374#else
375 void debug_check_bounds_([[maybe_unused]] size_t bytes) {}
376#endif
377
378 std::vector<uint8_t> *buffer_;
379 uint8_t *pos_;
380};
381
382#ifdef HAS_PROTO_MESSAGE_DUMP
389 public:
390 // Matches default tx_buffer_size in logger component
391 static constexpr size_t CAPACITY = 512;
392
393 DumpBuffer() : pos_(0) { buf_[0] = '\0'; }
394
395 DumpBuffer &append(const char *str) {
396 if (str) {
397 append_impl_(str, strlen(str));
398 }
399 return *this;
400 }
401
402 DumpBuffer &append(const char *str, size_t len) {
403 append_impl_(str, len);
404 return *this;
405 }
406
407 DumpBuffer &append(size_t n, char c) {
408 size_t space = CAPACITY - 1 - pos_;
409 if (n > space)
410 n = space;
411 if (n > 0) {
412 memset(buf_ + pos_, c, n);
413 pos_ += n;
414 buf_[pos_] = '\0';
415 }
416 return *this;
417 }
418
419 const char *c_str() const { return buf_; }
420 size_t size() const { return pos_; }
421
423 char *data() { return buf_; }
425 size_t pos() const { return pos_; }
427 void set_pos(size_t pos) {
428 if (pos >= CAPACITY) {
429 pos_ = CAPACITY - 1;
430 } else {
431 pos_ = pos;
432 }
433 buf_[pos_] = '\0';
434 }
435
436 private:
437 void append_impl_(const char *str, size_t len) {
438 size_t space = CAPACITY - 1 - pos_;
439 if (len > space)
440 len = space;
441 if (len > 0) {
442 memcpy(buf_ + pos_, str, len);
443 pos_ += len;
444 buf_[pos_] = '\0';
445 }
446 }
447
448 char buf_[CAPACITY];
449 size_t pos_;
450};
451#endif
452
454 public:
455 // Default implementation for messages with no fields
456 virtual void encode(ProtoWriteBuffer &buffer) const {}
457 // Default implementation for messages with no fields
458 virtual void calculate_size(ProtoSize &size) const {}
459 // Convenience: calculate and return size directly (defined after ProtoSize)
460 uint32_t calculated_size() const;
461#ifdef HAS_PROTO_MESSAGE_DUMP
462 virtual const char *dump_to(DumpBuffer &out) const = 0;
463 virtual const char *message_name() const { return "unknown"; }
464#endif
465
466 protected:
467 // Non-virtual: messages are never deleted polymorphically.
468 // Protected prevents accidental `delete base_ptr` (compile error).
469 ~ProtoMessage() = default;
470};
471
472// Base class for messages that support decoding
474 public:
475 virtual void decode(const uint8_t *buffer, size_t length);
476
486 static uint32_t count_repeated_field(const uint8_t *buffer, size_t length, uint32_t target_field_id);
487
488 protected:
490 virtual bool decode_varint(uint32_t field_id, ProtoVarInt value) { return false; }
491 virtual bool decode_length(uint32_t field_id, ProtoLengthDelimited value) { return false; }
492 virtual bool decode_32bit(uint32_t field_id, Proto32Bit value) { return false; }
493 // NOTE: decode_64bit removed - wire type 1 not supported
494};
495
497 private:
498 uint32_t total_size_ = 0;
499
500 public:
519 ProtoSize() = default;
520
521 uint32_t get_size() const { return total_size_; }
522
529 static constexpr uint32_t varint(uint32_t value) {
530 // Optimized varint size calculation using leading zeros
531 // Each 7 bits requires one byte in the varint encoding
532 if (value < 128)
533 return 1; // 7 bits, common case for small values
534
535 // For larger values, count bytes needed based on the position of the highest bit set
536 if (value < 16384) {
537 return 2; // 14 bits
538 } else if (value < 2097152) {
539 return 3; // 21 bits
540 } else if (value < 268435456) {
541 return 4; // 28 bits
542 } else {
543 return 5; // 32 bits (maximum for uint32_t)
544 }
545 }
546
553 static constexpr uint32_t varint(uint64_t value) {
554 // Handle common case of values fitting in uint32_t (vast majority of use cases)
555 if (value <= UINT32_MAX) {
556 return varint(static_cast<uint32_t>(value));
557 }
558
559 // For larger values, determine size based on highest bit position
560 if (value < (1ULL << 35)) {
561 return 5; // 35 bits
562 } else if (value < (1ULL << 42)) {
563 return 6; // 42 bits
564 } else if (value < (1ULL << 49)) {
565 return 7; // 49 bits
566 } else if (value < (1ULL << 56)) {
567 return 8; // 56 bits
568 } else if (value < (1ULL << 63)) {
569 return 9; // 63 bits
570 } else {
571 return 10; // 64 bits (maximum for uint64_t)
572 }
573 }
574
584 static constexpr uint32_t varint(int32_t value) {
585 // Negative values are sign-extended to 64 bits in protocol buffers,
586 // which always results in a 10-byte varint for negative int32
587 if (value < 0) {
588 return 10; // Negative int32 is always 10 bytes long
589 }
590 // For non-negative values, use the uint32_t implementation
591 return varint(static_cast<uint32_t>(value));
592 }
593
600 static constexpr uint32_t varint(int64_t value) {
601 // For int64_t, we convert to uint64_t and calculate the size
602 // This works because the bit pattern determines the encoding size,
603 // and we've handled negative int32 values as a special case above
604 return varint(static_cast<uint64_t>(value));
605 }
606
614 static constexpr uint32_t field(uint32_t field_id, uint32_t type) {
615 uint32_t tag = (field_id << 3) | (type & WIRE_TYPE_MASK);
616 return varint(tag);
617 }
618
636 inline void add_int32(uint32_t field_id_size, int32_t value) {
637 if (value != 0) {
638 add_int32_force(field_id_size, value);
639 }
640 }
641
645 inline void add_int32_force(uint32_t field_id_size, int32_t value) {
646 // Always calculate size when forced
647 // Negative values are encoded as 10-byte varints in protobuf
648 total_size_ += field_id_size + (value < 0 ? 10 : varint(static_cast<uint32_t>(value)));
649 }
650
654 inline void add_uint32(uint32_t field_id_size, uint32_t value) {
655 if (value != 0) {
656 add_uint32_force(field_id_size, value);
657 }
658 }
659
663 inline void add_uint32_force(uint32_t field_id_size, uint32_t value) {
664 // Always calculate size when force is true
665 total_size_ += field_id_size + varint(value);
666 }
667
671 inline void add_bool(uint32_t field_id_size, bool value) {
672 if (value) {
673 // Boolean fields always use 1 byte when true
674 total_size_ += field_id_size + 1;
675 }
676 }
677
681 inline void add_bool_force(uint32_t field_id_size, bool value) {
682 // Always calculate size when force is true
683 // Boolean fields always use 1 byte
684 total_size_ += field_id_size + 1;
685 }
686
690 inline void add_float(uint32_t field_id_size, float value) {
691 if (value != 0.0f) {
692 total_size_ += field_id_size + 4;
693 }
694 }
695
696 // NOTE: add_double_field removed - wire type 1 (64-bit: double) not supported
697 // to reduce overhead on embedded systems
698
702 inline void add_fixed32(uint32_t field_id_size, uint32_t value) {
703 if (value != 0) {
704 total_size_ += field_id_size + 4;
705 }
706 }
707
708 // NOTE: add_fixed64_field removed - wire type 1 (64-bit: fixed64) not supported
709 // to reduce overhead on embedded systems
710
714 inline void add_sfixed32(uint32_t field_id_size, int32_t value) {
715 if (value != 0) {
716 total_size_ += field_id_size + 4;
717 }
718 }
719
720 // NOTE: add_sfixed64_field removed - wire type 1 (64-bit: sfixed64) not supported
721 // to reduce overhead on embedded systems
722
728 inline void add_sint32(uint32_t field_id_size, int32_t value) {
729 if (value != 0) {
730 add_sint32_force(field_id_size, value);
731 }
732 }
733
739 inline void add_sint32_force(uint32_t field_id_size, int32_t value) {
740 // Always calculate size when force is true
741 // ZigZag encoding for sint32
742 total_size_ += field_id_size + varint(encode_zigzag32(value));
743 }
744
748 inline void add_int64(uint32_t field_id_size, int64_t value) {
749 if (value != 0) {
750 add_int64_force(field_id_size, value);
751 }
752 }
753
757 inline void add_int64_force(uint32_t field_id_size, int64_t value) {
758 // Always calculate size when force is true
759 total_size_ += field_id_size + varint(value);
760 }
761
765 inline void add_uint64(uint32_t field_id_size, uint64_t value) {
766 if (value != 0) {
767 add_uint64_force(field_id_size, value);
768 }
769 }
770
774 inline void add_uint64_force(uint32_t field_id_size, uint64_t value) {
775 // Always calculate size when force is true
776 total_size_ += field_id_size + varint(value);
777 }
778
779 // NOTE: sint64 support functions (add_sint64_field, add_sint64_field_force) removed
780 // sint64 type is not supported by ESPHome API to reduce overhead on embedded systems
781
785 inline void add_length(uint32_t field_id_size, size_t len) {
786 if (len != 0) {
787 add_length_force(field_id_size, len);
788 }
789 }
790
795 inline void add_length_force(uint32_t field_id_size, size_t len) {
796 // Always calculate size when force is true
797 // Field ID + length varint + data bytes
798 total_size_ += field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len);
799 }
800
809 inline void add_precalculated_size(uint32_t size) { total_size_ += size; }
810
819 inline void add_message_field(uint32_t field_id_size, uint32_t nested_size) {
820 if (nested_size != 0) {
821 add_message_field_force(field_id_size, nested_size);
822 }
823 }
824
830 inline void add_message_field_force(uint32_t field_id_size, uint32_t nested_size) {
831 // Always calculate size when force is true
832 // Field ID + length varint + nested message content
833 total_size_ += field_id_size + varint(nested_size) + nested_size;
834 }
835
845 inline void add_message_object(uint32_t field_id_size, const ProtoMessage &message) {
846 // Calculate nested message size by creating a temporary ProtoSize
847 ProtoSize nested_calc;
848 message.calculate_size(nested_calc);
849 uint32_t nested_size = nested_calc.get_size();
850
851 // Use the base implementation with the calculated nested_size
852 add_message_field(field_id_size, nested_size);
853 }
854
860 inline void add_message_object_force(uint32_t field_id_size, const ProtoMessage &message) {
861 // Calculate nested message size by creating a temporary ProtoSize
862 ProtoSize nested_calc;
863 message.calculate_size(nested_calc);
864 uint32_t nested_size = nested_calc.get_size();
865
866 // Use the base implementation with the calculated nested_size
867 add_message_field_force(field_id_size, nested_size);
868 }
869
879 template<typename MessageType>
880 inline void add_repeated_message(uint32_t field_id_size, const std::vector<MessageType> &messages) {
881 // Skip if the vector is empty
882 if (!messages.empty()) {
883 // Use the force version for all messages in the repeated field
884 for (const auto &message : messages) {
885 add_message_object_force(field_id_size, message);
886 }
887 }
888 }
889
897 template<typename MessageType>
898 inline void add_repeated_message(uint32_t field_id_size, const FixedVector<MessageType> &messages) {
899 // Skip if the fixed vector is empty
900 if (!messages.empty()) {
901 // Use the force version for all messages in the repeated field
902 for (const auto &message : messages) {
903 add_message_object_force(field_id_size, message);
904 }
905 }
906 }
907
911 inline void add_packed_sint32(uint32_t field_id_size, const std::vector<int32_t> &values) {
912 if (values.empty())
913 return;
914
915 size_t packed_size = 0;
916 for (int value : values) {
917 packed_size += varint(encode_zigzag32(value));
918 }
919
920 // field_id + length varint + packed data
921 total_size_ += field_id_size + varint(static_cast<uint32_t>(packed_size)) + static_cast<uint32_t>(packed_size);
922 }
923};
924
925// Implementation of methods that depend on ProtoSize being fully defined
926
927inline uint32_t ProtoMessage::calculated_size() const {
929 this->calculate_size(size);
930 return size.get_size();
931}
932
933// Implementation of encode_packed_sint32 - must be after ProtoSize is defined
934inline void ProtoWriteBuffer::encode_packed_sint32(uint32_t field_id, const std::vector<int32_t> &values) {
935 if (values.empty())
936 return;
937
938 // Calculate packed size
939 size_t packed_size = 0;
940 for (int value : values) {
941 packed_size += ProtoSize::varint(encode_zigzag32(value));
942 }
943
944 // Write tag (LENGTH_DELIMITED) + length + all zigzag-encoded values
946 this->encode_varint_raw(packed_size);
947 for (int value : values) {
949 }
950}
951
952// Implementation of encode_message - must be after ProtoMessage is defined
953inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessage &value, bool force) {
954 // Calculate the message size first
955 ProtoSize msg_size;
956 value.calculate_size(msg_size);
957 uint32_t msg_length_bytes = msg_size.get_size();
958
959 // Skip empty singular messages (matches add_message_field which skips when nested_size == 0)
960 // Repeated messages (force=true) are always encoded since an empty item is meaningful
961 if (msg_length_bytes == 0 && !force)
962 return;
963
964 this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
965
966 // Write the length varint directly through pos_
967 this->encode_varint_raw(msg_length_bytes);
968
969 // Encode nested message - pos_ advances directly through the reference
970#ifdef ESPHOME_DEBUG_API
971 uint8_t *start = this->pos_;
972 value.encode(*this);
973 if (static_cast<uint32_t>(this->pos_ - start) != msg_length_bytes)
974 this->debug_check_encode_size_(field_id, msg_length_bytes, this->pos_ - start);
975#else
976 value.encode(*this);
977#endif
978}
979
980// Implementation of decode_to_message - must be after ProtoDecodableMessage is defined
982 msg.decode(this->value_, this->length_);
983}
984
985template<typename T> const char *proto_enum_to_string(T value);
986
988 public:
989 protected:
990 virtual bool is_authenticated() = 0;
991 virtual bool is_connection_setup() = 0;
992 virtual void on_fatal_error() = 0;
993 virtual void on_no_setup_connection() = 0;
994 virtual bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) = 0;
995 virtual void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) = 0;
1003 virtual bool send_message_impl(const ProtoMessage &msg, uint8_t message_type) = 0;
1004
1005 // Authentication helper methods
1007 if (!this->is_connection_setup()) {
1008 this->on_no_setup_connection();
1009 return false;
1010 }
1011 return true;
1012 }
1013
1014 inline bool check_authenticated_() { return this->check_connection_setup_(); }
1015};
1016
1017} // namespace esphome::api
uint8_t raw[35]
Definition bl0939.h:0
Fixed-capacity vector - allocates once at runtime, never reallocates This avoids std::vector template...
Definition helpers.h:299
bool empty() const
Definition helpers.h:457
StringRef is a reference to a string owned by something else.
Definition string_ref.h:26
constexpr const char * c_str() const
Definition string_ref.h:73
constexpr size_type size() const
Definition string_ref.h:74
Fixed-size buffer for message dumps - avoids heap allocation.
Definition proto.h:388
const char * c_str() const
Definition proto.h:419
DumpBuffer & append(size_t n, char c)
Definition proto.h:407
size_t size() const
Definition proto.h:420
size_t pos() const
Get current position for use with buf_append_printf.
Definition proto.h:425
static constexpr size_t CAPACITY
Definition proto.h:391
DumpBuffer & append(const char *str, size_t len)
Definition proto.h:402
DumpBuffer & append(const char *str)
Definition proto.h:395
char * data()
Get writable buffer pointer for use with buf_append_printf.
Definition proto.h:423
void set_pos(size_t pos)
Update position after buf_append_printf call.
Definition proto.h:427
uint32_t as_fixed32() const
Definition proto.h:221
int32_t as_sfixed32() const
Definition proto.h:222
float as_float() const
Definition proto.h:223
const uint32_t value_
Definition proto.h:233
Proto32Bit(uint32_t value)
Definition proto.h:220
virtual bool decode_32bit(uint32_t field_id, Proto32Bit value)
Definition proto.h:492
virtual bool decode_varint(uint32_t field_id, ProtoVarInt value)
Definition proto.h:490
virtual void decode(const uint8_t *buffer, size_t length)
Definition proto.cpp:105
virtual bool decode_length(uint32_t field_id, ProtoLengthDelimited value)
Definition proto.h:491
static uint32_t count_repeated_field(const uint8_t *buffer, size_t length, uint32_t target_field_id)
Count occurrences of a repeated field in a protobuf buffer.
Definition proto.cpp:27
void decode_to_message(ProtoDecodableMessage &msg) const
Decode the length-delimited data into an existing ProtoDecodableMessage instance.
Definition proto.h:981
const uint8_t *const value_
Definition proto.h:214
const uint8_t * data() const
Definition proto.h:199
ProtoLengthDelimited(const uint8_t *value, size_t length)
Definition proto.h:195
std::string as_string() const
Definition proto.h:196
uint32_t calculated_size() const
Definition proto.h:927
virtual const char * message_name() const
Definition proto.h:463
virtual void calculate_size(ProtoSize &size) const
Definition proto.h:458
virtual void encode(ProtoWriteBuffer &buffer) const
Definition proto.h:456
virtual const char * dump_to(DumpBuffer &out) const =0
virtual void on_fatal_error()=0
virtual bool send_message_impl(const ProtoMessage &msg, uint8_t message_type)=0
Send a protobuf message by calculating its size, allocating a buffer, encoding, and sending.
virtual bool is_connection_setup()=0
virtual bool is_authenticated()=0
virtual void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data)=0
virtual void on_no_setup_connection()=0
virtual bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type)=0
void add_message_object(uint32_t field_id_size, const ProtoMessage &message)
Calculates and adds the size of a nested message field to the total message size.
Definition proto.h:845
static constexpr uint32_t varint(uint32_t value)
Calculates the size in bytes needed to encode a uint32_t value as a varint.
Definition proto.h:529
void add_message_field_force(uint32_t field_id_size, uint32_t nested_size)
Calculates and adds the size of a nested message field to the total message size (force version)
Definition proto.h:830
void add_message_object_force(uint32_t field_id_size, const ProtoMessage &message)
Calculates and adds the size of a nested message field to the total message size (force version)
Definition proto.h:860
void add_float(uint32_t field_id_size, float value)
Calculates and adds the size of a float field to the total message size.
Definition proto.h:690
static constexpr uint32_t field(uint32_t field_id, uint32_t type)
Calculates the size in bytes needed to encode a field ID and wire type.
Definition proto.h:614
static constexpr uint32_t varint(int32_t value)
Calculates the size in bytes needed to encode an int32_t value as a varint.
Definition proto.h:584
void add_sfixed32(uint32_t field_id_size, int32_t value)
Calculates and adds the size of a sfixed32 field to the total message size.
Definition proto.h:714
void add_uint32_force(uint32_t field_id_size, uint32_t value)
Calculates and adds the size of a uint32 field to the total message size (force version)
Definition proto.h:663
void add_int32_force(uint32_t field_id_size, int32_t value)
Calculates and adds the size of an int32 field to the total message size (force version)
Definition proto.h:645
void add_repeated_message(uint32_t field_id_size, const FixedVector< MessageType > &messages)
Calculates and adds the sizes of all messages in a repeated field to the total message size (FixedVec...
Definition proto.h:898
static constexpr uint32_t varint(int64_t value)
Calculates the size in bytes needed to encode an int64_t value as a varint.
Definition proto.h:600
void add_int64_force(uint32_t field_id_size, int64_t value)
Calculates and adds the size of an int64 field to the total message size (force version)
Definition proto.h:757
void add_sint32(uint32_t field_id_size, int32_t value)
Calculates and adds the size of a sint32 field to the total message size.
Definition proto.h:728
void add_message_field(uint32_t field_id_size, uint32_t nested_size)
Calculates and adds the size of a nested message field to the total message size.
Definition proto.h:819
void add_bool_force(uint32_t field_id_size, bool value)
Calculates and adds the size of a boolean field to the total message size (force version)
Definition proto.h:681
void add_int64(uint32_t field_id_size, int64_t value)
Calculates and adds the size of an int64 field to the total message size.
Definition proto.h:748
void add_bool(uint32_t field_id_size, bool value)
Calculates and adds the size of a boolean field to the total message size.
Definition proto.h:671
uint32_t get_size() const
Definition proto.h:521
void add_uint32(uint32_t field_id_size, uint32_t value)
Calculates and adds the size of a uint32 field to the total message size.
Definition proto.h:654
static constexpr uint32_t varint(uint64_t value)
Calculates the size in bytes needed to encode a uint64_t value as a varint.
Definition proto.h:553
void add_repeated_message(uint32_t field_id_size, const std::vector< MessageType > &messages)
Calculates and adds the sizes of all messages in a repeated field to the total message size.
Definition proto.h:880
void add_uint64(uint32_t field_id_size, uint64_t value)
Calculates and adds the size of a uint64 field to the total message size.
Definition proto.h:765
void add_length_force(uint32_t field_id_size, size_t len)
Calculates and adds the size of a length-delimited field (string/bytes) to the total message size (re...
Definition proto.h:795
void add_int32(uint32_t field_id_size, int32_t value)
Common parameters for all add_*_field methods.
Definition proto.h:636
void add_precalculated_size(uint32_t size)
Adds a pre-calculated size directly to the total.
Definition proto.h:809
void add_sint32_force(uint32_t field_id_size, int32_t value)
Calculates and adds the size of a sint32 field to the total message size (force version)
Definition proto.h:739
void add_uint64_force(uint32_t field_id_size, uint64_t value)
Calculates and adds the size of a uint64 field to the total message size (force version)
Definition proto.h:774
void add_fixed32(uint32_t field_id_size, uint32_t value)
Calculates and adds the size of a fixed32 field to the total message size.
Definition proto.h:702
ProtoSize()=default
ProtoSize class for Protocol Buffer serialization size calculation.
void add_packed_sint32(uint32_t field_id_size, const std::vector< int32_t > &values)
Calculate size of a packed repeated sint32 field.
Definition proto.h:911
void add_length(uint32_t field_id_size, size_t len)
Calculates and adds the size of a length-delimited field (string/bytes) to the total message size.
Definition proto.h:785
Representation of a VarInt - in ProtoBuf should be 64bit but we only use 32bit.
Definition proto.h:102
constexpr uint16_t as_uint16() const
Definition proto.h:157
constexpr uint64_t as_uint64() const
Definition proto.h:169
constexpr int32_t as_int32() const
Definition proto.h:160
constexpr uint32_t as_uint32() const
Definition proto.h:158
static optional< ProtoVarInt > parse_wide(const uint8_t *buffer, uint32_t len, uint32_t *consumed, uint32_t result32) __attribute__((noinline))
Continue parsing varint bytes 4-9 with 64-bit arithmetic.
Definition proto.cpp:11
constexpr int64_t as_int64() const
Definition proto.h:170
constexpr bool as_bool() const
Definition proto.h:159
ProtoVarInt(uint64_t value)
Definition proto.h:105
constexpr int32_t as_sint32() const
Definition proto.h:164
constexpr int64_t as_sint64() const
Definition proto.h:174
static optional< ProtoVarInt > parse(const uint8_t *buffer, uint32_t len, uint32_t *consumed)
Parse a varint from buffer. consumed must be a valid pointer (not null).
Definition proto.h:108
void encode_varint_raw(uint32_t value)
Definition proto.h:243
void encode_string(uint32_t field_id, const std::string &value, bool force=false)
Definition proto.h:286
ProtoWriteBuffer(std::vector< uint8_t > *buffer, size_t write_pos)
Definition proto.h:241
void debug_check_encode_size_(uint32_t field_id, uint32_t expected, ptrdiff_t actual)
Definition proto.cpp:98
void debug_check_bounds_(size_t bytes)
Definition proto.h:375
void encode_string(uint32_t field_id, const char *string, size_t len, bool force=false)
Definition proto.h:274
__attribute__((noinline)) void encode_fixed32(uint32_t field_id
void encode_bool(uint32_t field_id, bool value, bool force=false)
Definition proto.h:307
ProtoWriteBuffer(std::vector< uint8_t > *buffer)
Definition proto.h:240
void encode_uint64(uint32_t field_id, uint64_t value, bool force=false)
Definition proto.h:301
void encode_string(uint32_t field_id, const StringRef &ref, bool force=false)
Definition proto.h:289
void encode_uint32(uint32_t field_id, uint32_t value, bool force=false)
Definition proto.h:295
void encode_field_raw(uint32_t field_id, uint32_t type)
Encode a field key (tag/wire type combination).
Definition proto.h:273
std::vector< uint8_t > * get_buffer() const
Definition proto.h:368
void debug_check_bounds_(size_t bytes, const char *caller=__builtin_FUNCTION())
Definition proto.cpp:91
void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force=false)
Definition proto.h:292
void encode_message(uint32_t field_id, const ProtoMessage &value, bool force=true)
Encode a nested message field (force=true for repeated, false for singular)
Definition proto.h:953
std::vector< uint8_t > * buffer_
Definition proto.h:378
void encode_varint_raw_64(uint64_t value)
Definition proto.h:252
struct @65::@66 __attribute__
const char * message
Definition component.cpp:38
uint16_t type
mopeka_std_values val[4]
constexpr uint32_t encode_zigzag32(int32_t value)
Definition proto.h:27
constexpr uint8_t WIRE_TYPE_VARINT
Definition proto.h:21
const char * proto_enum_to_string(T value)
constexpr uint64_t encode_zigzag64(int64_t value)
Definition proto.h:31
constexpr uint8_t WIRE_TYPE_MASK
Definition proto.h:24
constexpr uint8_t WIRE_TYPE_LENGTH_DELIMITED
Definition proto.h:22
constexpr int32_t decode_zigzag32(uint32_t value)
Definition proto.h:35
constexpr uint8_t WIRE_TYPE_FIXED32
Definition proto.h:23
constexpr int64_t decode_zigzag64(uint64_t value)
Definition proto.h:39
void encode_varint_to_buffer(uint32_t val, uint8_t *buffer)
Encode a varint directly into a pre-allocated buffer.
Definition proto.h:63
uint16_t count_packed_varints(const uint8_t *data, size_t len)
Count number of varints in a packed buffer.
Definition proto.h:44
std::string size_t len
Definition helpers.h:817
size_t size
Definition helpers.h:854
uint16_t length
Definition tt21100.cpp:0