ESPHome 2026.1.5
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
60/*
61 * StringRef Ownership Model for API Protocol Messages
62 * ===================================================
63 *
64 * StringRef is used for zero-copy string handling in outgoing (SOURCE_SERVER) messages.
65 * It holds a pointer and length to existing string data without copying.
66 *
67 * CRITICAL: The referenced string data MUST remain valid until message encoding completes.
68 *
69 * Safe StringRef Patterns:
70 * 1. String literals: StringRef("literal") - Always safe (static storage duration)
71 * 2. Member variables: StringRef(this->member_string_) - Safe if object outlives encoding
72 * 3. Global/static strings: StringRef(GLOBAL_CONSTANT) - Always safe
73 * 4. Local variables: Safe ONLY if encoding happens before function returns:
74 * std::string temp = compute_value();
75 * msg.field = StringRef(temp);
76 * return this->send_message(msg); // temp is valid during encoding
77 *
78 * Unsafe Patterns (WILL cause crashes/corruption):
79 * 1. Temporaries: msg.field = StringRef(obj.get_string()) // get_string() returns by value
80 * 2. Concatenation: msg.field = StringRef(str1 + str2) // Result is temporary
81 *
82 * For unsafe patterns, store in a local variable first:
83 * std::string temp = get_string(); // or str1 + str2
84 * msg.field = StringRef(temp);
85 *
86 * The send_*_response pattern ensures proper lifetime management by encoding
87 * within the same function scope where temporaries are created.
88 */
89
92 public:
94 explicit ProtoVarInt(uint64_t value) : value_(value) {}
95
96 static optional<ProtoVarInt> parse(const uint8_t *buffer, uint32_t len, uint32_t *consumed) {
97 if (len == 0) {
98 if (consumed != nullptr)
99 *consumed = 0;
100 return {};
101 }
102
103 // Most common case: single-byte varint (values 0-127)
104 if ((buffer[0] & 0x80) == 0) {
105 if (consumed != nullptr)
106 *consumed = 1;
107 return ProtoVarInt(buffer[0]);
108 }
109
110 // General case for multi-byte varints
111 // Since we know buffer[0]'s high bit is set, initialize with its value
112 uint64_t result = buffer[0] & 0x7F;
113 uint8_t bitpos = 7;
114
115 // Start from the second byte since we've already processed the first
116 for (uint32_t i = 1; i < len; i++) {
117 uint8_t val = buffer[i];
118 result |= uint64_t(val & 0x7F) << uint64_t(bitpos);
119 bitpos += 7;
120 if ((val & 0x80) == 0) {
121 if (consumed != nullptr)
122 *consumed = i + 1;
123 return ProtoVarInt(result);
124 }
125 }
126
127 if (consumed != nullptr)
128 *consumed = 0;
129 return {}; // Incomplete or invalid varint
130 }
131
132 constexpr uint16_t as_uint16() const { return this->value_; }
133 constexpr uint32_t as_uint32() const { return this->value_; }
134 constexpr uint64_t as_uint64() const { return this->value_; }
135 constexpr bool as_bool() const { return this->value_; }
136 constexpr int32_t as_int32() const {
137 // Not ZigZag encoded
138 return static_cast<int32_t>(this->as_int64());
139 }
140 constexpr int64_t as_int64() const {
141 // Not ZigZag encoded
142 return static_cast<int64_t>(this->value_);
143 }
144 constexpr int32_t as_sint32() const {
145 // with ZigZag encoding
146 return decode_zigzag32(static_cast<uint32_t>(this->value_));
147 }
148 constexpr int64_t as_sint64() const {
149 // with ZigZag encoding
150 return decode_zigzag64(this->value_);
151 }
163 void encode_to_buffer_unchecked(uint8_t *buffer, size_t len) {
164 uint64_t val = this->value_;
165 if (val <= 0x7F) {
166 buffer[0] = val;
167 return;
168 }
169 size_t i = 0;
170 while (val && i < len) {
171 uint8_t temp = val & 0x7F;
172 val >>= 7;
173 if (val) {
174 buffer[i++] = temp | 0x80;
175 } else {
176 buffer[i++] = temp;
177 }
178 }
179 }
180 void encode(std::vector<uint8_t> &out) {
181 uint64_t val = this->value_;
182 if (val <= 0x7F) {
183 out.push_back(val);
184 return;
185 }
186 while (val) {
187 uint8_t temp = val & 0x7F;
188 val >>= 7;
189 if (val) {
190 out.push_back(temp | 0x80);
191 } else {
192 out.push_back(temp);
193 }
194 }
195 }
196
197 protected:
198 uint64_t value_;
199};
200
201// Forward declarations for decode_to_message, encode_message and encode_packed_sint32
203class ProtoMessage;
204class ProtoSize;
205
207 public:
208 explicit ProtoLengthDelimited(const uint8_t *value, size_t length) : value_(value), length_(length) {}
209 std::string as_string() const { return std::string(reinterpret_cast<const char *>(this->value_), this->length_); }
210
211 // Direct access to raw data without string allocation
212 const uint8_t *data() const { return this->value_; }
213 size_t size() const { return this->length_; }
214
225
226 protected:
227 const uint8_t *const value_;
228 const size_t length_;
229};
230
232 public:
233 explicit Proto32Bit(uint32_t value) : value_(value) {}
234 uint32_t as_fixed32() const { return this->value_; }
235 int32_t as_sfixed32() const { return static_cast<int32_t>(this->value_); }
236 float as_float() const {
237 union {
238 uint32_t raw;
239 float value;
240 } s{};
241 s.raw = this->value_;
242 return s.value;
243 }
244
245 protected:
246 const uint32_t value_;
247};
248
249// NOTE: Proto64Bit class removed - wire type 1 (64-bit fixed) not supported
250
252 public:
253 ProtoWriteBuffer(std::vector<uint8_t> *buffer) : buffer_(buffer) {}
254 void write(uint8_t value) { this->buffer_->push_back(value); }
255 void encode_varint_raw(ProtoVarInt value) { value.encode(*this->buffer_); }
256 void encode_varint_raw(uint32_t value) { this->encode_varint_raw(ProtoVarInt(value)); }
269 void encode_field_raw(uint32_t field_id, uint32_t type) {
270 uint32_t val = (field_id << 3) | (type & WIRE_TYPE_MASK);
271 this->encode_varint_raw(val);
272 }
273 void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) {
274 if (len == 0 && !force)
275 return;
276
277 this->encode_field_raw(field_id, 2); // type 2: Length-delimited string
278 this->encode_varint_raw(len);
279
280 // Using resize + memcpy instead of insert provides significant performance improvement:
281 // ~10-11x faster for 16-32 byte strings, ~3x faster for 64-byte strings
282 // as it avoids iterator checks and potential element moves that insert performs
283 size_t old_size = this->buffer_->size();
284 this->buffer_->resize(old_size + len);
285 std::memcpy(this->buffer_->data() + old_size, string, len);
286 }
287 void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
288 this->encode_string(field_id, value.data(), value.size(), force);
289 }
290 void encode_string(uint32_t field_id, const StringRef &ref, bool force = false) {
291 this->encode_string(field_id, ref.c_str(), ref.size(), force);
292 }
293 void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) {
294 this->encode_string(field_id, reinterpret_cast<const char *>(data), len, force);
295 }
296 void encode_uint32(uint32_t field_id, uint32_t value, bool force = false) {
297 if (value == 0 && !force)
298 return;
299 this->encode_field_raw(field_id, 0); // type 0: Varint - uint32
300 this->encode_varint_raw(value);
301 }
302 void encode_uint64(uint32_t field_id, uint64_t value, bool force = false) {
303 if (value == 0 && !force)
304 return;
305 this->encode_field_raw(field_id, 0); // type 0: Varint - uint64
306 this->encode_varint_raw(ProtoVarInt(value));
307 }
308 void encode_bool(uint32_t field_id, bool value, bool force = false) {
309 if (!value && !force)
310 return;
311 this->encode_field_raw(field_id, 0); // type 0: Varint - bool
312 this->write(0x01);
313 }
314 void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) {
315 if (value == 0 && !force)
316 return;
317
318 this->encode_field_raw(field_id, 5); // type 5: 32-bit fixed32
319 this->write((value >> 0) & 0xFF);
320 this->write((value >> 8) & 0xFF);
321 this->write((value >> 16) & 0xFF);
322 this->write((value >> 24) & 0xFF);
323 }
324 // NOTE: Wire type 1 (64-bit fixed: double, fixed64, sfixed64) is intentionally
325 // not supported to reduce overhead on embedded systems. All ESPHome devices are
326 // 32-bit microcontrollers where 64-bit operations are expensive. If 64-bit support
327 // is needed in the future, the necessary encoding/decoding functions must be added.
328 void encode_float(uint32_t field_id, float value, bool force = false) {
329 if (value == 0.0f && !force)
330 return;
331
332 union {
333 float value;
334 uint32_t raw;
335 } val{};
336 val.value = value;
337 this->encode_fixed32(field_id, val.raw);
338 }
339 void encode_int32(uint32_t field_id, int32_t value, bool force = false) {
340 if (value < 0) {
341 // negative int32 is always 10 byte long
342 this->encode_int64(field_id, value, force);
343 return;
344 }
345 this->encode_uint32(field_id, static_cast<uint32_t>(value), force);
346 }
347 void encode_int64(uint32_t field_id, int64_t value, bool force = false) {
348 this->encode_uint64(field_id, static_cast<uint64_t>(value), force);
349 }
350 void encode_sint32(uint32_t field_id, int32_t value, bool force = false) {
351 this->encode_uint32(field_id, encode_zigzag32(value), force);
352 }
353 void encode_sint64(uint32_t field_id, int64_t value, bool force = false) {
354 this->encode_uint64(field_id, encode_zigzag64(value), force);
355 }
357 void encode_packed_sint32(uint32_t field_id, const std::vector<int32_t> &values);
358 void encode_message(uint32_t field_id, const ProtoMessage &value);
359 std::vector<uint8_t> *get_buffer() const { return buffer_; }
360
361 protected:
362 std::vector<uint8_t> *buffer_;
363};
364
365#ifdef HAS_PROTO_MESSAGE_DUMP
372 public:
373 // Matches default tx_buffer_size in logger component
374 static constexpr size_t CAPACITY = 512;
375
376 DumpBuffer() : pos_(0) { buf_[0] = '\0'; }
377
378 DumpBuffer &append(const char *str) {
379 if (str) {
380 append_impl_(str, strlen(str));
381 }
382 return *this;
383 }
384
385 DumpBuffer &append(const char *str, size_t len) {
386 append_impl_(str, len);
387 return *this;
388 }
389
390 DumpBuffer &append(size_t n, char c) {
391 size_t space = CAPACITY - 1 - pos_;
392 if (n > space)
393 n = space;
394 if (n > 0) {
395 memset(buf_ + pos_, c, n);
396 pos_ += n;
397 buf_[pos_] = '\0';
398 }
399 return *this;
400 }
401
402 const char *c_str() const { return buf_; }
403 size_t size() const { return pos_; }
404
405 private:
406 void append_impl_(const char *str, size_t len) {
407 size_t space = CAPACITY - 1 - pos_;
408 if (len > space)
409 len = space;
410 if (len > 0) {
411 memcpy(buf_ + pos_, str, len);
412 pos_ += len;
413 buf_[pos_] = '\0';
414 }
415 }
416
417 char buf_[CAPACITY];
418 size_t pos_;
419};
420#endif
421
423 public:
424 virtual ~ProtoMessage() = default;
425 // Default implementation for messages with no fields
426 virtual void encode(ProtoWriteBuffer buffer) const {}
427 // Default implementation for messages with no fields
428 virtual void calculate_size(ProtoSize &size) const {}
429#ifdef HAS_PROTO_MESSAGE_DUMP
430 virtual const char *dump_to(DumpBuffer &out) const = 0;
431 virtual const char *message_name() const { return "unknown"; }
432#endif
433};
434
435// Base class for messages that support decoding
437 public:
438 virtual void decode(const uint8_t *buffer, size_t length);
439
449 static uint32_t count_repeated_field(const uint8_t *buffer, size_t length, uint32_t target_field_id);
450
451 protected:
452 virtual bool decode_varint(uint32_t field_id, ProtoVarInt value) { return false; }
453 virtual bool decode_length(uint32_t field_id, ProtoLengthDelimited value) { return false; }
454 virtual bool decode_32bit(uint32_t field_id, Proto32Bit value) { return false; }
455 // NOTE: decode_64bit removed - wire type 1 not supported
456};
457
459 private:
460 uint32_t total_size_ = 0;
461
462 public:
481 ProtoSize() = default;
482
483 uint32_t get_size() const { return total_size_; }
484
491 static constexpr uint32_t varint(uint32_t value) {
492 // Optimized varint size calculation using leading zeros
493 // Each 7 bits requires one byte in the varint encoding
494 if (value < 128)
495 return 1; // 7 bits, common case for small values
496
497 // For larger values, count bytes needed based on the position of the highest bit set
498 if (value < 16384) {
499 return 2; // 14 bits
500 } else if (value < 2097152) {
501 return 3; // 21 bits
502 } else if (value < 268435456) {
503 return 4; // 28 bits
504 } else {
505 return 5; // 32 bits (maximum for uint32_t)
506 }
507 }
508
515 static constexpr uint32_t varint(uint64_t value) {
516 // Handle common case of values fitting in uint32_t (vast majority of use cases)
517 if (value <= UINT32_MAX) {
518 return varint(static_cast<uint32_t>(value));
519 }
520
521 // For larger values, determine size based on highest bit position
522 if (value < (1ULL << 35)) {
523 return 5; // 35 bits
524 } else if (value < (1ULL << 42)) {
525 return 6; // 42 bits
526 } else if (value < (1ULL << 49)) {
527 return 7; // 49 bits
528 } else if (value < (1ULL << 56)) {
529 return 8; // 56 bits
530 } else if (value < (1ULL << 63)) {
531 return 9; // 63 bits
532 } else {
533 return 10; // 64 bits (maximum for uint64_t)
534 }
535 }
536
546 static constexpr uint32_t varint(int32_t value) {
547 // Negative values are sign-extended to 64 bits in protocol buffers,
548 // which always results in a 10-byte varint for negative int32
549 if (value < 0) {
550 return 10; // Negative int32 is always 10 bytes long
551 }
552 // For non-negative values, use the uint32_t implementation
553 return varint(static_cast<uint32_t>(value));
554 }
555
562 static constexpr uint32_t varint(int64_t value) {
563 // For int64_t, we convert to uint64_t and calculate the size
564 // This works because the bit pattern determines the encoding size,
565 // and we've handled negative int32 values as a special case above
566 return varint(static_cast<uint64_t>(value));
567 }
568
576 static constexpr uint32_t field(uint32_t field_id, uint32_t type) {
577 uint32_t tag = (field_id << 3) | (type & WIRE_TYPE_MASK);
578 return varint(tag);
579 }
580
598 inline void add_int32(uint32_t field_id_size, int32_t value) {
599 if (value != 0) {
600 add_int32_force(field_id_size, value);
601 }
602 }
603
607 inline void add_int32_force(uint32_t field_id_size, int32_t value) {
608 // Always calculate size when forced
609 // Negative values are encoded as 10-byte varints in protobuf
610 total_size_ += field_id_size + (value < 0 ? 10 : varint(static_cast<uint32_t>(value)));
611 }
612
616 inline void add_uint32(uint32_t field_id_size, uint32_t value) {
617 if (value != 0) {
618 add_uint32_force(field_id_size, value);
619 }
620 }
621
625 inline void add_uint32_force(uint32_t field_id_size, uint32_t value) {
626 // Always calculate size when force is true
627 total_size_ += field_id_size + varint(value);
628 }
629
633 inline void add_bool(uint32_t field_id_size, bool value) {
634 if (value) {
635 // Boolean fields always use 1 byte when true
636 total_size_ += field_id_size + 1;
637 }
638 }
639
643 inline void add_bool_force(uint32_t field_id_size, bool value) {
644 // Always calculate size when force is true
645 // Boolean fields always use 1 byte
646 total_size_ += field_id_size + 1;
647 }
648
652 inline void add_float(uint32_t field_id_size, float value) {
653 if (value != 0.0f) {
654 total_size_ += field_id_size + 4;
655 }
656 }
657
658 // NOTE: add_double_field removed - wire type 1 (64-bit: double) not supported
659 // to reduce overhead on embedded systems
660
664 inline void add_fixed32(uint32_t field_id_size, uint32_t value) {
665 if (value != 0) {
666 total_size_ += field_id_size + 4;
667 }
668 }
669
670 // NOTE: add_fixed64_field removed - wire type 1 (64-bit: fixed64) not supported
671 // to reduce overhead on embedded systems
672
676 inline void add_sfixed32(uint32_t field_id_size, int32_t value) {
677 if (value != 0) {
678 total_size_ += field_id_size + 4;
679 }
680 }
681
682 // NOTE: add_sfixed64_field removed - wire type 1 (64-bit: sfixed64) not supported
683 // to reduce overhead on embedded systems
684
690 inline void add_sint32(uint32_t field_id_size, int32_t value) {
691 if (value != 0) {
692 add_sint32_force(field_id_size, value);
693 }
694 }
695
701 inline void add_sint32_force(uint32_t field_id_size, int32_t value) {
702 // Always calculate size when force is true
703 // ZigZag encoding for sint32
704 total_size_ += field_id_size + varint(encode_zigzag32(value));
705 }
706
710 inline void add_int64(uint32_t field_id_size, int64_t value) {
711 if (value != 0) {
712 add_int64_force(field_id_size, value);
713 }
714 }
715
719 inline void add_int64_force(uint32_t field_id_size, int64_t value) {
720 // Always calculate size when force is true
721 total_size_ += field_id_size + varint(value);
722 }
723
727 inline void add_uint64(uint32_t field_id_size, uint64_t value) {
728 if (value != 0) {
729 add_uint64_force(field_id_size, value);
730 }
731 }
732
736 inline void add_uint64_force(uint32_t field_id_size, uint64_t value) {
737 // Always calculate size when force is true
738 total_size_ += field_id_size + varint(value);
739 }
740
741 // NOTE: sint64 support functions (add_sint64_field, add_sint64_field_force) removed
742 // sint64 type is not supported by ESPHome API to reduce overhead on embedded systems
743
747 inline void add_length(uint32_t field_id_size, size_t len) {
748 if (len != 0) {
749 add_length_force(field_id_size, len);
750 }
751 }
752
757 inline void add_length_force(uint32_t field_id_size, size_t len) {
758 // Always calculate size when force is true
759 // Field ID + length varint + data bytes
760 total_size_ += field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len);
761 }
762
771 inline void add_precalculated_size(uint32_t size) { total_size_ += size; }
772
781 inline void add_message_field(uint32_t field_id_size, uint32_t nested_size) {
782 if (nested_size != 0) {
783 add_message_field_force(field_id_size, nested_size);
784 }
785 }
786
792 inline void add_message_field_force(uint32_t field_id_size, uint32_t nested_size) {
793 // Always calculate size when force is true
794 // Field ID + length varint + nested message content
795 total_size_ += field_id_size + varint(nested_size) + nested_size;
796 }
797
807 inline void add_message_object(uint32_t field_id_size, const ProtoMessage &message) {
808 // Calculate nested message size by creating a temporary ProtoSize
809 ProtoSize nested_calc;
810 message.calculate_size(nested_calc);
811 uint32_t nested_size = nested_calc.get_size();
812
813 // Use the base implementation with the calculated nested_size
814 add_message_field(field_id_size, nested_size);
815 }
816
822 inline void add_message_object_force(uint32_t field_id_size, const ProtoMessage &message) {
823 // Calculate nested message size by creating a temporary ProtoSize
824 ProtoSize nested_calc;
825 message.calculate_size(nested_calc);
826 uint32_t nested_size = nested_calc.get_size();
827
828 // Use the base implementation with the calculated nested_size
829 add_message_field_force(field_id_size, nested_size);
830 }
831
841 template<typename MessageType>
842 inline void add_repeated_message(uint32_t field_id_size, const std::vector<MessageType> &messages) {
843 // Skip if the vector is empty
844 if (!messages.empty()) {
845 // Use the force version for all messages in the repeated field
846 for (const auto &message : messages) {
847 add_message_object_force(field_id_size, message);
848 }
849 }
850 }
851
859 template<typename MessageType>
860 inline void add_repeated_message(uint32_t field_id_size, const FixedVector<MessageType> &messages) {
861 // Skip if the fixed vector is empty
862 if (!messages.empty()) {
863 // Use the force version for all messages in the repeated field
864 for (const auto &message : messages) {
865 add_message_object_force(field_id_size, message);
866 }
867 }
868 }
869
873 inline void add_packed_sint32(uint32_t field_id_size, const std::vector<int32_t> &values) {
874 if (values.empty())
875 return;
876
877 size_t packed_size = 0;
878 for (int value : values) {
879 packed_size += varint(encode_zigzag32(value));
880 }
881
882 // field_id + length varint + packed data
883 total_size_ += field_id_size + varint(static_cast<uint32_t>(packed_size)) + static_cast<uint32_t>(packed_size);
884 }
885};
886
887// Implementation of encode_packed_sint32 - must be after ProtoSize is defined
888inline void ProtoWriteBuffer::encode_packed_sint32(uint32_t field_id, const std::vector<int32_t> &values) {
889 if (values.empty())
890 return;
891
892 // Calculate packed size
893 size_t packed_size = 0;
894 for (int value : values) {
895 packed_size += ProtoSize::varint(encode_zigzag32(value));
896 }
897
898 // Write tag (LENGTH_DELIMITED) + length + all zigzag-encoded values
900 this->encode_varint_raw(packed_size);
901 for (int value : values) {
903 }
904}
905
906// Implementation of encode_message - must be after ProtoMessage is defined
907inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessage &value) {
908 this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
909
910 // Calculate the message size first
911 ProtoSize msg_size;
912 value.calculate_size(msg_size);
913 uint32_t msg_length_bytes = msg_size.get_size();
914
915 // Calculate how many bytes the length varint needs
916 uint32_t varint_length_bytes = ProtoSize::varint(msg_length_bytes);
917
918 // Reserve exact space for the length varint
919 size_t begin = this->buffer_->size();
920 this->buffer_->resize(this->buffer_->size() + varint_length_bytes);
921
922 // Write the length varint directly
923 ProtoVarInt(msg_length_bytes).encode_to_buffer_unchecked(this->buffer_->data() + begin, varint_length_bytes);
924
925 // Now encode the message content - it will append to the buffer
926 value.encode(*this);
927
928 // Verify that the encoded size matches what we calculated
929 assert(this->buffer_->size() == begin + varint_length_bytes + msg_length_bytes);
930}
931
932// Implementation of decode_to_message - must be after ProtoDecodableMessage is defined
934 msg.decode(this->value_, this->length_);
935}
936
937template<typename T> const char *proto_enum_to_string(T value);
938
940 public:
941 protected:
942 virtual bool is_authenticated() = 0;
943 virtual bool is_connection_setup() = 0;
944 virtual void on_fatal_error() = 0;
945 virtual void on_no_setup_connection() = 0;
953 virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0;
954 virtual bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) = 0;
955 virtual void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) = 0;
956
957 // Optimized method that pre-allocates buffer based on message size
958 bool send_message_(const ProtoMessage &msg, uint8_t message_type) {
959 ProtoSize size;
960 msg.calculate_size(size);
961 uint32_t msg_size = size.get_size();
962
963 // Create a pre-sized buffer
964 auto buffer = this->create_buffer(msg_size);
965
966 // Encode message into the buffer
967 msg.encode(buffer);
968
969 // Send the buffer
970 return this->send_buffer(buffer, message_type);
971 }
972
973 // Authentication helper methods
975 if (!this->is_connection_setup()) {
977 return false;
978 }
979 return true;
980 }
981
982 inline bool check_authenticated_() { return this->check_connection_setup_(); }
983};
984
985} // 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:188
bool empty() const
Definition helpers.h:346
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:371
const char * c_str() const
Definition proto.h:402
DumpBuffer & append(size_t n, char c)
Definition proto.h:390
size_t size() const
Definition proto.h:403
static constexpr size_t CAPACITY
Definition proto.h:374
DumpBuffer & append(const char *str, size_t len)
Definition proto.h:385
DumpBuffer & append(const char *str)
Definition proto.h:378
uint32_t as_fixed32() const
Definition proto.h:234
int32_t as_sfixed32() const
Definition proto.h:235
float as_float() const
Definition proto.h:236
const uint32_t value_
Definition proto.h:246
Proto32Bit(uint32_t value)
Definition proto.h:233
virtual bool decode_32bit(uint32_t field_id, Proto32Bit value)
Definition proto.h:454
virtual bool decode_varint(uint32_t field_id, ProtoVarInt value)
Definition proto.h:452
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:453
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:933
const uint8_t *const value_
Definition proto.h:227
const uint8_t * data() const
Definition proto.h:212
ProtoLengthDelimited(const uint8_t *value, size_t length)
Definition proto.h:208
std::string as_string() const
Definition proto.h:209
virtual void encode(ProtoWriteBuffer buffer) const
Definition proto.h:426
virtual const char * message_name() const
Definition proto.h:431
virtual ~ProtoMessage()=default
virtual void calculate_size(ProtoSize &size) const
Definition proto.h:428
virtual const char * dump_to(DumpBuffer &out) const =0
virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size)=0
Create a buffer with a reserved size.
virtual void on_fatal_error()=0
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
bool send_message_(const ProtoMessage &msg, uint8_t message_type)
Definition proto.h:958
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:807
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:491
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:792
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:822
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:652
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:576
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:546
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:676
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:625
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:607
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:860
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:562
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:719
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:690
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:781
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:643
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:710
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:633
uint32_t get_size() const
Definition proto.h:483
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:616
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:515
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:842
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:727
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:757
void add_int32(uint32_t field_id_size, int32_t value)
Common parameters for all add_*_field methods.
Definition proto.h:598
void add_precalculated_size(uint32_t size)
Adds a pre-calculated size directly to the total.
Definition proto.h:771
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:701
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:736
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:664
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:873
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:747
Representation of a VarInt - in ProtoBuf should be 64bit but we only use 32bit.
Definition proto.h:91
constexpr uint16_t as_uint16() const
Definition proto.h:132
constexpr uint64_t as_uint64() const
Definition proto.h:134
constexpr int32_t as_int32() const
Definition proto.h:136
void encode_to_buffer_unchecked(uint8_t *buffer, size_t len)
Encode the varint value to a pre-allocated buffer without bounds checking.
Definition proto.h:163
void encode(std::vector< uint8_t > &out)
Definition proto.h:180
constexpr uint32_t as_uint32() const
Definition proto.h:133
constexpr int64_t as_int64() const
Definition proto.h:140
constexpr bool as_bool() const
Definition proto.h:135
ProtoVarInt(uint64_t value)
Definition proto.h:94
constexpr int32_t as_sint32() const
Definition proto.h:144
constexpr int64_t as_sint64() const
Definition proto.h:148
static optional< ProtoVarInt > parse(const uint8_t *buffer, uint32_t len, uint32_t *consumed)
Definition proto.h:96
void encode_varint_raw(uint32_t value)
Definition proto.h:256
void encode_string(uint32_t field_id, const std::string &value, bool force=false)
Definition proto.h:287
void write(uint8_t value)
Definition proto.h:254
void encode_int64(uint32_t field_id, int64_t value, bool force=false)
Definition proto.h:347
void encode_float(uint32_t field_id, float value, bool force=false)
Definition proto.h:328
void encode_int32(uint32_t field_id, int32_t value, bool force=false)
Definition proto.h:339
void encode_sint64(uint32_t field_id, int64_t value, bool force=false)
Definition proto.h:353
void encode_string(uint32_t field_id, const char *string, size_t len, bool force=false)
Definition proto.h:273
void encode_bool(uint32_t field_id, bool value, bool force=false)
Definition proto.h:308
ProtoWriteBuffer(std::vector< uint8_t > *buffer)
Definition proto.h:253
void encode_uint64(uint32_t field_id, uint64_t value, bool force=false)
Definition proto.h:302
void encode_string(uint32_t field_id, const StringRef &ref, bool force=false)
Definition proto.h:290
void encode_uint32(uint32_t field_id, uint32_t value, bool force=false)
Definition proto.h:296
void encode_sint32(uint32_t field_id, int32_t value, bool force=false)
Definition proto.h:350
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:888
void encode_field_raw(uint32_t field_id, uint32_t type)
Encode a field key (tag/wire type combination).
Definition proto.h:269
void encode_message(uint32_t field_id, const ProtoMessage &value)
Definition proto.h:907
std::vector< uint8_t > * get_buffer() const
Definition proto.h:359
void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force=false)
Definition proto.h:293
void encode_fixed32(uint32_t field_id, uint32_t value, bool force=false)
Definition proto.h:314
void encode_varint_raw(ProtoVarInt value)
Definition proto.h:255
std::vector< uint8_t > * buffer_
Definition proto.h:362
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
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:595
uint16_t length
Definition tt21100.cpp:0