ESPHome 2026.6.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"
4#include "api_buffer.h"
7#include "esphome/core/log.h"
10
11#include <cassert>
12#include <cstring>
13#include <vector>
14
15#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
16#define HAS_PROTO_MESSAGE_DUMP
17#endif
18
19namespace esphome::api {
20
21// Protocol Buffer wire type constants
22// See https://protobuf.dev/programming-guides/encoding/#structure
23constexpr uint8_t WIRE_TYPE_VARINT = 0; // int32, int64, uint32, uint64, sint32, sint64, bool, enum
24constexpr uint8_t WIRE_TYPE_LENGTH_DELIMITED = 2; // string, bytes, embedded messages, packed repeated fields
25constexpr uint8_t WIRE_TYPE_FIXED32 = 5; // fixed32, sfixed32, float
26constexpr uint8_t WIRE_TYPE_MASK = 0b111; // Mask to extract wire type from tag
27
28// Reinterpret float bits as uint32_t without floating-point comparison.
29// Used by both encode_float() and calc_float() to ensure identical zero checks.
30// Uses union type-punning which is a GCC/Clang extension (not standard C++),
31// but bit_cast/memcpy don't optimize to a no-op on xtensa-gcc (ESP8266).
32inline uint32_t float_to_raw(float value) {
33 union {
34 float f;
35 uint32_t u;
36 } v;
37 v.f = value;
38 return v.u;
39}
40
41// Helper functions for ZigZag encoding/decoding
42inline constexpr uint32_t encode_zigzag32(int32_t value) {
43 return (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
44}
45
46inline constexpr uint64_t encode_zigzag64(int64_t value) {
47 return (static_cast<uint64_t>(value) << 1) ^ (static_cast<uint64_t>(value >> 63));
48}
49
50inline constexpr int32_t decode_zigzag32(uint32_t value) {
51 return (value & 1) ? static_cast<int32_t>(~(value >> 1)) : static_cast<int32_t>(value >> 1);
52}
53
54inline constexpr int64_t decode_zigzag64(uint64_t value) {
55 return (value & 1) ? static_cast<int64_t>(~(value >> 1)) : static_cast<int64_t>(value >> 1);
56}
57
59inline uint16_t count_packed_varints(const uint8_t *data, size_t len) {
60 uint16_t count = 0;
61 while (len > 0) {
62 // Skip varint bytes until we find one without continuation bit
63 while (len > 0 && (*data & 0x80)) {
64 data++;
65 len--;
66 }
67 if (len > 0) {
68 data++;
69 len--;
70 count++;
71 }
72 }
73 return count;
74}
75
78inline void encode_varint_to_buffer(uint32_t val, uint8_t *buffer) {
79 while (val > 0x7F) {
80 *buffer++ = static_cast<uint8_t>(val | 0x80);
81 val >>= 7;
82 }
83 *buffer = static_cast<uint8_t>(val);
84}
85
86/*
87 * StringRef Ownership Model for API Protocol Messages
88 * ===================================================
89 *
90 * StringRef is used for zero-copy string handling in outgoing (SOURCE_SERVER) messages.
91 * It holds a pointer and length to existing string data without copying.
92 *
93 * CRITICAL: The referenced string data MUST remain valid until message encoding completes.
94 *
95 * Safe StringRef Patterns:
96 * 1. String literals: StringRef("literal") - Always safe (static storage duration)
97 * 2. Member variables: StringRef(this->member_string_) - Safe if object outlives encoding
98 * 3. Global/static strings: StringRef(GLOBAL_CONSTANT) - Always safe
99 * 4. Local variables: Safe ONLY if encoding happens before function returns:
100 * std::string temp = compute_value();
101 * msg.field = StringRef(temp);
102 * return this->send_message(msg); // temp is valid during encoding
103 *
104 * Unsafe Patterns (WILL cause crashes/corruption):
105 * 1. Temporaries: msg.field = StringRef(obj.get_string()) // get_string() returns by value
106 * 2. Concatenation: msg.field = StringRef(str1 + str2) // Result is temporary
107 *
108 * For unsafe patterns, store in a local variable first:
109 * std::string temp = get_string(); // or str1 + str2
110 * msg.field = StringRef(temp);
111 *
112 * The send_*_response pattern ensures proper lifetime management by encoding
113 * within the same function scope where temporaries are created.
114 */
115
117#ifdef USE_API_VARINT64
118using proto_varint_value_t = uint64_t;
119#else
121#endif
122
125
130 uint32_t consumed; // PROTO_VARINT_PARSE_FAILED = parse failed
131
132 constexpr bool has_value() const { return this->consumed != PROTO_VARINT_PARSE_FAILED; }
133};
134
137 public:
140 static inline ProtoVarIntResult ESPHOME_ALWAYS_INLINE parse_non_empty(const uint8_t *buffer, uint32_t len) {
141#ifdef ESPHOME_DEBUG_API
142 assert(len > 0);
143#endif
144 // Fast path: single-byte varints (0-127) are the most common case
145 // (booleans, small enums, field tags, small message sizes/types).
146 if ((buffer[0] & 0x80) == 0) [[likely]]
147 return {buffer[0], 1};
148 return parse_slow(buffer, len);
149 }
150
153 static inline ProtoVarIntResult ESPHOME_ALWAYS_INLINE parse(const uint8_t *buffer, uint32_t len) {
154 if (len == 0)
155 return {0, PROTO_VARINT_PARSE_FAILED};
156 return parse_non_empty(buffer, len);
157 }
158
159 protected:
160 // Slow path for multi-byte varints (>= 128), outlined to keep fast path small
161 static ProtoVarIntResult parse_slow(const uint8_t *buffer, uint32_t len) __attribute__((noinline));
162
163#ifdef USE_API_VARINT64
165 static ProtoVarIntResult parse_wide(const uint8_t *buffer, uint32_t len, uint32_t result32) __attribute__((noinline));
166#endif
167};
168
169// Forward declarations for encoding helpers
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
184 template<typename T> void decode_to_message(T &msg) const;
185
186 protected:
187 const uint8_t *const value_;
188 const size_t length_;
189};
190
192 public:
193 explicit Proto32Bit(uint32_t value) : value_(value) {}
194 uint32_t as_fixed32() const { return this->value_; }
195 int32_t as_sfixed32() const { return static_cast<int32_t>(this->value_); }
196 float as_float() const {
197 union {
199 float value;
200 } s{};
201 s.raw = this->value_;
202 return s.value;
203 }
204
205 protected:
207};
208
209// NOTE: Proto64Bit class removed - wire type 1 (64-bit fixed) not supported
210
211// Debug bounds checking for proto encode functions.
212// In debug mode (ESPHOME_DEBUG_API), an extra end-of-buffer pointer is threaded
213// through the entire encode chain. In production, these expand to nothing.
214#ifdef ESPHOME_DEBUG_API
215#define PROTO_ENCODE_DEBUG_PARAM , uint8_t *proto_debug_end_
216#define PROTO_ENCODE_DEBUG_ARG , proto_debug_end_
217#define PROTO_ENCODE_DEBUG_INIT(buf) , (buf)->data() + (buf)->size()
218#define PROTO_ENCODE_CHECK_BOUNDS(pos, n) \
219 do { \
220 if ((pos) + (n) > proto_debug_end_) \
221 proto_check_bounds_failed(pos, n, proto_debug_end_, __builtin_FUNCTION()); \
222 } while (0)
223void proto_check_bounds_failed(const uint8_t *pos, size_t bytes, const uint8_t *end, const char *caller);
224#else
225#define PROTO_ENCODE_DEBUG_PARAM
226#define PROTO_ENCODE_DEBUG_ARG
227#define PROTO_ENCODE_DEBUG_INIT(buf)
228#define PROTO_ENCODE_CHECK_BOUNDS(pos, n)
229#endif
230
232 public:
233 ProtoWriteBuffer(APIBuffer *buffer) : buffer_(buffer), pos_(buffer->data() + buffer->size()) {}
234 ProtoWriteBuffer(APIBuffer *buffer, size_t write_pos) : buffer_(buffer), pos_(buffer->data() + write_pos) {}
235 inline void ESPHOME_ALWAYS_INLINE encode_varint_raw(uint32_t value) {
236 if (value < 128) [[likely]] {
237 this->debug_check_bounds_(1);
238 *this->pos_++ = static_cast<uint8_t>(value);
239 return;
240 }
241 this->encode_varint_raw_slow_(value);
242 }
255 void encode_field_raw(uint32_t field_id, uint32_t type) { this->encode_varint_raw((field_id << 3) | type); }
258 template<typename T> void encode_sub_message(uint32_t field_id, const T &value);
261 template<typename T> void encode_optional_sub_message(uint32_t field_id, const T &value);
262
263 // NOLINTBEGIN(readability-identifier-naming)
264 // Non-template core for encode_sub_message — backpatch approach.
265 void encode_sub_message(uint32_t field_id, const void *value,
266 uint8_t *(*encode_fn)(const void *, ProtoWriteBuffer &PROTO_ENCODE_DEBUG_PARAM));
267 // Non-template core for encode_optional_sub_message.
268 void encode_optional_sub_message(uint32_t field_id, uint32_t nested_size, const void *value,
269 uint8_t *(*encode_fn)(const void *, ProtoWriteBuffer &PROTO_ENCODE_DEBUG_PARAM));
270 // NOLINTEND(readability-identifier-naming)
271 APIBuffer *get_buffer() const { return buffer_; }
272 uint8_t *get_pos() const { return pos_; }
273 void set_pos(uint8_t *pos) { pos_ = pos; }
274
275 protected:
276 // Slow path for encode_varint_raw values >= 128, outlined to keep fast path small
277 void encode_varint_raw_slow_(uint32_t value) __attribute__((noinline));
278
279#ifdef ESPHOME_DEBUG_API
280 void debug_check_bounds_(size_t bytes, const char *caller = __builtin_FUNCTION());
281 void debug_check_encode_size_(uint32_t field_id, uint32_t expected, ptrdiff_t actual);
282#else
283 void debug_check_bounds_([[maybe_unused]] size_t bytes) {}
284#endif
285
287 uint8_t *pos_;
288};
289
290// Varint encoding thresholds — used by both proto_encode_* free functions and ProtoSize.
291constexpr uint32_t VARINT_MAX_1_BYTE = 1 << 7; // 128
292constexpr uint32_t VARINT_MAX_2_BYTE = 1 << 14; // 16384
293
299 public:
301 template<typename T>
302 static inline void encode_varint_raw_loop(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, T value) {
303 do {
304 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
305 *pos++ = static_cast<uint8_t>(value | 0x80);
306 value >>= 7;
307 } while (value > 0x7F);
308 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
309 *pos++ = static_cast<uint8_t>(value);
310 }
311 static inline void ESPHOME_ALWAYS_INLINE encode_varint_raw(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM,
312 uint32_t value) {
313 if (value < VARINT_MAX_1_BYTE) [[likely]] {
314 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
315 *pos++ = static_cast<uint8_t>(value);
316 return;
317 }
318 encode_varint_raw_loop(pos PROTO_ENCODE_DEBUG_ARG, value);
319 }
321 static inline void ESPHOME_ALWAYS_INLINE encode_varint_raw_short(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM,
322 uint32_t value) {
323 if (value < VARINT_MAX_1_BYTE) [[likely]] {
324 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
325 *pos++ = static_cast<uint8_t>(value);
326 return;
327 }
328 if (value < VARINT_MAX_2_BYTE) [[likely]] {
329 PROTO_ENCODE_CHECK_BOUNDS(pos, 2);
330 *pos++ = static_cast<uint8_t>(value | 0x80);
331 *pos++ = static_cast<uint8_t>(value >> 7);
332 return;
333 }
334 encode_varint_raw_loop(pos PROTO_ENCODE_DEBUG_ARG, value);
335 }
336 static inline void ESPHOME_ALWAYS_INLINE encode_varint_raw_64(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM,
337 uint64_t value) {
338 if (value < VARINT_MAX_1_BYTE) [[likely]] {
339 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
340 *pos++ = static_cast<uint8_t>(value);
341 return;
342 }
343 encode_varint_raw_loop(pos PROTO_ENCODE_DEBUG_ARG, value);
344 }
350 static inline void ESPHOME_ALWAYS_INLINE encode_varint_raw_48bit(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM,
351 uint64_t value) {
352#ifdef ESPHOME_DEBUG_API
353 assert(value < (1ULL << (MAC_ADDRESS_SIZE * 8)) && "encode_varint_raw_48bit: value exceeds 48 bits");
354#endif
355 // 7-byte varint holds 49 bits (7 * 7), so a 48-bit value needs all 7 bytes
356 // whenever bit 42 or higher is set (i.e. value >= 1 << (48 - 6)).
357 if (value >= (1ULL << (MAC_ADDRESS_SIZE * 8 - 6))) [[likely]] {
358 PROTO_ENCODE_CHECK_BOUNDS(pos, 7);
359 pos[0] = static_cast<uint8_t>(value | 0x80);
360 pos[1] = static_cast<uint8_t>((value >> 7) | 0x80);
361 pos[2] = static_cast<uint8_t>((value >> 14) | 0x80);
362 pos[3] = static_cast<uint8_t>((value >> 21) | 0x80);
363 pos[4] = static_cast<uint8_t>((value >> 28) | 0x80);
364 pos[5] = static_cast<uint8_t>((value >> 35) | 0x80);
365 pos[6] = static_cast<uint8_t>(value >> 42);
366 pos += 7;
367 return;
368 }
369 encode_varint_raw_64(pos PROTO_ENCODE_DEBUG_ARG, value);
370 }
371 static inline void ESPHOME_ALWAYS_INLINE encode_field_raw(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM,
372 uint32_t field_id, uint32_t type) {
373 encode_varint_raw(pos PROTO_ENCODE_DEBUG_ARG, (field_id << 3) | type);
374 }
376 static inline void ESPHOME_ALWAYS_INLINE write_raw_byte(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM,
377 uint8_t b) {
378 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
379 *pos++ = b;
380 }
383 static inline void ESPHOME_ALWAYS_INLINE reserve_byte(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM) {
384 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
385 pos++;
386 }
388 static inline void ESPHOME_ALWAYS_INLINE encode_raw(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM,
389 const void *data, size_t len) {
390 PROTO_ENCODE_CHECK_BOUNDS(pos, len);
391 std::memcpy(pos, data, len);
392 pos += len;
393 }
396 static inline void encode_short_string_force(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint8_t tag,
397 const StringRef &ref) {
398#ifdef ESPHOME_DEBUG_API
399 assert(ref.size() < 128 && "encode_short_string_force: string exceeds max_data_length < 128");
400#endif
401 PROTO_ENCODE_CHECK_BOUNDS(pos, 2 + ref.size());
402 pos[0] = tag;
403 pos[1] = static_cast<uint8_t>(ref.size());
404 std::memcpy(pos + 2, ref.c_str(), ref.size());
405 pos += 2 + ref.size();
406 }
408 static inline void ESPHOME_ALWAYS_INLINE write_tag_and_fixed32(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM,
409 uint8_t tag, uint32_t value) {
410 PROTO_ENCODE_CHECK_BOUNDS(pos, 5);
411 pos[0] = tag;
412#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
413 std::memcpy(pos + 1, &value, 4);
414#else
415 pos[1] = static_cast<uint8_t>(value & 0xFF);
416 pos[2] = static_cast<uint8_t>((value >> 8) & 0xFF);
417 pos[3] = static_cast<uint8_t>((value >> 16) & 0xFF);
418 pos[4] = static_cast<uint8_t>((value >> 24) & 0xFF);
419#endif
420 pos += 5;
421 }
422 static inline void encode_string(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id,
423 const char *string, size_t len, bool force = false) {
424 if (len == 0 && !force)
425 return;
426 encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 2); // type 2: Length-delimited string
427 // NOLINTNEXTLINE(readability-inconsistent-ifelse-braces) -- false positive on [[likely]] attribute
428 if (len < VARINT_MAX_1_BYTE) [[likely]] {
429 PROTO_ENCODE_CHECK_BOUNDS(pos, 1 + len);
430 *pos++ = static_cast<uint8_t>(len);
431 } else {
432 encode_varint_raw_loop(pos PROTO_ENCODE_DEBUG_ARG, len);
433 PROTO_ENCODE_CHECK_BOUNDS(pos, len);
434 }
435 std::memcpy(pos, string, len);
436 pos += len;
437 }
438 static inline void encode_string(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id,
439 const std::string &value, bool force = false) {
440 encode_string(pos PROTO_ENCODE_DEBUG_ARG, field_id, value.data(), value.size(), force);
441 }
442 static inline void encode_string(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id,
443 const StringRef &ref, bool force = false) {
444 encode_string(pos PROTO_ENCODE_DEBUG_ARG, field_id, ref.c_str(), ref.size(), force);
445 }
446 static inline void encode_bytes(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id,
447 const uint8_t *data, size_t len, bool force = false) {
448 encode_string(pos PROTO_ENCODE_DEBUG_ARG, field_id, reinterpret_cast<const char *>(data), len, force);
449 }
450 static inline void encode_uint32(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id,
451 uint32_t value, bool force = false) {
452 if (value == 0 && !force)
453 return;
454 encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 0);
455 encode_varint_raw(pos PROTO_ENCODE_DEBUG_ARG, value);
456 }
457 static inline void encode_uint64(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id,
458 uint64_t value, bool force = false) {
459 if (value == 0 && !force)
460 return;
461 encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 0);
462 encode_varint_raw_64(pos PROTO_ENCODE_DEBUG_ARG, value);
463 }
464 static inline void encode_bool(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, bool value,
465 bool force = false) {
466 if (!value && !force)
467 return;
468 encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 0);
469 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
470 *pos++ = value ? 0x01 : 0x00;
471 }
472 static inline void encode_fixed32(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id,
473 uint32_t value, bool force = false) {
474 if (value == 0 && !force)
475 return;
476 encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 5);
477 PROTO_ENCODE_CHECK_BOUNDS(pos, 4);
478#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
479 std::memcpy(pos, &value, 4);
480 pos += 4;
481#else
482 *pos++ = (value >> 0) & 0xFF;
483 *pos++ = (value >> 8) & 0xFF;
484 *pos++ = (value >> 16) & 0xFF;
485 *pos++ = (value >> 24) & 0xFF;
486#endif
487 }
488 // NOTE: Wire type 1 (64-bit fixed: double, fixed64, sfixed64) is intentionally
489 // not supported to reduce overhead on embedded systems. All ESPHome devices are
490 // 32-bit microcontrollers where 64-bit operations are expensive. If 64-bit support
491 // is needed in the future, the necessary encoding/decoding functions must be added.
492 static inline void encode_float(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, float value,
493 bool force = false) {
494 uint32_t raw = float_to_raw(value);
495 if (raw == 0 && !force)
496 return;
497 encode_fixed32(pos PROTO_ENCODE_DEBUG_ARG, field_id, raw);
498 }
499 static inline void encode_int32(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, int32_t value,
500 bool force = false) {
501 if (value < 0) {
502 // negative int32 is always 10 byte long
503 encode_uint64(pos PROTO_ENCODE_DEBUG_ARG, field_id, static_cast<uint64_t>(value), force);
504 return;
505 }
506 encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, field_id, static_cast<uint32_t>(value), force);
507 }
508 static inline void encode_int64(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, int64_t value,
509 bool force = false) {
510 encode_uint64(pos PROTO_ENCODE_DEBUG_ARG, field_id, static_cast<uint64_t>(value), force);
511 }
512 static inline void encode_sint32(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id,
513 int32_t value, bool force = false) {
514 encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, field_id, encode_zigzag32(value), force);
515 }
516 static inline void encode_sint64(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id,
517 int64_t value, bool force = false) {
518 encode_uint64(pos PROTO_ENCODE_DEBUG_ARG, field_id, encode_zigzag64(value), force);
519 }
521 template<typename T>
522 static inline void encode_sub_message(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, ProtoWriteBuffer &buffer,
523 uint32_t field_id, const T &value) {
524 buffer.set_pos(pos);
525 buffer.encode_sub_message(field_id, value);
526 pos = buffer.get_pos();
527 }
528 template<typename T>
529 static inline void encode_optional_sub_message(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM,
530 ProtoWriteBuffer &buffer, uint32_t field_id, const T &value) {
531 buffer.set_pos(pos);
532 buffer.encode_optional_sub_message(field_id, value);
533 pos = buffer.get_pos();
534 }
535};
536
537#ifdef HAS_PROTO_MESSAGE_DUMP
544 public:
545 // Matches default tx_buffer_size in logger component
546 static constexpr size_t CAPACITY = 512;
547
548 DumpBuffer() : pos_(0) { buf_[0] = '\0'; }
549
550 DumpBuffer &append(const char *str) {
551 if (str) {
552 append_impl_(str, strlen(str));
553 }
554 return *this;
555 }
556
557 DumpBuffer &append(const char *str, size_t len) {
558 append_impl_(str, len);
559 return *this;
560 }
561
562 DumpBuffer &append(size_t n, char c) {
563 size_t space = CAPACITY - 1 - pos_;
564 if (n > space)
565 n = space;
566 if (n > 0) {
567 memset(buf_ + pos_, c, n);
568 pos_ += n;
569 buf_[pos_] = '\0';
570 }
571 return *this;
572 }
573
575 DumpBuffer &append_p(const char *str) {
576 if (str) {
577#ifdef USE_ESP8266
578 append_p_esp8266(str);
579#else
580 append_impl_(str, strlen(str));
581#endif
582 }
583 return *this;
584 }
585
586#ifdef USE_ESP8266
588 void append_p_esp8266(const char *str);
589#endif
590
591 const char *c_str() const { return buf_; }
592 size_t size() const { return pos_; }
593
595 char *data() { return buf_; }
597 size_t pos() const { return pos_; }
599 void set_pos(size_t pos) {
600 if (pos >= CAPACITY) {
601 pos_ = CAPACITY - 1;
602 } else {
603 pos_ = pos;
604 }
605 buf_[pos_] = '\0';
606 }
607
608 private:
609 void append_impl_(const char *str, size_t len) {
610 size_t space = CAPACITY - 1 - pos_;
611 if (len > space)
612 len = space;
613 if (len > 0) {
614 memcpy(buf_ + pos_, str, len);
615 pos_ += len;
616 buf_[pos_] = '\0';
617 }
618 }
619
620 char buf_[CAPACITY];
621 size_t pos_;
622};
623#endif
624
626 public:
627 // Non-virtual defaults for messages with no fields.
628 // Concrete message classes hide these with their own implementations.
629 // All call sites use templates to preserve the concrete type, so virtual
630 // dispatch is not needed. This eliminates per-message vtable entries for
631 // encode/calculate_size, saving ~1.3 KB of flash across all message types.
632 uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const { return buffer.get_pos(); }
633 uint32_t calculate_size() const { return 0; }
634#ifdef HAS_PROTO_MESSAGE_DUMP
635 virtual const char *dump_to(DumpBuffer &out) const = 0;
636 virtual const LogString *message_name() const { return LOG_STR("unknown"); }
637#endif
638
639#ifndef USE_HOST
640 protected:
641#endif
642 // Non-virtual destructor is protected to prevent polymorphic deletion.
643 // On host platform, made public to allow value-initialization of std::array
644 // members (e.g. DeviceInfoResponse::devices) without clang errors.
645 ~ProtoMessage() = default;
646};
647
648// Base class for messages that support decoding
650 public:
651 void decode(const uint8_t *buffer, size_t length);
652
662 static uint32_t count_repeated_field(const uint8_t *buffer, size_t length, uint32_t target_field_id);
663
664 protected:
666 virtual bool decode_varint(uint32_t field_id, proto_varint_value_t value) { return false; }
667 virtual bool decode_length(uint32_t field_id, ProtoLengthDelimited value) { return false; }
668 virtual bool decode_32bit(uint32_t field_id, Proto32Bit value) { return false; }
669 // NOTE: decode_64bit removed - wire type 1 not supported
670};
671
673 public:
674 // Varint encoding thresholds — use namespace-level constants for 1/2 byte,
675 // class-level for 3/4 byte (only used within ProtoSize).
678 static constexpr uint32_t VARINT_THRESHOLD_3_BYTE = 1 << 21; // 2097152
679 static constexpr uint32_t VARINT_THRESHOLD_4_BYTE = 1 << 28; // 268435456
680
681 // Varint encoded length for a 16-bit value (1, 2, or 3 bytes).
682 // Fully inline — no slow path call for values >= 128.
683 static constexpr inline uint8_t ESPHOME_ALWAYS_INLINE varint16(uint16_t value) {
684 return value < VARINT_THRESHOLD_1_BYTE ? 1 : (value < VARINT_THRESHOLD_2_BYTE ? 2 : 3);
685 }
686
687 // Varint encoded length for an 8-bit value (1 or 2 bytes).
688 static constexpr inline uint8_t ESPHOME_ALWAYS_INLINE varint8(uint8_t value) {
689 return value < VARINT_THRESHOLD_1_BYTE ? 1 : 2;
690 }
691
698 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE varint(uint32_t value) {
699 if (value < VARINT_THRESHOLD_1_BYTE) [[likely]]
700 return 1; // Fast path: 7 bits, most common case
701 if (__builtin_is_constant_evaluated())
702 return varint_wide(value);
703 return varint_slow(value);
704 }
707 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE varint_short(uint32_t value) {
708 if (value < VARINT_THRESHOLD_1_BYTE) [[likely]]
709 return 1;
710 if (value < VARINT_THRESHOLD_2_BYTE) [[likely]]
711 return 2;
712 if (__builtin_is_constant_evaluated())
713 return varint_wide(value);
714 return varint_slow(value);
715 }
716
717 private:
718 // Slow path for varint >= 128, outlined to keep fast path small
719 static uint32_t varint_slow(uint32_t value) __attribute__((noinline));
720 // Shared cascade for values >= 128 (used by both constexpr and noinline paths)
721 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE varint_wide(uint32_t value) {
722 if (value < VARINT_THRESHOLD_2_BYTE)
723 return 2;
724 if (value < VARINT_THRESHOLD_3_BYTE)
725 return 3;
726 if (value < VARINT_THRESHOLD_4_BYTE)
727 return 4;
728 return 5;
729 }
730
731 public:
738 static constexpr uint32_t varint(uint64_t value) {
739 // Handle common case of values fitting in uint32_t (vast majority of use cases)
740 if (value <= UINT32_MAX) {
741 return varint(static_cast<uint32_t>(value));
742 }
743
744 // For larger values, determine size based on highest bit position
745 if (value < (1ULL << 35)) {
746 return 5; // 35 bits
747 } else if (value < (1ULL << 42)) {
748 return 6; // 42 bits
749 } else if (value < (1ULL << 49)) {
750 return 7; // 49 bits
751 } else if (value < (1ULL << 56)) {
752 return 8; // 56 bits
753 } else if (value < (1ULL << 63)) {
754 return 9; // 63 bits
755 } else {
756 return 10; // 64 bits (maximum for uint64_t)
757 }
758 }
759
769 static constexpr uint32_t varint(int32_t value) {
770 // Negative values are sign-extended to 64 bits in protocol buffers,
771 // which always results in a 10-byte varint for negative int32
772 if (value < 0) {
773 return 10; // Negative int32 is always 10 bytes long
774 }
775 // For non-negative values, use the uint32_t implementation
776 return varint(static_cast<uint32_t>(value));
777 }
778
785 static constexpr uint32_t varint(int64_t value) {
786 // For int64_t, we convert to uint64_t and calculate the size
787 // This works because the bit pattern determines the encoding size,
788 // and we've handled negative int32 values as a special case above
789 return varint(static_cast<uint64_t>(value));
790 }
791
799 static constexpr uint32_t field(uint32_t field_id, uint32_t type) {
800 uint32_t tag = (field_id << 3) | (type & WIRE_TYPE_MASK);
801 return varint(tag);
802 }
803
804 // Static methods that RETURN size contribution (no ProtoSize object needed).
805 // Used by generated calculate_size() methods to accumulate into a plain uint32_t register.
806 static constexpr uint32_t calc_int32(uint32_t field_id_size, int32_t value) {
807 return value ? field_id_size + (value < 0 ? 10 : varint(static_cast<uint32_t>(value))) : 0;
808 }
809 static constexpr uint32_t calc_int32_force(uint32_t field_id_size, int32_t value) {
810 return field_id_size + (value < 0 ? 10 : varint(static_cast<uint32_t>(value)));
811 }
812 static constexpr uint32_t calc_uint32(uint32_t field_id_size, uint32_t value) {
813 return value ? field_id_size + varint(value) : 0;
814 }
815 static constexpr uint32_t calc_uint32_force(uint32_t field_id_size, uint32_t value) {
816 return field_id_size + varint(value);
817 }
818 static constexpr uint32_t calc_bool(uint32_t field_id_size, bool value) { return value ? field_id_size + 1 : 0; }
819 static constexpr uint32_t calc_bool_force(uint32_t field_id_size) { return field_id_size + 1; }
820 static uint32_t calc_float(uint32_t field_id_size, float value) {
821 return float_to_raw(value) != 0 ? field_id_size + 4 : 0;
822 }
823 static constexpr uint32_t calc_fixed32(uint32_t field_id_size, uint32_t value) {
824 return value ? field_id_size + 4 : 0;
825 }
826 static constexpr uint32_t calc_sfixed32(uint32_t field_id_size, int32_t value) {
827 return value ? field_id_size + 4 : 0;
828 }
829 static constexpr uint32_t calc_sint32(uint32_t field_id_size, int32_t value) {
830 return value ? field_id_size + varint_short(encode_zigzag32(value)) : 0;
831 }
832 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE calc_sint32_force(uint32_t field_id_size, int32_t value) {
833 return field_id_size + varint_short(encode_zigzag32(value));
834 }
835 static constexpr uint32_t calc_int64(uint32_t field_id_size, int64_t value) {
836 return value ? field_id_size + varint(value) : 0;
837 }
838 static constexpr uint32_t calc_int64_force(uint32_t field_id_size, int64_t value) {
839 return field_id_size + varint(value);
840 }
841 static constexpr uint32_t calc_uint64(uint32_t field_id_size, uint64_t value) {
842 return value ? field_id_size + varint(value) : 0;
843 }
844 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE calc_uint64_force(uint32_t field_id_size, uint64_t value) {
845 return field_id_size + varint(value);
846 }
851 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE calc_uint64_48bit_force(uint32_t field_id_size,
852 uint64_t value) {
853 return field_id_size + (value >= (1ULL << (MAC_ADDRESS_SIZE * 8 - 6)) ? 7 : varint(value));
854 }
855 static constexpr uint32_t calc_length(uint32_t field_id_size, size_t len) {
856 return len ? field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len) : 0;
857 }
858 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE calc_length_force(uint32_t field_id_size, size_t len) {
859 return field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len);
860 }
861 static constexpr uint32_t calc_sint64(uint32_t field_id_size, int64_t value) {
862 return value ? field_id_size + varint(encode_zigzag64(value)) : 0;
863 }
864 static constexpr uint32_t calc_sint64_force(uint32_t field_id_size, int64_t value) {
865 return field_id_size + varint(encode_zigzag64(value));
866 }
867 static constexpr uint32_t calc_fixed64(uint32_t field_id_size, uint64_t value) {
868 return value ? field_id_size + 8 : 0;
869 }
870 static constexpr uint32_t calc_sfixed64(uint32_t field_id_size, int64_t value) {
871 return value ? field_id_size + 8 : 0;
872 }
873 static constexpr uint32_t calc_message(uint32_t field_id_size, uint32_t nested_size) {
874 return nested_size ? field_id_size + varint(nested_size) + nested_size : 0;
875 }
876 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE calc_message_force(uint32_t field_id_size,
877 uint32_t nested_size) {
878 return field_id_size + varint(nested_size) + nested_size;
879 }
880};
881
882// Implementation of methods that depend on ProtoSize being fully defined
883
884// Encode thunk — converts void* back to concrete type for direct encode() call
885template<typename T> uint8_t *proto_encode_msg(const void *msg, ProtoWriteBuffer &buf PROTO_ENCODE_DEBUG_PARAM) {
886 return static_cast<const T *>(msg)->encode(buf PROTO_ENCODE_DEBUG_ARG);
887}
888
889// Thin template wrapper; delegates to non-template core in proto.cpp.
890template<typename T> inline void ProtoWriteBuffer::encode_sub_message(uint32_t field_id, const T &value) {
891 this->encode_sub_message(field_id, &value, &proto_encode_msg<T>);
892}
893
894// Thin template wrapper; delegates to non-template core.
895template<typename T> inline void ProtoWriteBuffer::encode_optional_sub_message(uint32_t field_id, const T &value) {
896 this->encode_optional_sub_message(field_id, value.calculate_size(), &value, &proto_encode_msg<T>);
897}
898
899// Template decode_to_message - preserves concrete type so decode() resolves statically
900template<typename T> void ProtoLengthDelimited::decode_to_message(T &msg) const {
901 msg.decode(this->value_, this->length_);
902}
903
904template<typename T> const char *proto_enum_to_string(T value);
905
906// ProtoService removed — its methods were inlined into APIConnection.
907// APIConnection is the concrete server-side implementation; the extra virtual layer was unnecessary.
908
909} // namespace esphome::api
uint8_t raw[35]
Definition bl0939.h:0
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
Byte buffer that skips zero-initialization on resize().
Definition api_buffer.h:36
Fixed-size buffer for message dumps - avoids heap allocation.
Definition proto.h:543
const char * c_str() const
Definition proto.h:591
DumpBuffer & append(size_t n, char c)
Definition proto.h:562
size_t size() const
Definition proto.h:592
void append_p_esp8266(const char *str)
Out-of-line ESP8266 PROGMEM append to avoid inlining strlen_P/memcpy_P at every call site.
DumpBuffer & append_p(const char *str)
Append a PROGMEM string (flash-safe on ESP8266, regular append on other platforms)
Definition proto.h:575
size_t pos() const
Get current position for use with buf_append_printf.
Definition proto.h:597
static constexpr size_t CAPACITY
Definition proto.h:546
DumpBuffer & append(const char *str, size_t len)
Definition proto.h:557
DumpBuffer & append(const char *str)
Definition proto.h:550
char * data()
Get writable buffer pointer for use with buf_append_printf.
Definition proto.h:595
void set_pos(size_t pos)
Update position after buf_append_printf call.
Definition proto.h:599
uint32_t as_fixed32() const
Definition proto.h:194
int32_t as_sfixed32() const
Definition proto.h:195
float as_float() const
Definition proto.h:196
const uint32_t value_
Definition proto.h:206
Proto32Bit(uint32_t value)
Definition proto.h:193
virtual bool decode_32bit(uint32_t field_id, Proto32Bit value)
Definition proto.h:668
virtual bool decode_varint(uint32_t field_id, proto_varint_value_t value)
Definition proto.h:666
void decode(const uint8_t *buffer, size_t length)
Definition proto.cpp:213
virtual bool decode_length(uint32_t field_id, ProtoLengthDelimited value)
Definition proto.h:667
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:60
Static encode helpers for generated encode() functions.
Definition proto.h:298
static void encode_bool(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, bool value, bool force=false)
Definition proto.h:464
static void encode_string(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const char *string, size_t len, bool force=false)
Definition proto.h:422
static void encode_sint32(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, int32_t value, bool force=false)
Definition proto.h:512
static void ESPHOME_ALWAYS_INLINE encode_varint_raw_64(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint64_t value)
Definition proto.h:336
static void ESPHOME_ALWAYS_INLINE encode_varint_raw_short(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t value)
Encode a varint that is expected to be 1-2 bytes (e.g. zigzag RSSI, small lengths).
Definition proto.h:321
static void encode_string(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const StringRef &ref, bool force=false)
Definition proto.h:442
static void encode_int64(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, int64_t value, bool force=false)
Definition proto.h:508
static void encode_varint_raw_loop(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, T value)
Write a multi-byte varint directly through a pos pointer.
Definition proto.h:302
static void ESPHOME_ALWAYS_INLINE write_raw_byte(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint8_t b)
Write a single precomputed tag byte. Tag must be < 128.
Definition proto.h:376
static void encode_float(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, float value, bool force=false)
Definition proto.h:492
static void encode_short_string_force(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint8_t tag, const StringRef &ref)
Encode tag + 1-byte length + raw string data.
Definition proto.h:396
static void ESPHOME_ALWAYS_INLINE write_tag_and_fixed32(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint8_t tag, uint32_t value)
Write a precomputed tag byte + 32-bit value in one operation.
Definition proto.h:408
static void encode_optional_sub_message(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, ProtoWriteBuffer &buffer, uint32_t field_id, const T &value)
Definition proto.h:529
static void encode_sub_message(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, ProtoWriteBuffer &buffer, uint32_t field_id, const T &value)
Sub-message encoding: sync pos to buffer, delegate, get pos from return value.
Definition proto.h:522
static void ESPHOME_ALWAYS_INLINE encode_varint_raw_48bit(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint64_t value)
Encode a 48-bit MAC address (stored in a uint64) as varint.
Definition proto.h:350
static void ESPHOME_ALWAYS_INLINE reserve_byte(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM)
Reserve one byte for later backpatch (e.g., sub-message length).
Definition proto.h:383
static void encode_sint64(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, int64_t value, bool force=false)
Definition proto.h:516
static void ESPHOME_ALWAYS_INLINE encode_raw(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, const void *data, size_t len)
Write raw bytes to the buffer (no tag, no length prefix).
Definition proto.h:388
static void encode_fixed32(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint32_t value, bool force=false)
Definition proto.h:472
static void ESPHOME_ALWAYS_INLINE encode_field_raw(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint32_t type)
Definition proto.h:371
static void ESPHOME_ALWAYS_INLINE encode_varint_raw(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t value)
Definition proto.h:311
static void encode_uint64(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint64_t value, bool force=false)
Definition proto.h:457
static void encode_int32(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, int32_t value, bool force=false)
Definition proto.h:499
static void encode_bytes(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const uint8_t *data, size_t len, bool force=false)
Definition proto.h:446
static void encode_uint32(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint32_t value, bool force=false)
Definition proto.h:450
static void encode_string(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const std::string &value, bool force=false)
Definition proto.h:438
void decode_to_message(T &msg) const
Decode the length-delimited data into a message instance.
Definition proto.h:900
const uint8_t *const value_
Definition proto.h:187
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 const LogString * message_name() const
Definition proto.h:636
uint8_t * encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const
Definition proto.h:632
uint32_t calculate_size() const
Definition proto.h:633
virtual const char * dump_to(DumpBuffer &out) const =0
static constexpr uint32_t calc_uint64(uint32_t field_id_size, uint64_t value)
Definition proto.h:841
static constexpr uint32_t calc_int64(uint32_t field_id_size, int64_t value)
Definition proto.h:835
static constexpr uint8_t ESPHOME_ALWAYS_INLINE varint8(uint8_t value)
Definition proto.h:688
static constexpr uint32_t calc_bool(uint32_t field_id_size, bool value)
Definition proto.h:818
static constexpr uint32_t calc_message(uint32_t field_id_size, uint32_t nested_size)
Definition proto.h:873
static constexpr uint32_t calc_sint32(uint32_t field_id_size, int32_t value)
Definition proto.h:829
static constexpr uint32_t calc_sint64_force(uint32_t field_id_size, int64_t value)
Definition proto.h:864
static constexpr uint32_t ESPHOME_ALWAYS_INLINE calc_uint64_force(uint32_t field_id_size, uint64_t value)
Definition proto.h:844
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:799
static constexpr uint32_t ESPHOME_ALWAYS_INLINE varint(uint32_t value)
Calculates the size in bytes needed to encode a uint32_t value as a varint.
Definition proto.h:698
static constexpr uint32_t calc_uint32_force(uint32_t field_id_size, uint32_t value)
Definition proto.h:815
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:769
static constexpr uint32_t calc_int64_force(uint32_t field_id_size, int64_t value)
Definition proto.h:838
static constexpr uint32_t ESPHOME_ALWAYS_INLINE calc_uint64_48bit_force(uint32_t field_id_size, uint64_t value)
48-bit MAC address variant: matches encode_varint_raw_48bit's fast path.
Definition proto.h:851
static constexpr uint32_t calc_sfixed64(uint32_t field_id_size, int64_t value)
Definition proto.h:870
static constexpr uint32_t calc_uint32(uint32_t field_id_size, uint32_t value)
Definition proto.h:812
static constexpr uint32_t VARINT_THRESHOLD_3_BYTE
Definition proto.h:678
static constexpr uint8_t ESPHOME_ALWAYS_INLINE varint16(uint16_t value)
Definition proto.h:683
static constexpr uint32_t calc_length(uint32_t field_id_size, size_t len)
Definition proto.h:855
static constexpr uint32_t calc_int32(uint32_t field_id_size, int32_t value)
Definition proto.h:806
static constexpr uint32_t calc_fixed64(uint32_t field_id_size, uint64_t value)
Definition proto.h:867
static constexpr uint32_t ESPHOME_ALWAYS_INLINE varint_short(uint32_t value)
Size of a varint expected to be 1-2 bytes (e.g.
Definition proto.h:707
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:785
static constexpr uint32_t ESPHOME_ALWAYS_INLINE calc_length_force(uint32_t field_id_size, size_t len)
Definition proto.h:858
static constexpr uint32_t calc_bool_force(uint32_t field_id_size)
Definition proto.h:819
static constexpr uint32_t calc_sfixed32(uint32_t field_id_size, int32_t value)
Definition proto.h:826
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:738
static uint32_t calc_float(uint32_t field_id_size, float value)
Definition proto.h:820
static constexpr uint32_t calc_sint64(uint32_t field_id_size, int64_t value)
Definition proto.h:861
static constexpr uint32_t calc_int32_force(uint32_t field_id_size, int32_t value)
Definition proto.h:809
static constexpr uint32_t calc_fixed32(uint32_t field_id_size, uint32_t value)
Definition proto.h:823
static constexpr uint32_t VARINT_THRESHOLD_2_BYTE
Definition proto.h:677
static constexpr uint32_t ESPHOME_ALWAYS_INLINE calc_message_force(uint32_t field_id_size, uint32_t nested_size)
Definition proto.h:876
static constexpr uint32_t ESPHOME_ALWAYS_INLINE calc_sint32_force(uint32_t field_id_size, int32_t value)
Definition proto.h:832
static constexpr uint32_t VARINT_THRESHOLD_1_BYTE
Definition proto.h:676
static constexpr uint32_t VARINT_THRESHOLD_4_BYTE
Definition proto.h:679
Static varint parsing methods for the protobuf wire format.
Definition proto.h:136
static ProtoVarIntResult ESPHOME_ALWAYS_INLINE parse_non_empty(const uint8_t *buffer, uint32_t len)
Parse a varint from buffer.
Definition proto.h:140
static ProtoVarIntResult parse_wide(const uint8_t *buffer, uint32_t len, uint32_t result32) __attribute__((noinline))
Continue parsing varint bytes 4-9 with 64-bit arithmetic.
Definition proto.cpp:46
static ProtoVarIntResult parse_slow(const uint8_t *buffer, uint32_t len) __attribute__((noinline))
Definition proto.cpp:23
static ProtoVarIntResult ESPHOME_ALWAYS_INLINE parse(const uint8_t *buffer, uint32_t len)
Parse a varint from buffer (safe for empty buffers).
Definition proto.h:153
void set_pos(uint8_t *pos)
Definition proto.h:273
void debug_check_encode_size_(uint32_t field_id, uint32_t expected, ptrdiff_t actual)
Definition proto.cpp:205
void debug_check_bounds_(size_t bytes)
Definition proto.h:283
void encode_varint_raw_slow_(uint32_t value) __attribute__((noinline))
Definition proto.cpp:13
void encode_field_raw(uint32_t field_id, uint32_t type)
Encode a field key (tag/wire type combination).
Definition proto.h:255
void encode_optional_sub_message(uint32_t field_id, const T &value)
Encode an optional singular submessage field — skips if empty.
Definition proto.h:895
void encode_sub_message(uint32_t field_id, const T &value)
Single-pass encode for repeated submessage elements.
Definition proto.h:890
void debug_check_bounds_(size_t bytes, const char *caller=__builtin_FUNCTION())
Definition proto.cpp:198
uint8_t * get_pos() const
Definition proto.h:272
APIBuffer * get_buffer() const
Definition proto.h:271
void ESPHOME_ALWAYS_INLINE encode_varint_raw(uint32_t value)
Definition proto.h:235
ProtoWriteBuffer(APIBuffer *buffer, size_t write_pos)
Definition proto.h:234
ProtoWriteBuffer(APIBuffer *buffer)
Definition proto.h:233
struct @65::@66 __attribute__
Wake the main loop task from an ISR. ISR-safe.
Definition main_task.h:32
uint16_t type
mopeka_std_values val[3]
constexpr uint32_t encode_zigzag32(int32_t value)
Definition proto.h:42
constexpr uint32_t VARINT_MAX_2_BYTE
Definition proto.h:292
constexpr uint8_t WIRE_TYPE_VARINT
Definition proto.h:23
uint64_t proto_varint_value_t
Type used for decoded varint values - uint64_t when BLE needs 64-bit addresses, uint32_t otherwise.
Definition proto.h:118
uint8_t * proto_encode_msg(const void *msg, ProtoWriteBuffer &buf PROTO_ENCODE_DEBUG_PARAM)
Definition proto.h:885
const char * proto_enum_to_string(T value)
constexpr uint32_t VARINT_MAX_1_BYTE
Definition proto.h:291
constexpr uint64_t encode_zigzag64(int64_t value)
Definition proto.h:46
constexpr uint8_t WIRE_TYPE_MASK
Definition proto.h:26
constexpr uint32_t PROTO_VARINT_PARSE_FAILED
Sentinel value for consumed field indicating parse failure.
Definition proto.h:124
constexpr uint8_t WIRE_TYPE_LENGTH_DELIMITED
Definition proto.h:24
constexpr int32_t decode_zigzag32(uint32_t value)
Definition proto.h:50
constexpr uint8_t WIRE_TYPE_FIXED32
Definition proto.h:25
uint32_t float_to_raw(float value)
Definition proto.h:32
constexpr int64_t decode_zigzag64(uint64_t value)
Definition proto.h:54
void encode_varint_to_buffer(uint32_t val, uint8_t *buffer)
Encode a varint directly into a pre-allocated buffer.
Definition proto.h:78
uint16_t count_packed_varints(const uint8_t *data, size_t len)
Count number of varints in a packed buffer.
Definition proto.h:59
void proto_check_bounds_failed(const uint8_t *pos, size_t bytes, const uint8_t *end, const char *caller)
Definition proto.cpp:194
const char * tag
Definition log.h:74
const void size_t len
Definition hal.h:64
uint16_t size
Definition helpers.cpp:25
size_t size_t pos
Definition helpers.h:1038
static void uint32_t
Result of parsing a varint: value + number of bytes consumed.
Definition proto.h:128
constexpr bool has_value() const
Definition proto.h:132
proto_varint_value_t value
Definition proto.h:129
uint8_t end[39]
Definition sun_gtil2.cpp:17
uint16_t length
Definition tt21100.cpp:0