ESPHome 2026.2.3
Loading...
Searching...
No Matches
proto.h
Go to the documentation of this file.
1#pragma once
2
5#include "esphome/core/log.h"
7
8#include <cassert>
9#include <cstring>
10#include <vector>
11
12#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
13#define HAS_PROTO_MESSAGE_DUMP
14#endif
15
16namespace esphome::api {
17
18// Protocol Buffer wire type constants
19// See https://protobuf.dev/programming-guides/encoding/#structure
20constexpr uint8_t WIRE_TYPE_VARINT = 0; // int32, int64, uint32, uint64, sint32, sint64, bool, enum
21constexpr uint8_t WIRE_TYPE_LENGTH_DELIMITED = 2; // string, bytes, embedded messages, packed repeated fields
22constexpr uint8_t WIRE_TYPE_FIXED32 = 5; // fixed32, sfixed32, float
23constexpr uint8_t WIRE_TYPE_MASK = 0b111; // Mask to extract wire type from tag
24
25// Helper functions for ZigZag encoding/decoding
26inline constexpr uint32_t encode_zigzag32(int32_t value) {
27 return (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
28}
29
30inline constexpr uint64_t encode_zigzag64(int64_t value) {
31 return (static_cast<uint64_t>(value) << 1) ^ (static_cast<uint64_t>(value >> 63));
32}
33
34inline constexpr int32_t decode_zigzag32(uint32_t value) {
35 return (value & 1) ? static_cast<int32_t>(~(value >> 1)) : static_cast<int32_t>(value >> 1);
36}
37
38inline constexpr int64_t decode_zigzag64(uint64_t value) {
39 return (value & 1) ? static_cast<int64_t>(~(value >> 1)) : static_cast<int64_t>(value >> 1);
40}
41
43inline uint16_t count_packed_varints(const uint8_t *data, size_t len) {
44 uint16_t count = 0;
45 while (len > 0) {
46 // Skip varint bytes until we find one without continuation bit
47 while (len > 0 && (*data & 0x80)) {
48 data++;
49 len--;
50 }
51 if (len > 0) {
52 data++;
53 len--;
54 count++;
55 }
56 }
57 return count;
58}
59
62inline void encode_varint_to_buffer(uint32_t val, uint8_t *buffer) {
63 while (val > 0x7F) {
64 *buffer++ = static_cast<uint8_t>(val | 0x80);
65 val >>= 7;
66 }
67 *buffer = static_cast<uint8_t>(val);
68}
69
70/*
71 * StringRef Ownership Model for API Protocol Messages
72 * ===================================================
73 *
74 * StringRef is used for zero-copy string handling in outgoing (SOURCE_SERVER) messages.
75 * It holds a pointer and length to existing string data without copying.
76 *
77 * CRITICAL: The referenced string data MUST remain valid until message encoding completes.
78 *
79 * Safe StringRef Patterns:
80 * 1. String literals: StringRef("literal") - Always safe (static storage duration)
81 * 2. Member variables: StringRef(this->member_string_) - Safe if object outlives encoding
82 * 3. Global/static strings: StringRef(GLOBAL_CONSTANT) - Always safe
83 * 4. Local variables: Safe ONLY if encoding happens before function returns:
84 * std::string temp = compute_value();
85 * msg.field = StringRef(temp);
86 * return this->send_message(msg); // temp is valid during encoding
87 *
88 * Unsafe Patterns (WILL cause crashes/corruption):
89 * 1. Temporaries: msg.field = StringRef(obj.get_string()) // get_string() returns by value
90 * 2. Concatenation: msg.field = StringRef(str1 + str2) // Result is temporary
91 *
92 * For unsafe patterns, store in a local variable first:
93 * std::string temp = get_string(); // or str1 + str2
94 * msg.field = StringRef(temp);
95 *
96 * The send_*_response pattern ensures proper lifetime management by encoding
97 * within the same function scope where temporaries are created.
98 */
99
102 public:
104 explicit ProtoVarInt(uint64_t value) : value_(value) {}
105
107 static optional<ProtoVarInt> parse(const uint8_t *buffer, uint32_t len, uint32_t *consumed) {
108#ifdef ESPHOME_DEBUG_API
109 assert(consumed != nullptr);
110#endif
111 if (len == 0)
112 return {};
113
114 // Most common case: single-byte varint (values 0-127)
115 if ((buffer[0] & 0x80) == 0) {
116 *consumed = 1;
117 return ProtoVarInt(buffer[0]);
118 }
119
120 // General case for multi-byte varints
121 // Since we know buffer[0]'s high bit is set, initialize with its value
122 uint64_t result = buffer[0] & 0x7F;
123 uint8_t bitpos = 7;
124
125 // A 64-bit varint is at most 10 bytes (ceil(64/7)). Reject overlong encodings
126 // to avoid undefined behavior from shifting uint64_t by >= 64 bits.
127 uint32_t max_len = std::min(len, uint32_t(10));
128
129 // Start from the second byte since we've already processed the first
130 for (uint32_t i = 1; i < max_len; i++) {
131 uint8_t val = buffer[i];
132 result |= uint64_t(val & 0x7F) << uint64_t(bitpos);
133 bitpos += 7;
134 if ((val & 0x80) == 0) {
135 *consumed = i + 1;
136 return ProtoVarInt(result);
137 }
138 }
139
140 return {}; // Incomplete or invalid varint
141 }
142
143 constexpr uint16_t as_uint16() const { return this->value_; }
144 constexpr uint32_t as_uint32() const { return this->value_; }
145 constexpr uint64_t as_uint64() const { return this->value_; }
146 constexpr bool as_bool() const { return this->value_; }
147 constexpr int32_t as_int32() const {
148 // Not ZigZag encoded
149 return static_cast<int32_t>(this->as_int64());
150 }
151 constexpr int64_t as_int64() const {
152 // Not ZigZag encoded
153 return static_cast<int64_t>(this->value_);
154 }
155 constexpr int32_t as_sint32() const {
156 // with ZigZag encoding
157 return decode_zigzag32(static_cast<uint32_t>(this->value_));
158 }
159 constexpr int64_t as_sint64() const {
160 // with ZigZag encoding
161 return decode_zigzag64(this->value_);
162 }
163
164 protected:
165 uint64_t value_;
166};
167
168// Forward declarations for decode_to_message, encode_message and encode_packed_sint32
170class ProtoMessage;
171class ProtoSize;
172
174 public:
175 explicit ProtoLengthDelimited(const uint8_t *value, size_t length) : value_(value), length_(length) {}
176 std::string as_string() const { return std::string(reinterpret_cast<const char *>(this->value_), this->length_); }
177
178 // Direct access to raw data without string allocation
179 const uint8_t *data() const { return this->value_; }
180 size_t size() const { return this->length_; }
181
192
193 protected:
194 const uint8_t *const value_;
195 const size_t length_;
196};
197
199 public:
200 explicit Proto32Bit(uint32_t value) : value_(value) {}
201 uint32_t as_fixed32() const { return this->value_; }
202 int32_t as_sfixed32() const { return static_cast<int32_t>(this->value_); }
203 float as_float() const {
204 union {
205 uint32_t raw;
206 float value;
207 } s{};
208 s.raw = this->value_;
209 return s.value;
210 }
211
212 protected:
213 const uint32_t value_;
214};
215
216// NOTE: Proto64Bit class removed - wire type 1 (64-bit fixed) not supported
217
219 public:
220 ProtoWriteBuffer(std::vector<uint8_t> *buffer) : buffer_(buffer) {}
221 void write(uint8_t value) { this->buffer_->push_back(value); }
222 void encode_varint_raw(uint32_t value) {
223 while (value > 0x7F) {
224 this->buffer_->push_back(static_cast<uint8_t>(value | 0x80));
225 value >>= 7;
226 }
227 this->buffer_->push_back(static_cast<uint8_t>(value));
228 }
229 void encode_varint_raw_64(uint64_t value) {
230 while (value > 0x7F) {
231 this->buffer_->push_back(static_cast<uint8_t>(value | 0x80));
232 value >>= 7;
233 }
234 this->buffer_->push_back(static_cast<uint8_t>(value));
235 }
248 void encode_field_raw(uint32_t field_id, uint32_t type) {
249 uint32_t val = (field_id << 3) | (type & WIRE_TYPE_MASK);
250 this->encode_varint_raw(val);
251 }
252 void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) {
253 if (len == 0 && !force)
254 return;
255
256 this->encode_field_raw(field_id, 2); // type 2: Length-delimited string
257 this->encode_varint_raw(len);
258
259 // Using resize + memcpy instead of insert provides significant performance improvement:
260 // ~10-11x faster for 16-32 byte strings, ~3x faster for 64-byte strings
261 // as it avoids iterator checks and potential element moves that insert performs
262 size_t old_size = this->buffer_->size();
263 this->buffer_->resize(old_size + len);
264 std::memcpy(this->buffer_->data() + old_size, string, len);
265 }
266 void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
267 this->encode_string(field_id, value.data(), value.size(), force);
268 }
269 void encode_string(uint32_t field_id, const StringRef &ref, bool force = false) {
270 this->encode_string(field_id, ref.c_str(), ref.size(), force);
271 }
272 void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) {
273 this->encode_string(field_id, reinterpret_cast<const char *>(data), len, force);
274 }
275 void encode_uint32(uint32_t field_id, uint32_t value, bool force = false) {
276 if (value == 0 && !force)
277 return;
278 this->encode_field_raw(field_id, 0); // type 0: Varint - uint32
279 this->encode_varint_raw(value);
280 }
281 void encode_uint64(uint32_t field_id, uint64_t value, bool force = false) {
282 if (value == 0 && !force)
283 return;
284 this->encode_field_raw(field_id, 0); // type 0: Varint - uint64
285 this->encode_varint_raw_64(value);
286 }
287 void encode_bool(uint32_t field_id, bool value, bool force = false) {
288 if (!value && !force)
289 return;
290 this->encode_field_raw(field_id, 0); // type 0: Varint - bool
291 this->buffer_->push_back(value ? 0x01 : 0x00);
292 }
293 void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) {
294 if (value == 0 && !force)
295 return;
296
297 this->encode_field_raw(field_id, 5); // type 5: 32-bit fixed32
298 this->write((value >> 0) & 0xFF);
299 this->write((value >> 8) & 0xFF);
300 this->write((value >> 16) & 0xFF);
301 this->write((value >> 24) & 0xFF);
302 }
303 // NOTE: Wire type 1 (64-bit fixed: double, fixed64, sfixed64) is intentionally
304 // not supported to reduce overhead on embedded systems. All ESPHome devices are
305 // 32-bit microcontrollers where 64-bit operations are expensive. If 64-bit support
306 // is needed in the future, the necessary encoding/decoding functions must be added.
307 void encode_float(uint32_t field_id, float value, bool force = false) {
308 if (value == 0.0f && !force)
309 return;
310
311 union {
312 float value;
313 uint32_t raw;
314 } val{};
315 val.value = value;
316 this->encode_fixed32(field_id, val.raw);
317 }
318 void encode_int32(uint32_t field_id, int32_t value, bool force = false) {
319 if (value < 0) {
320 // negative int32 is always 10 byte long
321 this->encode_int64(field_id, value, force);
322 return;
323 }
324 this->encode_uint32(field_id, static_cast<uint32_t>(value), force);
325 }
326 void encode_int64(uint32_t field_id, int64_t value, bool force = false) {
327 this->encode_uint64(field_id, static_cast<uint64_t>(value), force);
328 }
329 void encode_sint32(uint32_t field_id, int32_t value, bool force = false) {
330 this->encode_uint32(field_id, encode_zigzag32(value), force);
331 }
332 void encode_sint64(uint32_t field_id, int64_t value, bool force = false) {
333 this->encode_uint64(field_id, encode_zigzag64(value), force);
334 }
336 void encode_packed_sint32(uint32_t field_id, const std::vector<int32_t> &values);
337 void encode_message(uint32_t field_id, const ProtoMessage &value);
338 std::vector<uint8_t> *get_buffer() const { return buffer_; }
339
340 protected:
341 std::vector<uint8_t> *buffer_;
342};
343
344#ifdef HAS_PROTO_MESSAGE_DUMP
351 public:
352 // Matches default tx_buffer_size in logger component
353 static constexpr size_t CAPACITY = 512;
354
355 DumpBuffer() : pos_(0) { buf_[0] = '\0'; }
356
357 DumpBuffer &append(const char *str) {
358 if (str) {
359 append_impl_(str, strlen(str));
360 }
361 return *this;
362 }
363
364 DumpBuffer &append(const char *str, size_t len) {
365 append_impl_(str, len);
366 return *this;
367 }
368
369 DumpBuffer &append(size_t n, char c) {
370 size_t space = CAPACITY - 1 - pos_;
371 if (n > space)
372 n = space;
373 if (n > 0) {
374 memset(buf_ + pos_, c, n);
375 pos_ += n;
376 buf_[pos_] = '\0';
377 }
378 return *this;
379 }
380
381 const char *c_str() const { return buf_; }
382 size_t size() const { return pos_; }
383
385 char *data() { return buf_; }
387 size_t pos() const { return pos_; }
389 void set_pos(size_t pos) {
390 if (pos >= CAPACITY) {
391 pos_ = CAPACITY - 1;
392 } else {
393 pos_ = pos;
394 }
395 buf_[pos_] = '\0';
396 }
397
398 private:
399 void append_impl_(const char *str, size_t len) {
400 size_t space = CAPACITY - 1 - pos_;
401 if (len > space)
402 len = space;
403 if (len > 0) {
404 memcpy(buf_ + pos_, str, len);
405 pos_ += len;
406 buf_[pos_] = '\0';
407 }
408 }
409
410 char buf_[CAPACITY];
411 size_t pos_;
412};
413#endif
414
416 public:
417 virtual ~ProtoMessage() = default;
418 // Default implementation for messages with no fields
419 virtual void encode(ProtoWriteBuffer buffer) const {}
420 // Default implementation for messages with no fields
421 virtual void calculate_size(ProtoSize &size) const {}
422#ifdef HAS_PROTO_MESSAGE_DUMP
423 virtual const char *dump_to(DumpBuffer &out) const = 0;
424 virtual const char *message_name() const { return "unknown"; }
425#endif
426};
427
428// Base class for messages that support decoding
430 public:
431 virtual void decode(const uint8_t *buffer, size_t length);
432
442 static uint32_t count_repeated_field(const uint8_t *buffer, size_t length, uint32_t target_field_id);
443
444 protected:
445 virtual bool decode_varint(uint32_t field_id, ProtoVarInt value) { return false; }
446 virtual bool decode_length(uint32_t field_id, ProtoLengthDelimited value) { return false; }
447 virtual bool decode_32bit(uint32_t field_id, Proto32Bit value) { return false; }
448 // NOTE: decode_64bit removed - wire type 1 not supported
449};
450
452 private:
453 uint32_t total_size_ = 0;
454
455 public:
474 ProtoSize() = default;
475
476 uint32_t get_size() const { return total_size_; }
477
484 static constexpr uint32_t varint(uint32_t value) {
485 // Optimized varint size calculation using leading zeros
486 // Each 7 bits requires one byte in the varint encoding
487 if (value < 128)
488 return 1; // 7 bits, common case for small values
489
490 // For larger values, count bytes needed based on the position of the highest bit set
491 if (value < 16384) {
492 return 2; // 14 bits
493 } else if (value < 2097152) {
494 return 3; // 21 bits
495 } else if (value < 268435456) {
496 return 4; // 28 bits
497 } else {
498 return 5; // 32 bits (maximum for uint32_t)
499 }
500 }
501
508 static constexpr uint32_t varint(uint64_t value) {
509 // Handle common case of values fitting in uint32_t (vast majority of use cases)
510 if (value <= UINT32_MAX) {
511 return varint(static_cast<uint32_t>(value));
512 }
513
514 // For larger values, determine size based on highest bit position
515 if (value < (1ULL << 35)) {
516 return 5; // 35 bits
517 } else if (value < (1ULL << 42)) {
518 return 6; // 42 bits
519 } else if (value < (1ULL << 49)) {
520 return 7; // 49 bits
521 } else if (value < (1ULL << 56)) {
522 return 8; // 56 bits
523 } else if (value < (1ULL << 63)) {
524 return 9; // 63 bits
525 } else {
526 return 10; // 64 bits (maximum for uint64_t)
527 }
528 }
529
539 static constexpr uint32_t varint(int32_t value) {
540 // Negative values are sign-extended to 64 bits in protocol buffers,
541 // which always results in a 10-byte varint for negative int32
542 if (value < 0) {
543 return 10; // Negative int32 is always 10 bytes long
544 }
545 // For non-negative values, use the uint32_t implementation
546 return varint(static_cast<uint32_t>(value));
547 }
548
555 static constexpr uint32_t varint(int64_t value) {
556 // For int64_t, we convert to uint64_t and calculate the size
557 // This works because the bit pattern determines the encoding size,
558 // and we've handled negative int32 values as a special case above
559 return varint(static_cast<uint64_t>(value));
560 }
561
569 static constexpr uint32_t field(uint32_t field_id, uint32_t type) {
570 uint32_t tag = (field_id << 3) | (type & WIRE_TYPE_MASK);
571 return varint(tag);
572 }
573
591 inline void add_int32(uint32_t field_id_size, int32_t value) {
592 if (value != 0) {
593 add_int32_force(field_id_size, value);
594 }
595 }
596
600 inline void add_int32_force(uint32_t field_id_size, int32_t value) {
601 // Always calculate size when forced
602 // Negative values are encoded as 10-byte varints in protobuf
603 total_size_ += field_id_size + (value < 0 ? 10 : varint(static_cast<uint32_t>(value)));
604 }
605
609 inline void add_uint32(uint32_t field_id_size, uint32_t value) {
610 if (value != 0) {
611 add_uint32_force(field_id_size, value);
612 }
613 }
614
618 inline void add_uint32_force(uint32_t field_id_size, uint32_t value) {
619 // Always calculate size when force is true
620 total_size_ += field_id_size + varint(value);
621 }
622
626 inline void add_bool(uint32_t field_id_size, bool value) {
627 if (value) {
628 // Boolean fields always use 1 byte when true
629 total_size_ += field_id_size + 1;
630 }
631 }
632
636 inline void add_bool_force(uint32_t field_id_size, bool value) {
637 // Always calculate size when force is true
638 // Boolean fields always use 1 byte
639 total_size_ += field_id_size + 1;
640 }
641
645 inline void add_float(uint32_t field_id_size, float value) {
646 if (value != 0.0f) {
647 total_size_ += field_id_size + 4;
648 }
649 }
650
651 // NOTE: add_double_field removed - wire type 1 (64-bit: double) not supported
652 // to reduce overhead on embedded systems
653
657 inline void add_fixed32(uint32_t field_id_size, uint32_t value) {
658 if (value != 0) {
659 total_size_ += field_id_size + 4;
660 }
661 }
662
663 // NOTE: add_fixed64_field removed - wire type 1 (64-bit: fixed64) not supported
664 // to reduce overhead on embedded systems
665
669 inline void add_sfixed32(uint32_t field_id_size, int32_t value) {
670 if (value != 0) {
671 total_size_ += field_id_size + 4;
672 }
673 }
674
675 // NOTE: add_sfixed64_field removed - wire type 1 (64-bit: sfixed64) not supported
676 // to reduce overhead on embedded systems
677
683 inline void add_sint32(uint32_t field_id_size, int32_t value) {
684 if (value != 0) {
685 add_sint32_force(field_id_size, value);
686 }
687 }
688
694 inline void add_sint32_force(uint32_t field_id_size, int32_t value) {
695 // Always calculate size when force is true
696 // ZigZag encoding for sint32
697 total_size_ += field_id_size + varint(encode_zigzag32(value));
698 }
699
703 inline void add_int64(uint32_t field_id_size, int64_t value) {
704 if (value != 0) {
705 add_int64_force(field_id_size, value);
706 }
707 }
708
712 inline void add_int64_force(uint32_t field_id_size, int64_t value) {
713 // Always calculate size when force is true
714 total_size_ += field_id_size + varint(value);
715 }
716
720 inline void add_uint64(uint32_t field_id_size, uint64_t value) {
721 if (value != 0) {
722 add_uint64_force(field_id_size, value);
723 }
724 }
725
729 inline void add_uint64_force(uint32_t field_id_size, uint64_t value) {
730 // Always calculate size when force is true
731 total_size_ += field_id_size + varint(value);
732 }
733
734 // NOTE: sint64 support functions (add_sint64_field, add_sint64_field_force) removed
735 // sint64 type is not supported by ESPHome API to reduce overhead on embedded systems
736
740 inline void add_length(uint32_t field_id_size, size_t len) {
741 if (len != 0) {
742 add_length_force(field_id_size, len);
743 }
744 }
745
750 inline void add_length_force(uint32_t field_id_size, size_t len) {
751 // Always calculate size when force is true
752 // Field ID + length varint + data bytes
753 total_size_ += field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len);
754 }
755
764 inline void add_precalculated_size(uint32_t size) { total_size_ += size; }
765
774 inline void add_message_field(uint32_t field_id_size, uint32_t nested_size) {
775 if (nested_size != 0) {
776 add_message_field_force(field_id_size, nested_size);
777 }
778 }
779
785 inline void add_message_field_force(uint32_t field_id_size, uint32_t nested_size) {
786 // Always calculate size when force is true
787 // Field ID + length varint + nested message content
788 total_size_ += field_id_size + varint(nested_size) + nested_size;
789 }
790
800 inline void add_message_object(uint32_t field_id_size, const ProtoMessage &message) {
801 // Calculate nested message size by creating a temporary ProtoSize
802 ProtoSize nested_calc;
803 message.calculate_size(nested_calc);
804 uint32_t nested_size = nested_calc.get_size();
805
806 // Use the base implementation with the calculated nested_size
807 add_message_field(field_id_size, nested_size);
808 }
809
815 inline void add_message_object_force(uint32_t field_id_size, const ProtoMessage &message) {
816 // Calculate nested message size by creating a temporary ProtoSize
817 ProtoSize nested_calc;
818 message.calculate_size(nested_calc);
819 uint32_t nested_size = nested_calc.get_size();
820
821 // Use the base implementation with the calculated nested_size
822 add_message_field_force(field_id_size, nested_size);
823 }
824
834 template<typename MessageType>
835 inline void add_repeated_message(uint32_t field_id_size, const std::vector<MessageType> &messages) {
836 // Skip if the vector is empty
837 if (!messages.empty()) {
838 // Use the force version for all messages in the repeated field
839 for (const auto &message : messages) {
840 add_message_object_force(field_id_size, message);
841 }
842 }
843 }
844
852 template<typename MessageType>
853 inline void add_repeated_message(uint32_t field_id_size, const FixedVector<MessageType> &messages) {
854 // Skip if the fixed vector is empty
855 if (!messages.empty()) {
856 // Use the force version for all messages in the repeated field
857 for (const auto &message : messages) {
858 add_message_object_force(field_id_size, message);
859 }
860 }
861 }
862
866 inline void add_packed_sint32(uint32_t field_id_size, const std::vector<int32_t> &values) {
867 if (values.empty())
868 return;
869
870 size_t packed_size = 0;
871 for (int value : values) {
872 packed_size += varint(encode_zigzag32(value));
873 }
874
875 // field_id + length varint + packed data
876 total_size_ += field_id_size + varint(static_cast<uint32_t>(packed_size)) + static_cast<uint32_t>(packed_size);
877 }
878};
879
880// Implementation of encode_packed_sint32 - must be after ProtoSize is defined
881inline void ProtoWriteBuffer::encode_packed_sint32(uint32_t field_id, const std::vector<int32_t> &values) {
882 if (values.empty())
883 return;
884
885 // Calculate packed size
886 size_t packed_size = 0;
887 for (int value : values) {
888 packed_size += ProtoSize::varint(encode_zigzag32(value));
889 }
890
891 // Write tag (LENGTH_DELIMITED) + length + all zigzag-encoded values
893 this->encode_varint_raw(packed_size);
894 for (int value : values) {
896 }
897}
898
899// Implementation of encode_message - must be after ProtoMessage is defined
900inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessage &value) {
901 this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
902
903 // Calculate the message size first
904 ProtoSize msg_size;
905 value.calculate_size(msg_size);
906 uint32_t msg_length_bytes = msg_size.get_size();
907
908 // Calculate how many bytes the length varint needs
909 uint32_t varint_length_bytes = ProtoSize::varint(msg_length_bytes);
910
911 // Reserve exact space for the length varint
912 size_t begin = this->buffer_->size();
913 this->buffer_->resize(this->buffer_->size() + varint_length_bytes);
914
915 // Write the length varint directly
916 encode_varint_to_buffer(msg_length_bytes, this->buffer_->data() + begin);
917
918 // Now encode the message content - it will append to the buffer
919 value.encode(*this);
920
921#ifdef ESPHOME_DEBUG_API
922 // Verify that the encoded size matches what we calculated
923 assert(this->buffer_->size() == begin + varint_length_bytes + msg_length_bytes);
924#endif
925}
926
927// Implementation of decode_to_message - must be after ProtoDecodableMessage is defined
929 msg.decode(this->value_, this->length_);
930}
931
932template<typename T> const char *proto_enum_to_string(T value);
933
935 public:
936 protected:
937 virtual bool is_authenticated() = 0;
938 virtual bool is_connection_setup() = 0;
939 virtual void on_fatal_error() = 0;
940 virtual void on_no_setup_connection() = 0;
941 virtual bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) = 0;
942 virtual void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) = 0;
950 virtual bool send_message_impl(const ProtoMessage &msg, uint8_t message_type) = 0;
951
952 // Authentication helper methods
954 if (!this->is_connection_setup()) {
956 return false;
957 }
958 return true;
959 }
960
961 inline bool check_authenticated_() { return this->check_connection_setup_(); }
962};
963
964} // 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:227
bool empty() const
Definition helpers.h:385
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:350
const char * c_str() const
Definition proto.h:381
DumpBuffer & append(size_t n, char c)
Definition proto.h:369
size_t size() const
Definition proto.h:382
size_t pos() const
Get current position for use with buf_append_printf.
Definition proto.h:387
static constexpr size_t CAPACITY
Definition proto.h:353
DumpBuffer & append(const char *str, size_t len)
Definition proto.h:364
DumpBuffer & append(const char *str)
Definition proto.h:357
char * data()
Get writable buffer pointer for use with buf_append_printf.
Definition proto.h:385
void set_pos(size_t pos)
Update position after buf_append_printf call.
Definition proto.h:389
uint32_t as_fixed32() const
Definition proto.h:201
int32_t as_sfixed32() const
Definition proto.h:202
float as_float() const
Definition proto.h:203
const uint32_t value_
Definition proto.h:213
Proto32Bit(uint32_t value)
Definition proto.h:200
virtual bool decode_32bit(uint32_t field_id, Proto32Bit value)
Definition proto.h:447
virtual bool decode_varint(uint32_t field_id, ProtoVarInt value)
Definition proto.h:445
virtual void decode(const uint8_t *buffer, size_t length)
Definition proto.cpp:73
virtual bool decode_length(uint32_t field_id, ProtoLengthDelimited value)
Definition proto.h:446
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:10
void decode_to_message(ProtoDecodableMessage &msg) const
Decode the length-delimited data into an existing ProtoDecodableMessage instance.
Definition proto.h:928
const uint8_t *const value_
Definition proto.h:194
const uint8_t * data() const
Definition proto.h:179
ProtoLengthDelimited(const uint8_t *value, size_t length)
Definition proto.h:175
std::string as_string() const
Definition proto.h:176
virtual void encode(ProtoWriteBuffer buffer) const
Definition proto.h:419
virtual const char * message_name() const
Definition proto.h:424
virtual ~ProtoMessage()=default
virtual void calculate_size(ProtoSize &size) const
Definition proto.h:421
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:800
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:484
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:785
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:815
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:645
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:569
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:539
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:669
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:618
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:600
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:853
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:555
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:712
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:683
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:774
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:636
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:703
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:626
uint32_t get_size() const
Definition proto.h:476
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:609
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:508
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:835
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:720
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:750
void add_int32(uint32_t field_id_size, int32_t value)
Common parameters for all add_*_field methods.
Definition proto.h:591
void add_precalculated_size(uint32_t size)
Adds a pre-calculated size directly to the total.
Definition proto.h:764
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:694
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:729
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:657
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:866
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:740
Representation of a VarInt - in ProtoBuf should be 64bit but we only use 32bit.
Definition proto.h:101
constexpr uint16_t as_uint16() const
Definition proto.h:143
constexpr uint64_t as_uint64() const
Definition proto.h:145
constexpr int32_t as_int32() const
Definition proto.h:147
constexpr uint32_t as_uint32() const
Definition proto.h:144
constexpr int64_t as_int64() const
Definition proto.h:151
constexpr bool as_bool() const
Definition proto.h:146
ProtoVarInt(uint64_t value)
Definition proto.h:104
constexpr int32_t as_sint32() const
Definition proto.h:155
constexpr int64_t as_sint64() const
Definition proto.h:159
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:107
void encode_varint_raw(uint32_t value)
Definition proto.h:222
void encode_string(uint32_t field_id, const std::string &value, bool force=false)
Definition proto.h:266
void write(uint8_t value)
Definition proto.h:221
void encode_int64(uint32_t field_id, int64_t value, bool force=false)
Definition proto.h:326
void encode_float(uint32_t field_id, float value, bool force=false)
Definition proto.h:307
void encode_int32(uint32_t field_id, int32_t value, bool force=false)
Definition proto.h:318
void encode_sint64(uint32_t field_id, int64_t value, bool force=false)
Definition proto.h:332
void encode_string(uint32_t field_id, const char *string, size_t len, bool force=false)
Definition proto.h:252
void encode_bool(uint32_t field_id, bool value, bool force=false)
Definition proto.h:287
ProtoWriteBuffer(std::vector< uint8_t > *buffer)
Definition proto.h:220
void encode_uint64(uint32_t field_id, uint64_t value, bool force=false)
Definition proto.h:281
void encode_string(uint32_t field_id, const StringRef &ref, bool force=false)
Definition proto.h:269
void encode_uint32(uint32_t field_id, uint32_t value, bool force=false)
Definition proto.h:275
void encode_sint32(uint32_t field_id, int32_t value, bool force=false)
Definition proto.h:329
void encode_packed_sint32(uint32_t field_id, const std::vector< int32_t > &values)
Encode a packed repeated sint32 field (zero-copy from vector)
Definition proto.h:881
void encode_field_raw(uint32_t field_id, uint32_t type)
Encode a field key (tag/wire type combination).
Definition proto.h:248
void encode_message(uint32_t field_id, const ProtoMessage &value)
Definition proto.h:900
std::vector< uint8_t > * get_buffer() const
Definition proto.h:338
void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force=false)
Definition proto.h:272
void encode_fixed32(uint32_t field_id, uint32_t value, bool force=false)
Definition proto.h:293
std::vector< uint8_t > * buffer_
Definition proto.h:341
void encode_varint_raw_64(uint64_t value)
Definition proto.h:229
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:26
constexpr uint8_t WIRE_TYPE_VARINT
Definition proto.h:20
const char * proto_enum_to_string(T value)
constexpr uint64_t encode_zigzag64(int64_t value)
Definition proto.h:30
constexpr uint8_t WIRE_TYPE_MASK
Definition proto.h:23
constexpr uint8_t WIRE_TYPE_LENGTH_DELIMITED
Definition proto.h:21
constexpr int32_t decode_zigzag32(uint32_t value)
Definition proto.h:34
constexpr uint8_t WIRE_TYPE_FIXED32
Definition proto.h:22
constexpr int64_t decode_zigzag64(uint64_t value)
Definition proto.h:38
void encode_varint_to_buffer(uint32_t val, uint8_t *buffer)
Encode a varint directly into a pre-allocated buffer.
Definition proto.h:62
uint16_t count_packed_varints(const uint8_t *data, size_t len)
Count number of varints in a packed buffer.
Definition proto.h:43
std::string size_t len
Definition helpers.h:692
size_t size
Definition helpers.h:729
uint16_t length
Definition tt21100.cpp:0