esphome-api-client 0.5.0
Modern C++17 client for the ESPHome native API (plaintext + Noise)
Loading...
Searching...
No Matches
wire.hpp
Go to the documentation of this file.
1#pragma once
2
11
12#include <array>
13#include <cstddef>
14#include <cstdint>
15#include <cstring>
16#include <string>
17
18namespace esphome::api {
19
21enum class WireType : std::uint8_t {
22 Varint = 0,
23 Fixed64 = 1,
24 LengthDelimited = 2,
25 Fixed32 = 5,
26};
27
28// ---------------------------------------------------------------------------
29// ZigZag encoding for sint32 / sint64.
30// ---------------------------------------------------------------------------
31
32inline std::uint32_t zigzag_encode32(const std::int32_t v) {
33 return (static_cast<std::uint32_t>(v) << 1) ^ static_cast<std::uint32_t>(v >> 31);
34}
35inline std::int32_t zigzag_decode32(const std::uint32_t v) {
36 return static_cast<std::int32_t>((v >> 1) ^ (~(v & 1) + 1));
37}
38inline std::uint64_t zigzag_encode64(const std::int64_t v) {
39 return (static_cast<std::uint64_t>(v) << 1) ^ static_cast<std::uint64_t>(v >> 63);
40}
41inline std::int64_t zigzag_decode64(const std::uint64_t v) {
42 return static_cast<std::int64_t>((v >> 1) ^ (~(v & 1) + 1));
43}
44
45// ---------------------------------------------------------------------------
46// Size helpers (mirror the encoder so calculate_size() can be exact).
47// ---------------------------------------------------------------------------
48
50inline std::size_t varint_size(std::uint64_t v) {
51 std::size_t n = 1;
52 while (v >= 0x80) {
53 v >>= 7;
54 ++n;
55 }
56 return n;
57}
58
61inline std::size_t tag_size(const std::uint32_t field_number) {
62 return varint_size(static_cast<std::uint64_t>(field_number) << 3);
63}
64
65// ---------------------------------------------------------------------------
66// Writer — appends to a std::string buffer.
67// ---------------------------------------------------------------------------
68
70public:
71 explicit ProtoWriter(std::string& out) : out_(out) {}
72
73 void raw_byte(const std::uint8_t b) const {
74 out_.push_back(static_cast<char>(b));
75 }
76 void raw_bytes(const void* data, const std::size_t n) const {
77 out_.append(static_cast<const char*>(data), n);
78 }
79
80 void varint(std::uint64_t v) const {
81 while (v >= 0x80) {
82 raw_byte(static_cast<std::uint8_t>(v) | 0x80U);
83 v >>= 7;
84 }
85 raw_byte(static_cast<std::uint8_t>(v));
86 }
87
88 void tag(const std::uint32_t field_number, WireType wt) const {
89 varint((static_cast<std::uint64_t>(field_number) << 3) | static_cast<std::uint64_t>(wt));
90 }
91
92 void fixed32(const std::uint32_t v) const {
93 std::array<std::uint8_t, 4> b{};
94 for (std::size_t i = 0; i < b.size(); ++i)
95 b[i] = static_cast<std::uint8_t>(v >> (8 * i));
96 raw_bytes(b.data(), b.size());
97 }
98
99 void fixed64(const std::uint64_t v) const {
100 std::array<std::uint8_t, 8> b{};
101 for (std::size_t i = 0; i < b.size(); ++i)
102 b[i] = static_cast<std::uint8_t>(v >> (8 * i));
103 raw_bytes(b.data(), b.size());
104 }
105
106 void length_delimited(const std::string& s) const {
107 varint(s.size());
108 raw_bytes(s.data(), s.size());
109 }
110
111private:
112 std::string& out_;
113};
114
115// ---------------------------------------------------------------------------
116// Reader — consumes a byte range. Methods return false on truncation / error
117// and latch an error flag so the decode loop can bail out.
118// ---------------------------------------------------------------------------
119
121public:
122 ProtoReader() = default;
123 ProtoReader(const void* data, const std::size_t size)
124 : ptr_(static_cast<const std::uint8_t*>(data)),
125 end_(static_cast<const std::uint8_t*>(data) + size) {}
126
127 [[nodiscard]] bool ok() const {
128 return ok_;
129 }
130 [[nodiscard]] bool eof() const {
131 return ptr_ >= end_;
132 }
133
136 bool read_tag(std::uint32_t& field_number, WireType& wt) {
137 if (ptr_ >= end_)
138 return false;
139 std::uint64_t key = 0;
140 if (!varint(key))
141 return false;
142 field_number = static_cast<std::uint32_t>(key >> 3);
143 wt = static_cast<WireType>(key & 0x7U);
144 return true;
145 }
146
147 bool varint(std::uint64_t& out) {
148 std::uint64_t result = 0;
149 int shift = 0;
150 while (shift < 64) {
151 if (ptr_ >= end_)
152 return fail();
153 const std::uint8_t b = *ptr_++;
154 result |= static_cast<std::uint64_t>(b & 0x7FU) << shift;
155 if ((b & 0x80U) == 0) {
156 out = result;
157 return true;
158 }
159 shift += 7;
160 }
161 return fail();
162 }
163
164 bool fixed32(std::uint32_t& out) {
165 if (end_ - ptr_ < 4)
166 return fail();
167 std::uint32_t v = 0;
168 v |= static_cast<std::uint32_t>(ptr_[0]);
169 v |= static_cast<std::uint32_t>(ptr_[1]) << 8;
170 v |= static_cast<std::uint32_t>(ptr_[2]) << 16;
171 v |= static_cast<std::uint32_t>(ptr_[3]) << 24;
172 ptr_ += 4;
173 out = v;
174 return true;
175 }
176
177 bool fixed64(std::uint64_t& out) {
178 if (end_ - ptr_ < 8)
179 return fail();
180 std::uint64_t v = 0;
181 for (int i = 0; i < 8; ++i)
182 v |= static_cast<std::uint64_t>(ptr_[i]) << (8 * i);
183 ptr_ += 8;
184 out = v;
185 return true;
186 }
187
189 bool length_delimited(std::string& out) {
190 std::uint64_t len = 0;
191 if (!varint(len))
192 return false;
193 if (static_cast<std::uint64_t>(end_ - ptr_) < len)
194 return fail();
195 out.assign(reinterpret_cast<const char*>(ptr_), static_cast<std::size_t>(len));
196 ptr_ += len;
197 return true;
198 }
199
203 std::uint64_t len = 0;
204 if (!varint(len))
205 return false;
206 if (static_cast<std::uint64_t>(end_ - ptr_) < len)
207 return fail();
208 out = ProtoReader(ptr_, static_cast<std::size_t>(len));
209 ptr_ += len;
210 return true;
211 }
212
214 bool skip_field(const WireType wt) {
215 switch (wt) {
216 case WireType::Varint: {
217 std::uint64_t tmp = 0;
218 return varint(tmp);
219 }
220 case WireType::Fixed64: {
221 std::uint64_t tmp = 0;
222 return fixed64(tmp);
223 }
224 case WireType::Fixed32: {
225 std::uint32_t tmp = 0;
226 return fixed32(tmp);
227 }
229 std::uint64_t len = 0;
230 if (!varint(len))
231 return false;
232 if (static_cast<std::uint64_t>(end_ - ptr_) < len)
233 return fail();
234 ptr_ += len;
235 return true;
236 }
237 }
238 return fail();
239 }
240
241private:
242 bool fail() {
243 ok_ = false;
244 return false;
245 }
246
247 const std::uint8_t* ptr_ = nullptr;
248 const std::uint8_t* end_ = nullptr;
249 bool ok_ = true;
250};
251
252// ---------------------------------------------------------------------------
253// float / double <-> integer bit reinterpretation (no bit_cast in C++17).
254// ---------------------------------------------------------------------------
255
256inline std::uint32_t float_to_bits(const float f) {
257 std::uint32_t bits;
258 std::memcpy(&bits, &f, sizeof(bits));
259 return bits;
260}
261inline float bits_to_float(const std::uint32_t bits) {
262 float f;
263 std::memcpy(&f, &bits, sizeof(f));
264 return f;
265}
266inline std::uint64_t double_to_bits(const double d) {
267 std::uint64_t bits;
268 std::memcpy(&bits, &d, sizeof(bits));
269 return bits;
270}
271inline double bits_to_double(const std::uint64_t bits) {
272 double d;
273 std::memcpy(&d, &bits, sizeof(d));
274 return d;
275}
276
277} // namespace esphome::api
Definition wire.hpp:120
ProtoReader(const void *data, const std::size_t size)
Definition wire.hpp:123
bool skip_field(const WireType wt)
Skip a field of the given wire type (unknown field forward-compat).
Definition wire.hpp:214
bool varint(std::uint64_t &out)
Definition wire.hpp:147
bool read_tag(std::uint32_t &field_number, WireType &wt)
Read a field tag.
Definition wire.hpp:136
bool eof() const
Definition wire.hpp:130
bool length_delimited(std::string &out)
Read a length-delimited blob into out.
Definition wire.hpp:189
bool sub_reader(ProtoReader &out)
Obtain a sub-reader over the next length-delimited region (for embedded messages and packed repeated ...
Definition wire.hpp:202
bool fixed64(std::uint64_t &out)
Definition wire.hpp:177
bool fixed32(std::uint32_t &out)
Definition wire.hpp:164
bool ok() const
Definition wire.hpp:127
Definition wire.hpp:69
ProtoWriter(std::string &out)
Definition wire.hpp:71
void length_delimited(const std::string &s) const
Definition wire.hpp:106
void raw_byte(const std::uint8_t b) const
Definition wire.hpp:73
void fixed32(const std::uint32_t v) const
Definition wire.hpp:92
void varint(std::uint64_t v) const
Definition wire.hpp:80
void raw_bytes(const void *data, const std::size_t n) const
Definition wire.hpp:76
void fixed64(const std::uint64_t v) const
Definition wire.hpp:99
void tag(const std::uint32_t field_number, WireType wt) const
Definition wire.hpp:88
Definition bytes.hpp:10
std::size_t tag_size(const std::uint32_t field_number)
Number of bytes the tag for field_number occupies (wire type does not change the byte count — it only...
Definition wire.hpp:61
std::uint32_t float_to_bits(const float f)
Definition wire.hpp:256
std::uint32_t zigzag_encode32(const std::int32_t v)
Definition wire.hpp:32
std::uint64_t double_to_bits(const double d)
Definition wire.hpp:266
std::uint64_t zigzag_encode64(const std::int64_t v)
Definition wire.hpp:38
std::size_t varint_size(std::uint32_t value) noexcept
Number of bytes the base-128 encoding of value occupies.
std::int32_t zigzag_decode32(const std::uint32_t v)
Definition wire.hpp:35
std::int64_t zigzag_decode64(const std::uint64_t v)
Definition wire.hpp:41
double bits_to_double(const std::uint64_t bits)
Definition wire.hpp:271
float bits_to_float(const std::uint32_t bits)
Definition wire.hpp:261
WireType
Protobuf wire types (the low 3 bits of a field tag).
Definition wire.hpp:21
@ Fixed32
fixed32/sfixed32/float
@ Fixed64
fixed64/sfixed64/double
@ Varint
int32/int64/uint32/uint64/sint32/sint64/bool/enum
@ LengthDelimited
string/bytes/embedded message/packed repeated