ESPHome 2026.11.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"
6#include "esphome/core/hal.h"
8#include "esphome/core/log.h"
11
12#include <cassert>
13#include <cstring>
14#include <type_traits>
15#include <vector>
16
17#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
18#define HAS_PROTO_MESSAGE_DUMP
19#endif
20
21namespace esphome::api {
22
23// Protocol Buffer wire type constants
24// See https://protobuf.dev/programming-guides/encoding/#structure
25constexpr uint8_t WIRE_TYPE_VARINT = 0; // int32, int64, uint32, uint64, sint32, sint64, bool, enum
26constexpr uint8_t WIRE_TYPE_LENGTH_DELIMITED = 2; // string, bytes, embedded messages, packed repeated fields
27constexpr uint8_t WIRE_TYPE_FIXED32 = 5; // fixed32, sfixed32, float
28constexpr uint8_t WIRE_TYPE_MASK = 0b111; // Mask to extract wire type from tag
29
30// Reinterpret float bits as uint32_t without floating-point comparison.
31// Used by both encode_float() and calc_float() to ensure identical zero checks.
32// Uses union type-punning which is a GCC/Clang extension (not standard C++),
33// but bit_cast/memcpy don't optimize to a no-op on xtensa-gcc (ESP8266).
34inline uint32_t float_to_raw(float value) {
35 union {
36 float f;
37 uint32_t u;
38 } v;
39 v.f = value;
40 return v.u;
41}
42
43// Helper functions for ZigZag encoding/decoding
44inline constexpr uint32_t encode_zigzag32(int32_t value) {
45 return (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
46}
47
48inline constexpr uint64_t encode_zigzag64(int64_t value) {
49 return (static_cast<uint64_t>(value) << 1) ^ (static_cast<uint64_t>(value >> 63));
50}
51
52inline constexpr int32_t decode_zigzag32(uint32_t value) {
53 return (value & 1) ? static_cast<int32_t>(~(value >> 1)) : static_cast<int32_t>(value >> 1);
54}
55
56inline constexpr int64_t decode_zigzag64(uint64_t value) {
57 return (value & 1) ? static_cast<int64_t>(~(value >> 1)) : static_cast<int64_t>(value >> 1);
58}
59
62template<typename Word = size_t> inline uint16_t count_packed_varints(const uint8_t *data, size_t len) {
63 constexpr size_t word_size = sizeof(Word);
64 constexpr Word lane_ones = ~Word{0} / 0xFF; // 0x01..01
65 const uint8_t *end = data + len;
66 size_t continuations = 0;
67 while (data != end) {
68 // Unaligned word loads fault on Xtensa
69 if ((reinterpret_cast<uintptr_t>(data) & (word_size - 1)) == 0 && static_cast<size_t>(end - data) >= word_size) {
70 Word word;
71 memcpy(&word, __builtin_assume_aligned(data, word_size), word_size);
72 continuations += (((word >> 7) & lane_ones) * lane_ones) >> (word_size * 8 - 8);
73 data += word_size;
74 } else {
75 continuations += *data++ >> 7;
76 }
77 }
78 return static_cast<uint16_t>(len - continuations);
79}
80
84 while (val > 0x7F) {
85 *buffer++ = static_cast<uint8_t>(val | 0x80);
86 val >>= 7;
87 }
88 *buffer = static_cast<uint8_t>(val);
89}
90
91/*
92 * StringRef Ownership Model for API Protocol Messages
93 * ===================================================
94 *
95 * StringRef is used for zero-copy string handling in outgoing (SOURCE_SERVER) messages.
96 * It holds a pointer and length to existing string data without copying.
97 *
98 * CRITICAL: The referenced string data MUST remain valid until message encoding completes.
99 *
100 * Safe StringRef Patterns:
101 * 1. String literals: StringRef("literal") - Always safe (static storage duration)
102 * 2. Member variables: StringRef(this->member_string_) - Safe if object outlives encoding
103 * 3. Global/static strings: StringRef(GLOBAL_CONSTANT) - Always safe
104 * 4. Local variables: Safe ONLY if encoding happens before function returns:
105 * std::string temp = compute_value();
106 * msg.field = StringRef(temp);
107 * return this->send_message(msg); // temp is valid during encoding
108 *
109 * Unsafe Patterns (WILL cause crashes/corruption):
110 * 1. Temporaries: msg.field = StringRef(obj.get_string()) // get_string() returns by value
111 * 2. Concatenation: msg.field = StringRef(str1 + str2) // Result is temporary
112 *
113 * For unsafe patterns, store in a local variable first:
114 * std::string temp = get_string(); // or str1 + str2
115 * msg.field = StringRef(temp);
116 *
117 * The send_*_response pattern ensures proper lifetime management by encoding
118 * within the same function scope where temporaries are created.
119 */
120
122#ifdef USE_API_VARINT64
123using proto_varint_value_t = uint64_t;
124#else
126#endif
127
130
135 uint32_t consumed; // PROTO_VARINT_PARSE_FAILED = parse failed
136
137 constexpr bool has_value() const { return this->consumed != PROTO_VARINT_PARSE_FAILED; }
138};
139
142 public:
145 static inline ProtoVarIntResult ESPHOME_ALWAYS_INLINE parse_non_empty(const uint8_t *buffer, uint32_t len) {
146#ifdef ESPHOME_DEBUG_API
147 assert(len > 0);
148#endif
149 // Fast path: single-byte varints (0-127) are the most common case
150 // (booleans, small enums, field tags, small message sizes/types).
151 if ((buffer[0] & 0x80) == 0) [[likely]]
152 return {buffer[0], 1};
153 return parse_slow(buffer, len);
154 }
155
158 static inline ProtoVarIntResult ESPHOME_ALWAYS_INLINE parse(const uint8_t *buffer, uint32_t len) {
159 if (len == 0)
160 return {0, PROTO_VARINT_PARSE_FAILED};
161 return parse_non_empty(buffer, len);
162 }
163
164 protected:
165 // Slow path for multi-byte varints (>= 128), outlined to keep fast path small
166 static ProtoVarIntResult parse_slow(const uint8_t *buffer, uint32_t len) __attribute__((noinline));
167
168#ifdef USE_API_VARINT64
170 static ProtoVarIntResult parse_wide(const uint8_t *buffer, uint32_t len, uint32_t result32) __attribute__((noinline));
171#endif
172};
173
174// Forward declarations for encoding helpers
175class ProtoMessage;
176class ProtoSize;
177
180constexpr uint32_t proto_tag(uint32_t field_id, uint32_t wire_type) { return (field_id << 3) | wire_type; }
181
185 public:
186 ProtoFieldValue(const uint8_t *data, proto_varint_value_t scalar) : data_(data), scalar_(scalar) {}
187
188 proto_varint_value_t as_varint() const { return this->scalar_; }
189 // A bool is sent as 0 or 1, so the low word is enough and saves a second compare with 64 bit varints
190 bool as_bool() const { return static_cast<uint32_t>(this->scalar_) != 0; }
191
192 // Length-delimited accessors
193 const uint8_t *data() const { return this->data_; }
194 size_t size() const { return static_cast<size_t>(this->scalar_); }
195 std::string as_string() const { return std::string(reinterpret_cast<const char *>(this->data_), this->size()); }
198 template<typename T> void decode_to_message(T &msg) const { msg.decode(this->data_, this->size()); }
199
200 // Fixed32 accessors
201 uint32_t as_fixed32() const { return static_cast<uint32_t>(this->scalar_); }
202 int32_t as_sfixed32() const { return static_cast<int32_t>(this->as_fixed32()); }
203 float as_float() const {
204 union {
206 float value;
207 } s{};
208 s.raw = this->as_fixed32();
209 return s.value;
210 }
211
212 private:
213 const uint8_t *data_;
214 proto_varint_value_t scalar_;
215};
216
217// NOTE: Proto64Bit class removed - wire type 1 (64-bit fixed) not supported
218
219// Debug bounds checking for proto encode functions.
220// In debug mode (ESPHOME_DEBUG_API), an extra end-of-buffer pointer is threaded
221// through the entire encode chain. In production, these expand to nothing.
222#ifdef ESPHOME_DEBUG_API
223#define PROTO_ENCODE_DEBUG_PARAM , uint8_t *proto_debug_end_
224#define PROTO_ENCODE_DEBUG_ARG , proto_debug_end_
225#define PROTO_ENCODE_DEBUG_INIT(buf) , (buf)->data() + (buf)->size()
226#define PROTO_ENCODE_CHECK_BOUNDS(pos, n) \
227 do { \
228 if ((pos) + (n) > proto_debug_end_) \
229 proto_check_bounds_failed(pos, n, proto_debug_end_, __builtin_FUNCTION()); \
230 } while (0)
231void proto_check_bounds_failed(const uint8_t *pos, size_t bytes, const uint8_t *end, const char *caller);
234void proto_check_encode_end(const uint8_t *end, const uint8_t *expected);
236void proto_check_sub_message_size(uint32_t field_id, uint32_t expected, const uint8_t *len_pos, const uint8_t *end);
237#else
238#define PROTO_ENCODE_DEBUG_PARAM
239#define PROTO_ENCODE_DEBUG_ARG
240#define PROTO_ENCODE_DEBUG_INIT(buf)
241#define PROTO_ENCODE_CHECK_BOUNDS(pos, n)
242#endif
243
245 public:
247 ProtoWriteBuffer(APIBuffer *buffer, size_t write_pos) : buffer_(buffer), pos_(buffer->data() + write_pos) {}
248 inline void ESPHOME_ALWAYS_INLINE encode_varint_raw(uint32_t value) {
249 if (value < 128) [[likely]] {
250 this->debug_check_bounds_(1);
251 *this->pos_++ = static_cast<uint8_t>(value);
252 return;
253 }
254 this->encode_varint_raw_slow_(value);
255 }
269 APIBuffer *get_buffer() const { return buffer_; }
270 uint8_t *get_pos() const { return pos_; }
271 void set_pos(uint8_t *pos) { pos_ = pos; }
272
273 protected:
274 // Slow path for encode_varint_raw values >= 128, outlined to keep fast path small
275 void encode_varint_raw_slow_(uint32_t value) __attribute__((noinline));
276
277#ifdef ESPHOME_DEBUG_API
278 void debug_check_bounds_(size_t bytes, const char *caller = __builtin_FUNCTION());
279#else
280 void debug_check_bounds_([[maybe_unused]] size_t bytes) {}
281#endif
282
284 uint8_t *pos_;
285};
286
287// A four byte unaligned store is a memcpy call on ESP-IDF (-fno-builtin-memcpy) and on ARM cores without
288// unaligned access (Cortex-M0+, ARM9), so those targets share one outlined byte store helper per fixed32
289// field. Elsewhere the write inlines to a single store, or on ESP8266 to a few stores that measured
290// faster than a call, so it stays inline.
291#if defined(USE_ESP32) || (defined(__arm__) && !defined(__ARM_FEATURE_UNALIGNED))
292#define PROTO_OUTLINE_FOR_SIZE __attribute__((noinline))
293#define PROTO_FIXED32_BYTE_STORES true
294#else
295#define PROTO_OUTLINE_FOR_SIZE inline
296#define PROTO_FIXED32_BYTE_STORES false
297#endif
298
299// Varint encoding thresholds — used by both proto_encode_* free functions and ProtoSize.
300constexpr uint32_t VARINT_MAX_1_BYTE = 1 << 7; // 128
301constexpr uint32_t VARINT_MAX_2_BYTE = 1 << 14; // 16384
302
304using ProtoEncodeFn = uint8_t *(*)(const void *, uint8_t *PROTO_ENCODE_DEBUG_PARAM);
305
310 public:
312 template<typename T>
313 [[nodiscard]] static inline uint8_t *encode_varint_raw_loop(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
314 T value) {
315 do {
316 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
317 *pos++ = static_cast<uint8_t>(value | 0x80);
318 value >>= 7;
319 } while (value > 0x7F);
320 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
321 *pos++ = static_cast<uint8_t>(value);
322 return pos;
323 }
324 [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE
325 encode_varint_raw(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t value) {
326 if (value < VARINT_MAX_1_BYTE) [[likely]] {
327 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
328 *pos++ = static_cast<uint8_t>(value);
329 return pos;
330 }
331 return encode_varint_raw_loop(pos PROTO_ENCODE_DEBUG_ARG, value);
332 }
334 [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE
335 encode_varint_raw_short(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t value) {
336 if (value < VARINT_MAX_1_BYTE) [[likely]] {
337 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
338 *pos++ = static_cast<uint8_t>(value);
339 return pos;
340 }
341 if (value < VARINT_MAX_2_BYTE) [[likely]] {
342 PROTO_ENCODE_CHECK_BOUNDS(pos, 2);
343 *pos++ = static_cast<uint8_t>(value | 0x80);
344 *pos++ = static_cast<uint8_t>(value >> 7);
345 return pos;
346 }
347 return encode_varint_raw_loop(pos PROTO_ENCODE_DEBUG_ARG, value);
348 }
349 [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE
350 encode_varint_raw_64(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint64_t value) {
351 if (value < VARINT_MAX_1_BYTE) [[likely]] {
352 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
353 *pos++ = static_cast<uint8_t>(value);
354 return pos;
355 }
356 return encode_varint_raw_loop(pos PROTO_ENCODE_DEBUG_ARG, value);
357 }
363 [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE
364 encode_varint_raw_48bit(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint64_t value) {
365#ifdef ESPHOME_DEBUG_API
366 assert(value < (1ULL << (MAC_ADDRESS_SIZE * 8)) && "encode_varint_raw_48bit: value exceeds 48 bits");
367#endif
368 // 7-byte varint holds 49 bits (7 * 7), so a 48-bit value needs all 7 bytes
369 // whenever bit 42 or higher is set (i.e. value >= 1 << (48 - 6)).
370 if (value >= (1ULL << (MAC_ADDRESS_SIZE * 8 - 6))) [[likely]] {
371 PROTO_ENCODE_CHECK_BOUNDS(pos, 7);
372 pos[0] = static_cast<uint8_t>(value | 0x80);
373 pos[1] = static_cast<uint8_t>((value >> 7) | 0x80);
374 pos[2] = static_cast<uint8_t>((value >> 14) | 0x80);
375 pos[3] = static_cast<uint8_t>((value >> 21) | 0x80);
376 pos[4] = static_cast<uint8_t>((value >> 28) | 0x80);
377 pos[5] = static_cast<uint8_t>((value >> 35) | 0x80);
378 pos[6] = static_cast<uint8_t>(value >> 42);
379 return pos + 7;
380 }
381 return encode_varint_raw_64(pos PROTO_ENCODE_DEBUG_ARG, value);
382 }
383 [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE
384 encode_field_raw(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint32_t type) {
385 return encode_varint_raw(pos PROTO_ENCODE_DEBUG_ARG, proto_tag(field_id, type));
386 }
388 [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE
389 write_raw_byte(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint8_t b) {
390 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
391 *pos++ = b;
392 return pos;
393 }
396 [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE
397 reserve_byte(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
398 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
399 return pos + 1;
400 }
402 [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE
403 encode_raw(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, const void *data, size_t len) {
404 PROTO_ENCODE_CHECK_BOUNDS(pos, len);
405 std::memcpy(pos, data, len);
406 return pos + len;
407 }
410 [[nodiscard]] static inline uint8_t *encode_short_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
411 uint8_t tag, const StringRef &ref) {
412#ifdef USE_ESP8266
413 // memcpy_P reads RAM too, and sharing the progmem copy is smaller than an inline memcpy
414 return encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, tag, ProgmemStringRef(ref));
415#else
416#ifdef ESPHOME_DEBUG_API
417 assert(ref.size() < 128 && "encode_short_string_force: string exceeds max_data_length < 128");
418#endif
419 PROTO_ENCODE_CHECK_BOUNDS(pos, 2 + ref.size());
420 pos[0] = tag;
421 pos[1] = static_cast<uint8_t>(ref.size());
422 std::memcpy(pos + 2, ref.c_str(), ref.size());
423 return pos + 2 + ref.size();
424#endif
425 }
427 [[nodiscard]] static inline uint8_t *encode_short_progmem_string_force(
428 uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint8_t tag, const ProgmemStringRef &ref) {
429#ifdef ESPHOME_DEBUG_API
430 assert(ref.size() < 128 && "encode_short_progmem_string_force: string exceeds max_data_length < 128");
431#endif
432 PROTO_ENCODE_CHECK_BOUNDS(pos, 2 + ref.size());
433 pos[0] = tag;
434 pos[1] = static_cast<uint8_t>(ref.size());
435 progmem_memcpy(pos + 2, ref.progmem_ptr(), ref.size());
436 return pos + 2 + ref.size();
437 }
439 [[nodiscard]] static PROTO_OUTLINE_FOR_SIZE uint8_t *write_tag_and_fixed32(
440 uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint8_t tag, uint32_t value) {
441 PROTO_ENCODE_CHECK_BOUNDS(pos, 5);
442 pos[0] = tag;
443 write_fixed32_le(pos + 1, value);
444 return pos + 5;
445 }
446 [[nodiscard]] static inline uint8_t *encode_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
447 uint32_t field_id, const char *string, size_t len) {
448 pos = encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 2); // type 2: Length-delimited string
449 // NOLINTNEXTLINE(readability-inconsistent-ifelse-braces) -- false positive on [[likely]] attribute
450 if (len < VARINT_MAX_1_BYTE) [[likely]] {
451 PROTO_ENCODE_CHECK_BOUNDS(pos, 1 + len);
452 *pos++ = static_cast<uint8_t>(len);
453 } else {
454 pos = encode_varint_raw_loop(pos PROTO_ENCODE_DEBUG_ARG, len);
455 PROTO_ENCODE_CHECK_BOUNDS(pos, len);
456 }
457 std::memcpy(pos, string, len);
458 return pos + len;
459 }
460 [[nodiscard]] static inline uint8_t *encode_string(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
461 uint32_t field_id, const char *string, size_t len) {
462 if (len == 0)
463 return pos;
464 return encode_string_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, string, len);
465 }
466 [[nodiscard]] static inline uint8_t *encode_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
467 uint32_t field_id, const std::string &value) {
468 return encode_string_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, value.data(), value.size());
469 }
470 [[nodiscard]] static inline uint8_t *encode_string(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
471 uint32_t field_id, const StringRef &ref) {
472 return encode_string(pos PROTO_ENCODE_DEBUG_ARG, field_id, ref.c_str(), ref.size());
473 }
474 [[nodiscard]] static inline uint8_t *encode_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
475 uint32_t field_id, const StringRef &ref) {
476 return encode_string_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, ref.c_str(), ref.size());
477 }
478 [[nodiscard]] static inline uint8_t *encode_bytes(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
479 uint32_t field_id, const uint8_t *data, size_t len) {
480 return encode_string(pos PROTO_ENCODE_DEBUG_ARG, field_id, reinterpret_cast<const char *>(data), len);
481 }
482 [[nodiscard]] static inline uint8_t *encode_bytes_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
483 uint32_t field_id, const uint8_t *data, size_t len) {
484 return encode_string_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, reinterpret_cast<const char *>(data), len);
485 }
487 [[nodiscard]] static inline uint8_t *encode_progmem_bytes(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
488 uint32_t field_id, const uint8_t *data, size_t len) {
489 if (len == 0)
490 return pos;
491 return encode_progmem_bytes_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, data, len);
492 }
493 [[nodiscard]] static inline uint8_t *encode_progmem_bytes_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
494 uint32_t field_id, const uint8_t *data, size_t len) {
495 pos = encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 2); // type 2: Length-delimited
496 pos = encode_varint_raw(pos PROTO_ENCODE_DEBUG_ARG, len);
497 PROTO_ENCODE_CHECK_BOUNDS(pos, len);
498 progmem_memcpy(pos, data, len);
499 return pos + len;
500 }
502 [[nodiscard]] static inline uint8_t *encode_progmem_string(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
503 uint32_t field_id, const ProgmemStringRef &ref) {
504#ifdef USE_ESP8266
505 return encode_progmem_bytes(pos PROTO_ENCODE_DEBUG_ARG, field_id,
506 reinterpret_cast<const uint8_t *>(ref.progmem_ptr()), ref.size());
507#else
508 // In RAM here, so share the plain string encoder
509 return encode_string(pos PROTO_ENCODE_DEBUG_ARG, field_id, ref.ram_ref());
510#endif
511 }
512 [[nodiscard]] static inline uint8_t *encode_progmem_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
513 uint32_t field_id, const ProgmemStringRef &ref) {
514#ifdef USE_ESP8266
515 return encode_progmem_bytes_force(pos PROTO_ENCODE_DEBUG_ARG, field_id,
516 reinterpret_cast<const uint8_t *>(ref.progmem_ptr()), ref.size());
517#else
518 return encode_string_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, ref.ram_ref());
519#endif
520 }
521 [[nodiscard]] static inline uint8_t *encode_uint32_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
522 uint32_t field_id, uint32_t value) {
523 pos = encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 0);
524 return encode_varint_raw(pos PROTO_ENCODE_DEBUG_ARG, value);
525 }
526 [[nodiscard]] static inline uint8_t *encode_uint32(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
527 uint32_t field_id, uint32_t value) {
528 if (value == 0)
529 return pos;
530 return encode_uint32_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, value);
531 }
532 [[nodiscard]] static inline uint8_t *encode_uint64_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
533 uint32_t field_id, uint64_t value) {
534 pos = encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 0);
535 return encode_varint_raw_64(pos PROTO_ENCODE_DEBUG_ARG, value);
536 }
537 [[nodiscard]] static inline uint8_t *encode_uint64(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
538 uint32_t field_id, uint64_t value) {
539 if (value == 0)
540 return pos;
541 return encode_uint64_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, value);
542 }
543 [[nodiscard]] static inline uint8_t *encode_bool_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
544 uint32_t field_id, bool value) {
545 pos = encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 0);
546 PROTO_ENCODE_CHECK_BOUNDS(pos, 1);
547 *pos++ = value ? 0x01 : 0x00;
548 return pos;
549 }
550 [[nodiscard]] static inline uint8_t *encode_bool(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
551 uint32_t field_id, bool value) {
552 if (!value)
553 return pos;
554 return encode_bool_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, value);
555 }
557 [[nodiscard]] static PROTO_OUTLINE_FOR_SIZE uint8_t *encode_fixed32_force(
558 uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint32_t value) {
559 pos = encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 5);
560 PROTO_ENCODE_CHECK_BOUNDS(pos, 4);
561 write_fixed32_le(pos, value);
562 return pos + 4;
563 }
564 [[nodiscard]] static inline uint8_t *encode_fixed32(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
565 uint32_t field_id, uint32_t value) {
566 if (value == 0)
567 return pos;
568 return encode_fixed32_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, value);
569 }
570 // NOTE: Wire type 1 (64-bit fixed: double, fixed64, sfixed64) is intentionally
571 // not supported to reduce overhead on embedded systems. All ESPHome devices are
572 // 32-bit microcontrollers where 64-bit operations are expensive. If 64-bit support
573 // is needed in the future, the necessary encoding/decoding functions must be added.
574 [[nodiscard]] static inline uint8_t *encode_float(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
575 uint32_t field_id, float value) {
576 return encode_fixed32(pos PROTO_ENCODE_DEBUG_ARG, field_id, float_to_raw(value));
577 }
578 [[nodiscard]] static inline uint8_t *encode_float_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
579 uint32_t field_id, float value) {
580 return encode_fixed32_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, float_to_raw(value));
581 }
582 [[nodiscard]] static inline uint8_t *encode_int32_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
583 uint32_t field_id, int32_t value) {
584 if (value < 0) {
585 // negative int32 is always 10 byte long
586 return encode_uint64_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, static_cast<uint64_t>(value));
587 }
588 return encode_uint32_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, static_cast<uint32_t>(value));
589 }
590 [[nodiscard]] static inline uint8_t *encode_int32(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
591 uint32_t field_id, int32_t value) {
592 if (value == 0)
593 return pos;
594 return encode_int32_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, value);
595 }
596 [[nodiscard]] static inline uint8_t *encode_int64(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
597 uint32_t field_id, int64_t value) {
598 return encode_uint64(pos PROTO_ENCODE_DEBUG_ARG, field_id, static_cast<uint64_t>(value));
599 }
600 [[nodiscard]] static inline uint8_t *encode_int64_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
601 uint32_t field_id, int64_t value) {
602 return encode_uint64_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, static_cast<uint64_t>(value));
603 }
604 [[nodiscard]] static inline uint8_t *encode_sint32(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
605 uint32_t field_id, int32_t value) {
606 return encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, field_id, encode_zigzag32(value));
607 }
608 [[nodiscard]] static inline uint8_t *encode_sint32_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
609 uint32_t field_id, int32_t value) {
610 return encode_uint32_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, encode_zigzag32(value));
611 }
612 [[nodiscard]] static inline uint8_t *encode_sint64(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
613 uint32_t field_id, int64_t value) {
614 return encode_uint64(pos PROTO_ENCODE_DEBUG_ARG, field_id, encode_zigzag64(value));
615 }
616 [[nodiscard]] static inline uint8_t *encode_sint64_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
617 uint32_t field_id, int64_t value) {
618 return encode_uint64_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, encode_zigzag64(value));
619 }
621 template<typename T>
622 [[nodiscard]] static inline uint8_t *encode_sub_message(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
623 uint32_t field_id, const T &value) {
624 pos = encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 2);
625 return encode_sub_message_body(pos PROTO_ENCODE_DEBUG_ARG, &value, &T::encode_msg);
626 }
628 template<typename T>
629 [[nodiscard]] static inline uint8_t *encode_optional_sub_message(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
630 uint32_t field_id, const T &value) {
631 uint32_t nested_size = T::calc_size_msg(&value);
632 if (nested_size == 0)
633 return pos;
634 pos = encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 2);
635#ifdef ESPHOME_DEBUG_API
636 uint8_t *end = encode_sized_sub_message_body(pos PROTO_ENCODE_DEBUG_ARG, nested_size, &value, &T::encode_msg);
637 proto_check_sub_message_size(field_id, nested_size, pos, end);
638 return end;
639#else
640 return encode_sized_sub_message_body(pos, nested_size, &value, &T::encode_msg);
641#endif
642 }
644 [[nodiscard]] static uint8_t *encode_sub_message_body(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
645 const void *value, ProtoEncodeFn encode_fn);
647 [[nodiscard]] static uint8_t *encode_sized_sub_message_body(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
648 uint32_t nested_size, const void *value,
649 ProtoEncodeFn encode_fn);
650
651 private:
655 static inline void ESPHOME_ALWAYS_INLINE write_fixed32_le(uint8_t *__restrict__ pos, uint32_t value) {
656 if constexpr (PROTO_FIXED32_BYTE_STORES) {
657 // Spelled out so the outlined helper does not itself become a memcpy call
658 pos[0] = static_cast<uint8_t>(value);
659 pos[1] = static_cast<uint8_t>(value >> 8);
660 pos[2] = static_cast<uint8_t>(value >> 16);
661 pos[3] = static_cast<uint8_t>(value >> 24);
662 } else {
663 const uint32_t le = convert_little_endian(value);
664 __builtin_memcpy(pos, &le, 4);
665 }
666 }
667};
668#undef PROTO_OUTLINE_FOR_SIZE
669#undef PROTO_FIXED32_BYTE_STORES
670
671#ifdef HAS_PROTO_MESSAGE_DUMP
678 public:
679 // Matches default tx_buffer_size in logger component
680 static constexpr size_t CAPACITY = 512;
681
682 DumpBuffer() : pos_(0) { buf_[0] = '\0'; }
683
684 DumpBuffer &append(const char *str) {
685 if (str) {
686 append_impl_(str, strlen(str));
687 }
688 return *this;
689 }
690
691 DumpBuffer &append(const char *str, size_t len) {
692 append_impl_(str, len);
693 return *this;
694 }
695
696 DumpBuffer &append(size_t n, char c) {
697 size_t space = CAPACITY - 1 - pos_;
698 if (n > space)
699 n = space;
700 if (n > 0) {
701 memset(buf_ + pos_, c, n);
702 pos_ += n;
703 buf_[pos_] = '\0';
704 }
705 return *this;
706 }
707
709 DumpBuffer &append_p(const char *str) {
710 if (str) {
711#ifdef USE_ESP8266
712 append_p_esp8266(str);
713#else
714 append_impl_(str, strlen(str));
715#endif
716 }
717 return *this;
718 }
719
721 DumpBuffer &append_p(const char *str, size_t len) {
722#ifdef USE_ESP8266
723 append_p_esp8266(str, len);
724#else
725 append_impl_(str, len);
726#endif
727 return *this;
728 }
729
730#ifdef USE_ESP8266
732 void append_p_esp8266(const char *str);
733 void append_p_esp8266(const char *str, size_t len);
734#endif
735
736 const char *c_str() const { return buf_; }
737 size_t size() const { return pos_; }
738
740 char *data() { return buf_; }
742 size_t pos() const { return pos_; }
744 void set_pos(size_t pos) {
745 if (pos >= CAPACITY) {
746 pos_ = CAPACITY - 1;
747 } else {
748 pos_ = pos;
749 }
750 buf_[pos_] = '\0';
751 }
752
753 private:
754 void append_impl_(const char *str, size_t len) {
755 size_t space = CAPACITY - 1 - pos_;
756 if (len > space)
757 len = space;
758 if (len > 0) {
759 memcpy(buf_ + pos_, str, len);
760 pos_ += len;
761 buf_[pos_] = '\0';
762 }
763 }
764
765 char buf_[CAPACITY];
766 size_t pos_;
767};
768#endif
769
771 public:
772 // Non-virtual defaults for messages with no fields; generated classes hide all four. The
773 // static encode_msg/calc_size_msg take const void * so &T::encode_msg needs no thunk.
774 static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM) { return pos; }
775 static uint32_t calc_size_msg(const void *self) { return 0; }
776 uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const { return buffer.get_pos(); }
777 uint32_t calculate_size() const { return 0; }
778#ifdef HAS_PROTO_MESSAGE_DUMP
779 virtual const char *dump_to(DumpBuffer &out) const = 0;
780 virtual const LogString *message_name() const { return LOG_STR("unknown"); }
781#endif
782
783#ifndef USE_HOST
784 protected:
785#endif
786 // Non-virtual destructor is protected to prevent polymorphic deletion.
787 // On host platform, made public to allow value-initialization of std::array
788 // members (e.g. DeviceInfoResponse::devices) without clang errors.
789 ~ProtoMessage() = default;
790};
791
792// Base class for messages that support decoding
794 public:
798 using DecodeFieldFn = void (*)(void *msg, uint32_t tag, const uint8_t *data, proto_varint_value_t scalar);
801 static void decode_fields(void *msg, const uint8_t *buffer, size_t length, DecodeFieldFn field);
802
812 static uint32_t count_repeated_field(const uint8_t *buffer, size_t length, uint32_t target_field_id);
813 // The destructor stays accessible on purpose: the generated messages are aggregates that brace
814 // initialise sub message members, which copies a base temporary. That trades away the compile time
815 // guard against deleting through this type; messages are stack locals and never owned through a base
816 // pointer. ProtoMessage keeps its guard for the dump builds.
817};
818#ifndef HAS_PROTO_MESSAGE_DUMP
819// decode() passes decode_field explicitly, so nothing here may add a vtable
820static_assert(!std::is_polymorphic_v<ProtoDecodableMessage>, "decodable messages carry no vtable");
821#endif
822
824 public:
825 // Varint encoding thresholds — use namespace-level constants for 1/2 byte,
826 // class-level for 3/4 byte (only used within ProtoSize).
829 static constexpr uint32_t VARINT_THRESHOLD_3_BYTE = 1 << 21; // 2097152
830 static constexpr uint32_t VARINT_THRESHOLD_4_BYTE = 1 << 28; // 268435456
831
832 // Varint encoded length for a 16-bit value (1, 2, or 3 bytes).
833 // Fully inline — no slow path call for values >= 128.
834 static constexpr inline uint8_t ESPHOME_ALWAYS_INLINE varint16(uint16_t value) {
835 return value < VARINT_THRESHOLD_1_BYTE ? 1 : (value < VARINT_THRESHOLD_2_BYTE ? 2 : 3);
836 }
837
844 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE varint(uint32_t value) {
845 if (value < VARINT_THRESHOLD_1_BYTE) [[likely]]
846 return 1; // Fast path: 7 bits, most common case
847 if (__builtin_is_constant_evaluated())
848 return varint_wide(value);
849 return varint_slow(value);
850 }
853 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE varint_short(uint32_t value) {
854 if (value < VARINT_THRESHOLD_1_BYTE) [[likely]]
855 return 1;
856 if (value < VARINT_THRESHOLD_2_BYTE) [[likely]]
857 return 2;
858 if (__builtin_is_constant_evaluated())
859 return varint_wide(value);
860 return varint_slow(value);
861 }
862
863 private:
864 // Slow path for varint >= 128, outlined to keep fast path small
865 static uint32_t varint_slow(uint32_t value) __attribute__((noinline));
866 // Shared cascade for values >= 128 (used by both constexpr and noinline paths)
867 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE varint_wide(uint32_t value) {
868 if (value < VARINT_THRESHOLD_2_BYTE)
869 return 2;
870 if (value < VARINT_THRESHOLD_3_BYTE)
871 return 3;
872 if (value < VARINT_THRESHOLD_4_BYTE)
873 return 4;
874 return 5;
875 }
876
877 public:
884 static constexpr uint32_t varint(uint64_t value) {
885 // Handle common case of values fitting in uint32_t (vast majority of use cases)
886 if (value <= UINT32_MAX) {
887 return varint(static_cast<uint32_t>(value));
888 }
889
890 // For larger values, determine size based on highest bit position
891 if (value < (1ULL << 35)) {
892 return 5; // 35 bits
893 } else if (value < (1ULL << 42)) {
894 return 6; // 42 bits
895 } else if (value < (1ULL << 49)) {
896 return 7; // 49 bits
897 } else if (value < (1ULL << 56)) {
898 return 8; // 56 bits
899 } else if (value < (1ULL << 63)) {
900 return 9; // 63 bits
901 } else {
902 return 10; // 64 bits (maximum for uint64_t)
903 }
904 }
905
915 static constexpr uint32_t varint(int32_t value) {
916 // Negative values are sign-extended to 64 bits in protocol buffers,
917 // which always results in a 10-byte varint for negative int32
918 if (value < 0) {
919 return 10; // Negative int32 is always 10 bytes long
920 }
921 // For non-negative values, use the uint32_t implementation
922 return varint(static_cast<uint32_t>(value));
923 }
924
931 static constexpr uint32_t varint(int64_t value) {
932 // For int64_t, we convert to uint64_t and calculate the size
933 // This works because the bit pattern determines the encoding size,
934 // and we've handled negative int32 values as a special case above
935 return varint(static_cast<uint64_t>(value));
936 }
937
945 static constexpr uint32_t field(uint32_t field_id, uint32_t type) {
946 uint32_t tag = proto_tag(field_id, type & WIRE_TYPE_MASK);
947 return varint(tag);
948 }
949
950 // Static methods that RETURN size contribution (no ProtoSize object needed).
951 // Used by generated calculate_size() methods to accumulate into a plain uint32_t register.
952 static constexpr uint32_t calc_int32(uint32_t field_id_size, int32_t value) {
953 return value ? field_id_size + (value < 0 ? 10 : varint(static_cast<uint32_t>(value))) : 0;
954 }
955 static constexpr uint32_t calc_int32_force(uint32_t field_id_size, int32_t value) {
956 return field_id_size + (value < 0 ? 10 : varint(static_cast<uint32_t>(value)));
957 }
958 static constexpr uint32_t calc_uint32(uint32_t field_id_size, uint32_t value) {
959 return value ? field_id_size + varint(value) : 0;
960 }
961 static constexpr uint32_t calc_uint32_force(uint32_t field_id_size, uint32_t value) {
962 return field_id_size + varint(value);
963 }
964 static constexpr uint32_t calc_bool(uint32_t field_id_size, bool value) { return value ? field_id_size + 1 : 0; }
965 static constexpr uint32_t calc_bool_force(uint32_t field_id_size) { return field_id_size + 1; }
966 static uint32_t calc_float(uint32_t field_id_size, float value) {
967 return float_to_raw(value) != 0 ? field_id_size + 4 : 0;
968 }
969 static constexpr uint32_t calc_fixed32(uint32_t field_id_size, uint32_t value) {
970 return value ? field_id_size + 4 : 0;
971 }
972 static constexpr uint32_t calc_sfixed32(uint32_t field_id_size, int32_t value) {
973 return value ? field_id_size + 4 : 0;
974 }
975 static constexpr uint32_t calc_sint32(uint32_t field_id_size, int32_t value) {
976 return value ? field_id_size + varint_short(encode_zigzag32(value)) : 0;
977 }
978 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE calc_sint32_force(uint32_t field_id_size, int32_t value) {
979 return field_id_size + varint_short(encode_zigzag32(value));
980 }
981 static constexpr uint32_t calc_int64(uint32_t field_id_size, int64_t value) {
982 return value ? field_id_size + varint(value) : 0;
983 }
984 static constexpr uint32_t calc_int64_force(uint32_t field_id_size, int64_t value) {
985 return field_id_size + varint(value);
986 }
987 static constexpr uint32_t calc_uint64(uint32_t field_id_size, uint64_t value) {
988 return value ? field_id_size + varint(value) : 0;
989 }
990 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE calc_uint64_force(uint32_t field_id_size, uint64_t value) {
991 return field_id_size + varint(value);
992 }
997 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE calc_uint64_48bit_force(uint32_t field_id_size,
998 uint64_t value) {
999 return field_id_size + (value >= (1ULL << (MAC_ADDRESS_SIZE * 8 - 6)) ? 7 : varint(value));
1000 }
1001 static constexpr uint32_t calc_length(uint32_t field_id_size, size_t len) {
1002 return len ? field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len) : 0;
1003 }
1004 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE calc_length_force(uint32_t field_id_size, size_t len) {
1005 return field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len);
1006 }
1007 static constexpr uint32_t calc_sint64(uint32_t field_id_size, int64_t value) {
1008 return value ? field_id_size + varint(encode_zigzag64(value)) : 0;
1009 }
1010 static constexpr uint32_t calc_sint64_force(uint32_t field_id_size, int64_t value) {
1011 return field_id_size + varint(encode_zigzag64(value));
1012 }
1013 static constexpr uint32_t calc_fixed64(uint32_t field_id_size, uint64_t value) {
1014 return value ? field_id_size + 8 : 0;
1015 }
1016 static constexpr uint32_t calc_sfixed64(uint32_t field_id_size, int64_t value) {
1017 return value ? field_id_size + 8 : 0;
1018 }
1019 static constexpr uint32_t calc_message(uint32_t field_id_size, uint32_t nested_size) {
1020 return nested_size ? field_id_size + varint(nested_size) + nested_size : 0;
1021 }
1022 static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE calc_message_force(uint32_t field_id_size,
1023 uint32_t nested_size) {
1024 return field_id_size + varint(nested_size) + nested_size;
1025 }
1026};
1027
1028template<typename T> const char *proto_enum_to_string(T value);
1029
1030// ProtoService removed — its methods were inlined into APIConnection.
1031// APIConnection is the concrete server-side implementation; the extra virtual layer was unnecessary.
1032
1033} // namespace esphome::api
uint8_t raw[35]
Definition bl0939.h:0
A string that may live in flash (on ESP8266): pointer and length, with no byte-reading operations.
Definition string_ref.h:230
StringRef is a reference to a string owned by something else.
Definition string_ref.h:31
Byte buffer that skips zero-initialization on resize().
Definition api_buffer.h:26
Fixed-size buffer for message dumps - avoids heap allocation.
Definition proto.h:677
const char * c_str() const
Definition proto.h:736
DumpBuffer & append(size_t n, char c)
Definition proto.h:696
size_t size() const
Definition proto.h:737
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:709
size_t pos() const
Get current position for use with buf_append_printf.
Definition proto.h:742
DumpBuffer & append_p(const char *str, size_t len)
Append len bytes of a PROGMEM string.
Definition proto.h:721
static constexpr size_t CAPACITY
Definition proto.h:680
DumpBuffer & append(const char *str, size_t len)
Definition proto.h:691
DumpBuffer & append(const char *str)
Definition proto.h:684
char * data()
Get writable buffer pointer for use with buf_append_printf.
Definition proto.h:740
void set_pos(size_t pos)
Update position after buf_append_printf call.
Definition proto.h:744
static void decode_fields(void *msg, const uint8_t *buffer, size_t length, DecodeFieldFn field)
Walk buffer and hand every field to field.
Definition proto.cpp:205
void(*)(void *msg, uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) DecodeFieldFn
Stores one decoded field into msg; generated per message type.
Definition proto.h:798
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 the generated encode bodies.
Definition proto.h:309
static uint8_t *ESPHOME_ALWAYS_INLINE encode_field_raw(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint32_t type)
Definition proto.h:384
static uint8_t * 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:410
static uint8_t * encode_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const StringRef &ref)
Definition proto.h:474
static uint8_t * encode_int64_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, int64_t value)
Definition proto.h:600
static uint8_t *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:364
static uint8_t * encode_progmem_bytes(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const uint8_t *data, size_t len)
encode_bytes for data in flash (PROGMEM), which ESP8266 can only read with progmem_memcpy.
Definition proto.h:487
static uint8_t * encode_int32_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, int32_t value)
Definition proto.h:582
static uint8_t * encode_string(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const StringRef &ref)
Definition proto.h:470
static uint8_t * encode_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const std::string &value)
Definition proto.h:466
static uint8_t * encode_sint64_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, int64_t value)
Definition proto.h:616
static uint8_t * encode_sub_message(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const T &value)
Repeated sub-message element; the constant tag is written inline.
Definition proto.h:622
static uint8_t * encode_uint32_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint32_t value)
Definition proto.h:521
static uint8_t * encode_int32(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, int32_t value)
Definition proto.h:590
static uint8_t *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:389
static uint8_t *ESPHOME_ALWAYS_INLINE encode_varint_raw(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t value)
Definition proto.h:325
static PROTO_OUTLINE_FOR_SIZE uint8_t * encode_fixed32_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint32_t value)
Tag + fixed32 for multi-byte tags; single-byte tags use write_tag_and_fixed32.
Definition proto.h:557
static uint8_t * encode_sint64(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, int64_t value)
Definition proto.h:612
static uint8_t * encode_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const char *string, size_t len)
Definition proto.h:446
static uint8_t * encode_string(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const char *string, size_t len)
Definition proto.h:460
static uint8_t * encode_uint32(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint32_t value)
Definition proto.h:526
static uint8_t * encode_sint32(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, int32_t value)
Definition proto.h:604
static uint8_t * encode_fixed32(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint32_t value)
Definition proto.h:564
static uint8_t * encode_sized_sub_message_body(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t nested_size, const void *value, ProtoEncodeFn encode_fn)
Length and body for a precomputed size.
Definition proto.cpp:170
static uint8_t * encode_optional_sub_message(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const T &value)
Singular sub-message field, skipped when it encodes to nothing.
Definition proto.h:629
static uint8_t *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:397
static uint8_t * 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:313
static uint8_t * encode_sint32_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, int32_t value)
Definition proto.h:608
static uint8_t * encode_progmem_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const ProgmemStringRef &ref)
Definition proto.h:512
static uint8_t * encode_short_progmem_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint8_t tag, const ProgmemStringRef &ref)
encode_short_string_force for a string that may be in flash (PROGMEM).
Definition proto.h:427
static uint8_t * encode_progmem_bytes_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const uint8_t *data, size_t len)
Definition proto.h:493
static PROTO_OUTLINE_FOR_SIZE uint8_t * 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. Outlined on embedded: one copy beats inline stores per f...
Definition proto.h:439
static uint8_t * encode_float_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, float value)
Definition proto.h:578
static uint8_t * encode_bool(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, bool value)
Definition proto.h:550
static uint8_t * encode_progmem_string(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const ProgmemStringRef &ref)
encode_progmem_bytes for a string field that may be in flash.
Definition proto.h:502
static uint8_t * encode_bytes(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const uint8_t *data, size_t len)
Definition proto.h:478
static uint8_t *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:403
static uint8_t *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:335
static uint8_t *ESPHOME_ALWAYS_INLINE encode_varint_raw_64(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint64_t value)
Definition proto.h:350
static uint8_t * encode_uint64(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint64_t value)
Definition proto.h:537
static uint8_t * encode_uint64_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint64_t value)
Definition proto.h:532
static uint8_t * encode_int64(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, int64_t value)
Definition proto.h:596
static uint8_t * encode_float(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, float value)
Definition proto.h:574
static uint8_t * encode_sub_message_body(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, const void *value, ProtoEncodeFn encode_fn)
Length and body, length backpatched after the body is written.
Definition proto.cpp:147
static uint8_t * encode_bytes_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, const uint8_t *data, size_t len)
Definition proto.h:482
static uint8_t * encode_bool_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, bool value)
Definition proto.h:543
One decoded field: the payload pointer and a scalar holding the varint or fixed32 value,...
Definition proto.h:184
void decode_to_message(T &msg) const
Decode the length-delimited payload into a message instance.
Definition proto.h:198
std::string as_string() const
Definition proto.h:195
uint32_t as_fixed32() const
Definition proto.h:201
const uint8_t * data() const
Definition proto.h:193
ProtoFieldValue(const uint8_t *data, proto_varint_value_t scalar)
Definition proto.h:186
proto_varint_value_t as_varint() const
Definition proto.h:188
int32_t as_sfixed32() const
Definition proto.h:202
virtual const LogString * message_name() const
Definition proto.h:780
uint8_t * encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const
Definition proto.h:776
uint32_t calculate_size() const
Definition proto.h:777
static uint32_t calc_size_msg(const void *self)
Definition proto.h:775
static uint8_t * encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM)
Definition proto.h:774
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:987
static constexpr uint32_t calc_int64(uint32_t field_id_size, int64_t value)
Definition proto.h:981
static constexpr uint32_t calc_bool(uint32_t field_id_size, bool value)
Definition proto.h:964
static constexpr uint32_t calc_message(uint32_t field_id_size, uint32_t nested_size)
Definition proto.h:1019
static constexpr uint32_t calc_sint32(uint32_t field_id_size, int32_t value)
Definition proto.h:975
static constexpr uint32_t calc_sint64_force(uint32_t field_id_size, int64_t value)
Definition proto.h:1010
static constexpr uint32_t ESPHOME_ALWAYS_INLINE calc_uint64_force(uint32_t field_id_size, uint64_t value)
Definition proto.h:990
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:945
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:844
static constexpr uint32_t calc_uint32_force(uint32_t field_id_size, uint32_t value)
Definition proto.h:961
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:915
static constexpr uint32_t calc_int64_force(uint32_t field_id_size, int64_t value)
Definition proto.h:984
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:997
static constexpr uint32_t calc_sfixed64(uint32_t field_id_size, int64_t value)
Definition proto.h:1016
static constexpr uint32_t calc_uint32(uint32_t field_id_size, uint32_t value)
Definition proto.h:958
static constexpr uint32_t VARINT_THRESHOLD_3_BYTE
Definition proto.h:829
static constexpr uint8_t ESPHOME_ALWAYS_INLINE varint16(uint16_t value)
Definition proto.h:834
static constexpr uint32_t calc_length(uint32_t field_id_size, size_t len)
Definition proto.h:1001
static constexpr uint32_t calc_int32(uint32_t field_id_size, int32_t value)
Definition proto.h:952
static constexpr uint32_t calc_fixed64(uint32_t field_id_size, uint64_t value)
Definition proto.h:1013
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: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:931
static constexpr uint32_t ESPHOME_ALWAYS_INLINE calc_length_force(uint32_t field_id_size, size_t len)
Definition proto.h:1004
static constexpr uint32_t calc_bool_force(uint32_t field_id_size)
Definition proto.h:965
static constexpr uint32_t calc_sfixed32(uint32_t field_id_size, int32_t value)
Definition proto.h:972
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:884
static uint32_t calc_float(uint32_t field_id_size, float value)
Definition proto.h:966
static constexpr uint32_t calc_sint64(uint32_t field_id_size, int64_t value)
Definition proto.h:1007
static constexpr uint32_t calc_int32_force(uint32_t field_id_size, int32_t value)
Definition proto.h:955
static constexpr uint32_t calc_fixed32(uint32_t field_id_size, uint32_t value)
Definition proto.h:969
static constexpr uint32_t VARINT_THRESHOLD_2_BYTE
Definition proto.h:828
static constexpr uint32_t ESPHOME_ALWAYS_INLINE calc_message_force(uint32_t field_id_size, uint32_t nested_size)
Definition proto.h:1022
static constexpr uint32_t ESPHOME_ALWAYS_INLINE calc_sint32_force(uint32_t field_id_size, int32_t value)
Definition proto.h:978
static constexpr uint32_t VARINT_THRESHOLD_1_BYTE
Definition proto.h:827
static constexpr uint32_t VARINT_THRESHOLD_4_BYTE
Definition proto.h:830
Static varint parsing methods for the protobuf wire format.
Definition proto.h:141
static ProtoVarIntResult ESPHOME_ALWAYS_INLINE parse_non_empty(const uint8_t *buffer, uint32_t len)
Parse a varint from buffer.
Definition proto.h:145
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:158
void set_pos(uint8_t *pos)
Definition proto.h:271
void debug_check_bounds_(size_t bytes)
Definition proto.h:280
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:268
void debug_check_bounds_(size_t bytes, const char *caller=__builtin_FUNCTION())
Definition proto.cpp:195
uint8_t * get_pos() const
Definition proto.h:270
APIBuffer * get_buffer() const
Definition proto.h:269
void ESPHOME_ALWAYS_INLINE encode_varint_raw(uint32_t value)
Definition proto.h:248
ProtoWriteBuffer(APIBuffer *buffer, size_t write_pos)
Definition proto.h:247
ProtoWriteBuffer(APIBuffer *buffer)
Definition proto.h:246
struct @66::@67 __attribute__
Wake the main loop task from an ISR. ISR-safe.
Definition main_task.h:45
uint8_t type
StringRef ref
std::vector< uint8_t > & out
Definition gif.cpp:49
uint32_t buffer
Definition gif.cpp:50
mopeka_std_values val[3]
uint16_t count_packed_varints(const uint8_t *data, size_t len)
Count varints in a packed buffer: len minus bytes with the continuation bit, summed a word at a time.
Definition proto.h:62
constexpr uint32_t encode_zigzag32(int32_t value)
Definition proto.h:44
uint8_t *(*)(const void *, uint8_t *PROTO_ENCODE_DEBUG_PARAM) ProtoEncodeFn
Generated encode body: writes the fields at pos, returns the cursor past them.
Definition proto.h:304
constexpr uint32_t VARINT_MAX_2_BYTE
Definition proto.h:301
constexpr uint8_t WIRE_TYPE_VARINT
Definition proto.h:25
constexpr uint32_t proto_tag(uint32_t field_id, uint32_t wire_type)
Case label for decode_field(): the wire tag of a field, so a field that arrives with another wire typ...
Definition proto.h:180
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:123
const char * proto_enum_to_string(T value)
void proto_check_encode_end(const uint8_t *end, const uint8_t *expected)
Aborts unless an encode body ended exactly where calculate_size() promised.
Definition proto.cpp:181
constexpr uint32_t VARINT_MAX_1_BYTE
Definition proto.h:300
constexpr uint64_t encode_zigzag64(int64_t value)
Definition proto.h:48
void proto_check_sub_message_size(uint32_t field_id, uint32_t expected, const uint8_t *len_pos, const uint8_t *end)
Aborts unless a sized sub-message (length prefix at len_pos) ended where its calculated size said.
Definition proto.cpp:187
constexpr uint8_t WIRE_TYPE_MASK
Definition proto.h:28
constexpr uint32_t PROTO_VARINT_PARSE_FAILED
Sentinel value for consumed field indicating parse failure.
Definition proto.h:129
constexpr uint8_t WIRE_TYPE_LENGTH_DELIMITED
Definition proto.h:26
constexpr int32_t decode_zigzag32(uint32_t value)
Definition proto.h:52
constexpr uint8_t WIRE_TYPE_FIXED32
Definition proto.h:27
uint32_t float_to_raw(float value)
Definition proto.h:34
constexpr int64_t decode_zigzag64(uint64_t value)
Definition proto.h:56
void encode_varint_to_buffer(uint32_t val, uint8_t *buffer)
Encode a varint directly into a pre-allocated buffer.
Definition proto.h:83
void proto_check_bounds_failed(const uint8_t *pos, size_t bytes, const uint8_t *end, const char *caller)
Definition proto.cpp:177
void progmem_memcpy(void *dst, const void *src, size_t len)
Definition hal.h:56
constexpr T convert_little_endian(T val)
Convert a value between host byte order and little endian (least significant byte first) order.
Definition helpers.h:1107
const void size_t len
Definition hal.h:68
uint16_t size
Definition helpers.cpp:34
size_t size_t pos
Definition helpers.h:1255
const char * pos_
Definition polygon.cpp:54
static void uint32_t
Result of parsing a varint: value + number of bytes consumed.
Definition proto.h:133
constexpr bool has_value() const
Definition proto.h:137
proto_varint_value_t value
Definition proto.h:134
uint8_t end[39]
Definition sun_gtil2.cpp:17
uint16_t length
Definition tt21100.cpp:0