C/C++ API Reference
Loading...
Searching...
No Matches
decoder.h
1// Copyright 2020 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16#include <string_view>
17
18#include "pw_bytes/span.h"
19#include "pw_protobuf/wire_format.h"
20#include "pw_result/result.h"
21#include "pw_span/span.h"
22#include "pw_status/status.h"
23#include "pw_varint/varint.h"
24
25// This file defines a low-level event-based protobuf wire format decoder.
26// The decoder processes an encoded message by iterating over its fields. The
27// caller can extract the values of any fields it cares about.
28//
29// The decoder does not provide any in-memory data structures to represent a
30// protobuf message's data. More sophisticated APIs can be built on top of the
31// low-level decoder to provide additional functionality, if desired.
32//
33// Example usage:
34//
35// Decoder decoder(proto);
36// while (decoder.Next().ok()) {
37// switch (decoder.FieldNumber()) {
38// case 1:
39// decoder.ReadUint32(&my_uint32);
40// break;
41// // ... and other fields.
42// }
43// }
44//
45namespace pw::protobuf {
46
48
49// TODO(frolv): Rename this to MemoryDecoder to match the encoder naming.
50class Decoder {
51 public:
52 constexpr Decoder(span<const std::byte> proto)
53 : proto_(proto), previous_field_consumed_(true) {}
54
55 Decoder(const Decoder& other) = delete;
56 Decoder& operator=(const Decoder& other) = delete;
57
58 // Advances to the next field in the proto.
59 //
60 // If Next() returns OK, there is guaranteed to be a valid protobuf field at
61 // the current cursor position.
62 //
63 // Return values:
64 //
65 // OK: Advanced to a valid proto field.
66 // OUT_OF_RANGE: Reached the end of the proto message.
67 // DATA_LOSS: Invalid protobuf data.
68 //
69 Status Next();
70
71 // Returns the field number of the field at the current cursor position.
72 //
73 // A return value of 0 indicates that the field number is invalid. An invalid
74 // field number terminates the decode operation; any subsequent calls to
75 // Next() or Read*() will return DATA_LOSS.
76 //
77 // TODO(frolv): This should be refactored to return a Result<uint32_t>.
78 uint32_t FieldNumber() const;
79
80 // Reads a proto int32 value from the current cursor.
81 Status ReadInt32(int32_t* out) {
82 return ReadUint32(reinterpret_cast<uint32_t*>(out));
83 }
84
85 // Reads a proto uint32 value from the current cursor.
86 Status ReadUint32(uint32_t* out);
87
88 // Reads a proto int64 value from the current cursor.
89 Status ReadInt64(int64_t* out) {
90 return ReadVarint(reinterpret_cast<uint64_t*>(out));
91 }
92
93 // Reads a proto uint64 value from the current cursor.
94 Status ReadUint64(uint64_t* out) { return ReadVarint(out); }
95
96 // Reads a proto sint32 value from the current cursor.
97 Status ReadSint32(int32_t* out);
98
99 // Reads a proto sint64 value from the current cursor.
100 Status ReadSint64(int64_t* out);
101
102 // Reads a proto bool value from the current cursor.
103 Status ReadBool(bool* out);
104
105 // Reads a proto fixed32 value from the current cursor.
106 Status ReadFixed32(uint32_t* out) { return ReadFixed(out); }
107
108 // Reads a proto fixed64 value from the current cursor.
109 Status ReadFixed64(uint64_t* out) { return ReadFixed(out); }
110
111 // Reads a proto sfixed32 value from the current cursor.
112 Status ReadSfixed32(int32_t* out) {
113 return ReadFixed32(reinterpret_cast<uint32_t*>(out));
114 }
115
116 // Reads a proto sfixed64 value from the current cursor.
117 Status ReadSfixed64(int64_t* out) {
118 return ReadFixed64(reinterpret_cast<uint64_t*>(out));
119 }
120
121 // Reads a proto float value from the current cursor.
122 Status ReadFloat(float* out) {
123 static_assert(sizeof(float) == sizeof(uint32_t),
124 "Float and uint32_t must be the same size for protobufs");
125 return ReadFixed(out);
126 }
127
128 // Reads a proto double value from the current cursor.
129 Status ReadDouble(double* out) {
130 static_assert(sizeof(double) == sizeof(uint64_t),
131 "Double and uint64_t must be the same size for protobufs");
132 return ReadFixed(out);
133 }
134
135 // Reads a proto string value from the current cursor and returns a view of it
136 // in `out`. The raw protobuf data must outlive `out`. If the string field is
137 // invalid, `out` is not modified.
138 Status ReadString(std::string_view* out);
139
140 // Reads a proto bytes value from the current cursor and returns a view of it
141 // in `out`. The raw protobuf data must outlive the `out` span. If the
142 // bytes field is invalid, `out` is not modified.
143 Status ReadBytes(span<const std::byte>* out) { return ReadDelimited(out); }
144
145 // Resets the decoder to start reading a new proto message.
146 void Reset(span<const std::byte> proto) {
147 proto_ = proto;
148 previous_field_consumed_ = true;
149 }
150
151 private:
152 // Allow only the FindRaw function to access the raw bytes of the field.
154
155 // Returns the raw field value. The decoder MUST be at a valid field.
156 ConstByteSpan RawFieldBytes() { return GetFieldSize().ValueBytes(proto_); }
157
158 // Advances the cursor to the next field in the proto.
159 Status SkipField();
160
161 // Stores the size of a field.
162 class FieldSize {
163 public:
164 static constexpr FieldSize Invalid() { return {0, 0}; }
165
166 constexpr FieldSize(size_t key_size, size_t value_size)
167 : key_size_bytes_(key_size), value_size_bytes_(value_size) {}
168
169 bool ok() const { return key_size_bytes_ != 0; }
170
171 // Total size of the field (key and value); 0 if ok() is false.
172 size_t total() const { return key_size_bytes_ + value_size_bytes_; }
173
174 ConstByteSpan ValueBytes(ConstByteSpan field) const {
175 PW_DASSERT(ok());
176 return field.subspan(key_size_bytes_, value_size_bytes_);
177 }
178
179 private:
180 size_t key_size_bytes_; // size of key + length (if delimited field)
181 size_t value_size_bytes_; // size of raw value only
182 };
183
184 // Returns the size of the current field as a FieldSize object.
185 FieldSize GetFieldSize() const;
186
187 Status ConsumeKey(WireType expected_type);
188
189 // Reads a varint key-value pair from the current cursor position.
190 Status ReadVarint(uint64_t* out);
191
192 // Reads a fixed-size key-value pair from the current cursor position.
193 Status ReadFixed(std::byte* out, size_t size);
194
195 template <typename T>
196 Status ReadFixed(T* out) {
197 static_assert(
198 sizeof(T) == sizeof(uint32_t) || sizeof(T) == sizeof(uint64_t),
199 "Protobuf fixed-size fields must be 32- or 64-bit");
200 return ReadFixed(reinterpret_cast<std::byte*>(out), sizeof(T));
201 }
202
203 Status ReadDelimited(span<const std::byte>* out);
204
206 bool previous_field_consumed_;
207};
208
209class DecodeHandler;
210
211// A protobuf decoder that iterates over an encoded protobuf, calling a handler
212// for each field it encounters.
213//
214// Example usage:
215//
216// class FooProtoHandler : public DecodeHandler {
217// public:
218// Status ProcessField(CallbackDecoder& decoder,
219// uint32_t field_number) override {
220// switch (field_number) {
221// case FooFields::kBar:
222// if (!decoder.ReadSint32(&bar).ok()) {
223// bar = 0;
224// }
225// break;
226// case FooFields::kBaz:
227// if (!decoder.ReadUint32(&baz).ok()) {
228// baz = 0;
229// }
230// break;
231// }
232//
233// return OkStatus();
234// }
235//
236// int bar;
237// unsigned int baz;
238// };
239//
240// void DecodeFooProto(span<std::byte> raw_proto) {
241// Decoder decoder;
242// FooProtoHandler handler;
243//
244// decoder.set_handler(&handler);
245// if (!decoder.Decode(raw_proto).ok()) {
246// LOG_FATAL("Invalid foo message!");
247// }
248//
249// LOG_INFO("Read Foo proto message; bar: %d baz: %u",
250// handler.bar, handler.baz);
251// }
252//
254 public:
255 constexpr CallbackDecoder()
256 : decoder_({}), handler_(nullptr), state_(kReady) {}
257
258 CallbackDecoder(const CallbackDecoder& other) = delete;
259 CallbackDecoder& operator=(const CallbackDecoder& other) = delete;
260
261 void set_handler(DecodeHandler* handler) { handler_ = handler; }
262
263 // Decodes the specified protobuf data. The registered handler's ProcessField
264 // function is called on each field found in the data.
265 Status Decode(span<const std::byte> proto);
266
267 // Reads a proto int32 value from the current cursor.
268 Status ReadInt32(int32_t* out) { return decoder_.ReadInt32(out); }
269
270 // Reads a proto uint32 value from the current cursor.
271 Status ReadUint32(uint32_t* out) { return decoder_.ReadUint32(out); }
272
273 // Reads a proto int64 value from the current cursor.
274 Status ReadInt64(int64_t* out) { return decoder_.ReadInt64(out); }
275
276 // Reads a proto uint64 value from the current cursor.
277 Status ReadUint64(uint64_t* out) { return decoder_.ReadUint64(out); }
278
279 // Reads a proto sint64 value from the current cursor.
280 Status ReadSint32(int32_t* out) { return decoder_.ReadSint32(out); }
281
282 // Reads a proto sint64 value from the current cursor.
283 Status ReadSint64(int64_t* out) { return decoder_.ReadSint64(out); }
284
285 // Reads a proto bool value from the current cursor.
286 Status ReadBool(bool* out) { return decoder_.ReadBool(out); }
287
288 // Reads a proto fixed32 value from the current cursor.
289 Status ReadFixed32(uint32_t* out) { return decoder_.ReadFixed32(out); }
290
291 // Reads a proto fixed64 value from the current cursor.
292 Status ReadFixed64(uint64_t* out) { return decoder_.ReadFixed64(out); }
293
294 // Reads a proto sfixed32 value from the current cursor.
295 Status ReadSfixed32(int32_t* out) { return decoder_.ReadSfixed32(out); }
296
297 // Reads a proto sfixed64 value from the current cursor.
298 Status ReadSfixed64(int64_t* out) { return decoder_.ReadSfixed64(out); }
299
300 // Reads a proto float value from the current cursor.
301 Status ReadFloat(float* out) { return decoder_.ReadFloat(out); }
302
303 // Reads a proto double value from the current cursor.
304 Status ReadDouble(double* out) { return decoder_.ReadDouble(out); }
305
306 // Reads a proto string value from the current cursor and returns a view of it
307 // in `out`. The raw protobuf data must outlive `out`. If the string field is
308 // invalid, `out` is not modified.
309 Status ReadString(std::string_view* out) { return decoder_.ReadString(out); }
310
311 // Reads a proto bytes value from the current cursor and returns a view of it
312 // in `out`. The raw protobuf data must outlive the `out` span. If the
313 // bytes field is invalid, `out` is not modified.
314 Status ReadBytes(span<const std::byte>* out) {
315 return decoder_.ReadBytes(out);
316 }
317
318 bool cancelled() const { return state_ == kDecodeCancelled; }
319
320 private:
321 enum State {
322 kReady,
323 kDecodeInProgress,
324 kDecodeCancelled,
325 kDecodeFailed,
326 };
327
328 Decoder decoder_;
329 DecodeHandler* handler_;
330
331 State state_;
332};
333
334// The event-handling interface implemented for a proto callback decoding
335// operation.
337 public:
338 virtual ~DecodeHandler() = default;
339
340 // Callback called for each field encountered in the decoded proto message.
341 // Receives a pointer to the decoder object, allowing the handler to call
342 // the appropriate method to extract the field's data.
343 //
344 // If the status returned is not OkStatus(), the decode operation is exited
345 // with the provided status. Returning Status::Cancelled() allows a convenient
346 // way of stopping a decode early (for example, if a desired field is found).
347 virtual Status ProcessField(CallbackDecoder& decoder,
348 uint32_t field_number) = 0;
349};
350
352
353} // namespace pw::protobuf
Definition: poll.h:25
Definition: status.h:109
Definition: decoder.h:253
Definition: decoder.h:336
Definition: decoder.h:50
friend Result< ConstByteSpan > FindRaw(ConstByteSpan, uint32_t)
Returns a span containing the raw bytes of the value.
Definition: find.h:1121
Definition: span_impl.h:235