ESPHome 2025.12.0-dev
Loading...
Searching...
No Matches
proto.cpp
Go to the documentation of this file.
1#include "proto.h"
2#include <cinttypes>
4#include "esphome/core/log.h"
5
6namespace esphome::api {
7
8static const char *const TAG = "api.proto";
9
10uint32_t ProtoDecodableMessage::count_repeated_field(const uint8_t *buffer, size_t length, uint32_t target_field_id) {
11 uint32_t count = 0;
12 const uint8_t *ptr = buffer;
13 const uint8_t *end = buffer + length;
14
15 while (ptr < end) {
16 uint32_t consumed;
17
18 // Parse field header (tag)
19 auto res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
20 if (!res.has_value()) {
21 break; // Invalid data, stop counting
22 }
23
24 uint32_t tag = res->as_uint32();
25 uint32_t field_type = tag & WIRE_TYPE_MASK;
26 uint32_t field_id = tag >> 3;
27 ptr += consumed;
28
29 // Count if this is the target field
30 if (field_id == target_field_id) {
31 count++;
32 }
33
34 // Skip field data based on wire type
35 switch (field_type) {
36 case WIRE_TYPE_VARINT: { // VarInt - parse and skip
37 res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
38 if (!res.has_value()) {
39 return count; // Invalid data, return what we have
40 }
41 ptr += consumed;
42 break;
43 }
44 case WIRE_TYPE_LENGTH_DELIMITED: { // Length-delimited - parse length and skip data
45 res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
46 if (!res.has_value()) {
47 return count;
48 }
49 uint32_t field_length = res->as_uint32();
50 ptr += consumed;
51 if (ptr + field_length > end) {
52 return count; // Out of bounds
53 }
54 ptr += field_length;
55 break;
56 }
57 case WIRE_TYPE_FIXED32: { // 32-bit - skip 4 bytes
58 if (ptr + 4 > end) {
59 return count;
60 }
61 ptr += 4;
62 break;
63 }
64 default:
65 // Unknown wire type, can't continue
66 return count;
67 }
68 }
69
70 return count;
71}
72
73void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
74 const uint8_t *ptr = buffer;
75 const uint8_t *end = buffer + length;
76
77 while (ptr < end) {
78 uint32_t consumed;
79
80 // Parse field header
81 auto res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
82 if (!res.has_value()) {
83 ESP_LOGV(TAG, "Invalid field start at offset %ld", (long) (ptr - buffer));
84 return;
85 }
86
87 uint32_t tag = res->as_uint32();
88 uint32_t field_type = tag & WIRE_TYPE_MASK;
89 uint32_t field_id = tag >> 3;
90 ptr += consumed;
91
92 switch (field_type) {
93 case WIRE_TYPE_VARINT: { // VarInt
94 res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
95 if (!res.has_value()) {
96 ESP_LOGV(TAG, "Invalid VarInt at offset %ld", (long) (ptr - buffer));
97 return;
98 }
99 if (!this->decode_varint(field_id, *res)) {
100 ESP_LOGV(TAG, "Cannot decode VarInt field %" PRIu32 " with value %" PRIu32 "!", field_id, res->as_uint32());
101 }
102 ptr += consumed;
103 break;
104 }
105 case WIRE_TYPE_LENGTH_DELIMITED: { // Length-delimited
106 res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
107 if (!res.has_value()) {
108 ESP_LOGV(TAG, "Invalid Length Delimited at offset %ld", (long) (ptr - buffer));
109 return;
110 }
111 uint32_t field_length = res->as_uint32();
112 ptr += consumed;
113 if (ptr + field_length > end) {
114 ESP_LOGV(TAG, "Out-of-bounds Length Delimited at offset %ld", (long) (ptr - buffer));
115 return;
116 }
117 if (!this->decode_length(field_id, ProtoLengthDelimited(ptr, field_length))) {
118 ESP_LOGV(TAG, "Cannot decode Length Delimited field %" PRIu32 "!", field_id);
119 }
120 ptr += field_length;
121 break;
122 }
123 case WIRE_TYPE_FIXED32: { // 32-bit
124 if (ptr + 4 > end) {
125 ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at offset %ld", (long) (ptr - buffer));
126 return;
127 }
128 uint32_t val = encode_uint32(ptr[3], ptr[2], ptr[1], ptr[0]);
129 if (!this->decode_32bit(field_id, Proto32Bit(val))) {
130 ESP_LOGV(TAG, "Cannot decode 32-bit field %" PRIu32 " with value %" PRIu32 "!", field_id, val);
131 }
132 ptr += 4;
133 break;
134 }
135 default:
136 ESP_LOGV(TAG, "Invalid field type %u at offset %ld", field_type, (long) (ptr - buffer));
137 return;
138 }
139 }
140}
141
142#ifdef HAS_PROTO_MESSAGE_DUMP
143std::string ProtoMessage::dump() const {
144 std::string out;
145 this->dump_to(out);
146 return out;
147}
148#endif
149
150} // namespace esphome::api
virtual bool decode_32bit(uint32_t field_id, Proto32Bit value)
Definition proto.h:380
virtual bool decode_varint(uint32_t field_id, ProtoVarInt value)
Definition proto.h:378
virtual void decode(const uint8_t *buffer, size_t length)
Definition proto.cpp:73
virtual bool decode_length(uint32_t field_id, ProtoLengthDelimited value)
Definition proto.h:379
static uint32_t count_repeated_field(const uint8_t *buffer, size_t length, uint32_t target_field_id)
Count occurrences of a repeated field in a protobuf buffer.
Definition proto.cpp:10
std::string dump() const
Definition proto.cpp:143
virtual void dump_to(std::string &out) const =0
static optional< ProtoVarInt > parse(const uint8_t *buffer, uint32_t len, uint32_t *consumed)
Definition proto.h:78
mopeka_std_values val[4]
constexpr uint8_t WIRE_TYPE_VARINT
Definition proto.h:20
constexpr uint8_t WIRE_TYPE_MASK
Definition proto.h:23
constexpr uint8_t WIRE_TYPE_LENGTH_DELIMITED
Definition proto.h:21
constexpr uint8_t WIRE_TYPE_FIXED32
Definition proto.h:22
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
Definition helpers.h:402
uint8_t end[39]
Definition sun_gtil2.cpp:17
uint16_t length
Definition tt21100.cpp:0