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 // Gets the field key of the field at the current cursor position if valid.
81 //
82 // Note: avoid using this function unless you need to examine the field wire
83 // format properties. Using FieldNumber() is enough for manual decoding in
84 // most cases.
85 //
86 // Returns values:
87 //
88 // OK: The FieldKey is valid.
89 // DATA_LOSS: The FieldKey is invalid, the decode operation terminated.
90 // Any subsequent calls to Next() or Read*() will return
91 // DATA_LOSS.
92 Result<FieldKey> GetFieldKey() const;
93
94 // Reads a proto int32 value from the current cursor.
95 Status ReadInt32(int32_t* out) {
96 return ReadUint32(reinterpret_cast<uint32_t*>(out));
97 }
98
99 // Reads a proto uint32 value from the current cursor.
100 Status ReadUint32(uint32_t* out);
101
102 // Reads a proto int64 value from the current cursor.
103 Status ReadInt64(int64_t* out) {
104 return ReadVarint(reinterpret_cast<uint64_t*>(out));
105 }
106
107 // Reads a proto uint64 value from the current cursor.
108 Status ReadUint64(uint64_t* out) { return ReadVarint(out); }
109
110 // Reads a proto sint32 value from the current cursor.
111 Status ReadSint32(int32_t* out);
112
113 // Reads a proto sint64 value from the current cursor.
114 Status ReadSint64(int64_t* out);
115
116 // Reads a proto bool value from the current cursor.
117 Status ReadBool(bool* out);
118
119 // Reads a proto fixed32 value from the current cursor.
120 Status ReadFixed32(uint32_t* out) { return ReadFixed(out); }
121
122 // Reads a proto fixed64 value from the current cursor.
123 Status ReadFixed64(uint64_t* out) { return ReadFixed(out); }
124
125 // Reads a proto sfixed32 value from the current cursor.
126 Status ReadSfixed32(int32_t* out) {
127 return ReadFixed32(reinterpret_cast<uint32_t*>(out));
128 }
129
130 // Reads a proto sfixed64 value from the current cursor.
131 Status ReadSfixed64(int64_t* out) {
132 return ReadFixed64(reinterpret_cast<uint64_t*>(out));
133 }
134
135 // Reads a proto float value from the current cursor.
136 Status ReadFloat(float* out) {
137 static_assert(sizeof(float) == sizeof(uint32_t),
138 "Float and uint32_t must be the same size for protobufs");
139 return ReadFixed(out);
140 }
141
142 // Reads a proto double value from the current cursor.
143 Status ReadDouble(double* out) {
144 static_assert(sizeof(double) == sizeof(uint64_t),
145 "Double and uint64_t must be the same size for protobufs");
146 return ReadFixed(out);
147 }
148
149 // Reads a proto string value from the current cursor and returns a view of it
150 // in `out`. The raw protobuf data must outlive `out`. If the string field is
151 // invalid, `out` is not modified.
152 Status ReadString(std::string_view* out);
153
154 // Reads a proto bytes value from the current cursor and returns a view of it
155 // in `out`. The raw protobuf data must outlive the `out` span. If the
156 // bytes field is invalid, `out` is not modified.
157 Status ReadBytes(span<const std::byte>* out) { return ReadDelimited(out); }
158
159 // Resets the decoder to start reading a new proto message.
160 void Reset(span<const std::byte> proto) {
161 proto_ = proto;
162 previous_field_consumed_ = true;
163 }
164
165 private:
166 // Allow only the FindRaw function to access the raw bytes of the field.
168
169 // Returns the raw field value. The decoder MUST be at a valid field.
170 ConstByteSpan RawFieldBytes() { return GetFieldSize().ValueBytes(proto_); }
171
172 // Advances the cursor to the next field in the proto.
173 Status SkipField();
174
175 // Stores the size of a field.
176 class FieldSize {
177 public:
178 static constexpr FieldSize Invalid() { return {0, 0}; }
179
180 constexpr FieldSize(size_t key_size, size_t value_size)
181 : key_size_bytes_(key_size), value_size_bytes_(value_size) {}
182
183 bool ok() const { return key_size_bytes_ != 0; }
184
185 // Total size of the field (key and value); 0 if ok() is false.
186 size_t total() const { return key_size_bytes_ + value_size_bytes_; }
187
188 ConstByteSpan ValueBytes(ConstByteSpan field) const {
189 PW_DASSERT(ok());
190 return field.subspan(key_size_bytes_, value_size_bytes_);
191 }
192
193 private:
194 size_t key_size_bytes_; // size of key + length (if delimited field)
195 size_t value_size_bytes_; // size of raw value only
196 };
197
198 // Returns the size of the current field as a FieldSize object.
199 FieldSize GetFieldSize() const;
200
201 Status ConsumeKey(WireType expected_type);
202
203 // Reads a varint key-value pair from the current cursor position.
204 Status ReadVarint(uint64_t* out);
205
206 // Reads a fixed-size key-value pair from the current cursor position.
207 Status ReadFixed(std::byte* out, size_t size);
208
209 template <typename T>
210 Status ReadFixed(T* out) {
211 static_assert(
212 sizeof(T) == sizeof(uint32_t) || sizeof(T) == sizeof(uint64_t),
213 "Protobuf fixed-size fields must be 32- or 64-bit");
214 return ReadFixed(reinterpret_cast<std::byte*>(out), sizeof(T));
215 }
216
217 Status ReadDelimited(span<const std::byte>* out);
218
220 bool previous_field_consumed_;
221};
222
223class DecodeHandler;
224
225// A protobuf decoder that iterates over an encoded protobuf, calling a handler
226// for each field it encounters.
227//
228// Example usage:
229//
230// class FooProtoHandler : public DecodeHandler {
231// public:
232// Status ProcessField(CallbackDecoder& decoder,
233// uint32_t field_number) override {
234// switch (field_number) {
235// case FooFields::kBar:
236// if (!decoder.ReadSint32(&bar).ok()) {
237// bar = 0;
238// }
239// break;
240// case FooFields::kBaz:
241// if (!decoder.ReadUint32(&baz).ok()) {
242// baz = 0;
243// }
244// break;
245// }
246//
247// return OkStatus();
248// }
249//
250// int bar;
251// unsigned int baz;
252// };
253//
254// void DecodeFooProto(span<std::byte> raw_proto) {
255// Decoder decoder;
256// FooProtoHandler handler;
257//
258// decoder.set_handler(&handler);
259// if (!decoder.Decode(raw_proto).ok()) {
260// LOG_FATAL("Invalid foo message!");
261// }
262//
263// LOG_INFO("Read Foo proto message; bar: %d baz: %u",
264// handler.bar, handler.baz);
265// }
266//
268 public:
269 constexpr CallbackDecoder()
270 : decoder_({}), handler_(nullptr), state_(kReady) {}
271
272 CallbackDecoder(const CallbackDecoder& other) = delete;
273 CallbackDecoder& operator=(const CallbackDecoder& other) = delete;
274
275 void set_handler(DecodeHandler* handler) { handler_ = handler; }
276
277 // Decodes the specified protobuf data. The registered handler's ProcessField
278 // function is called on each field found in the data.
279 Status Decode(span<const std::byte> proto);
280
281 // Reads a proto int32 value from the current cursor.
282 Status ReadInt32(int32_t* out) { return decoder_.ReadInt32(out); }
283
284 // Reads a proto uint32 value from the current cursor.
285 Status ReadUint32(uint32_t* out) { return decoder_.ReadUint32(out); }
286
287 // Reads a proto int64 value from the current cursor.
288 Status ReadInt64(int64_t* out) { return decoder_.ReadInt64(out); }
289
290 // Reads a proto uint64 value from the current cursor.
291 Status ReadUint64(uint64_t* out) { return decoder_.ReadUint64(out); }
292
293 // Reads a proto sint64 value from the current cursor.
294 Status ReadSint32(int32_t* out) { return decoder_.ReadSint32(out); }
295
296 // Reads a proto sint64 value from the current cursor.
297 Status ReadSint64(int64_t* out) { return decoder_.ReadSint64(out); }
298
299 // Reads a proto bool value from the current cursor.
300 Status ReadBool(bool* out) { return decoder_.ReadBool(out); }
301
302 // Reads a proto fixed32 value from the current cursor.
303 Status ReadFixed32(uint32_t* out) { return decoder_.ReadFixed32(out); }
304
305 // Reads a proto fixed64 value from the current cursor.
306 Status ReadFixed64(uint64_t* out) { return decoder_.ReadFixed64(out); }
307
308 // Reads a proto sfixed32 value from the current cursor.
309 Status ReadSfixed32(int32_t* out) { return decoder_.ReadSfixed32(out); }
310
311 // Reads a proto sfixed64 value from the current cursor.
312 Status ReadSfixed64(int64_t* out) { return decoder_.ReadSfixed64(out); }
313
314 // Reads a proto float value from the current cursor.
315 Status ReadFloat(float* out) { return decoder_.ReadFloat(out); }
316
317 // Reads a proto double value from the current cursor.
318 Status ReadDouble(double* out) { return decoder_.ReadDouble(out); }
319
320 // Reads a proto string value from the current cursor and returns a view of it
321 // in `out`. The raw protobuf data must outlive `out`. If the string field is
322 // invalid, `out` is not modified.
323 Status ReadString(std::string_view* out) { return decoder_.ReadString(out); }
324
325 // Reads a proto bytes value from the current cursor and returns a view of it
326 // in `out`. The raw protobuf data must outlive the `out` span. If the
327 // bytes field is invalid, `out` is not modified.
328 Status ReadBytes(span<const std::byte>* out) {
329 return decoder_.ReadBytes(out);
330 }
331
332 bool cancelled() const { return state_ == kDecodeCancelled; }
333
334 private:
335 enum State {
336 kReady,
337 kDecodeInProgress,
338 kDecodeCancelled,
339 kDecodeFailed,
340 };
341
342 Decoder decoder_;
343 DecodeHandler* handler_;
344
345 State state_;
346};
347
348// The event-handling interface implemented for a proto callback decoding
349// operation.
351 public:
352 virtual ~DecodeHandler() = default;
353
354 // Callback called for each field encountered in the decoded proto message.
355 // Receives a pointer to the decoder object, allowing the handler to call
356 // the appropriate method to extract the field's data.
357 //
358 // If the status returned is not OkStatus(), the decode operation is exited
359 // with the provided status. Returning Status::Cancelled() allows a convenient
360 // way of stopping a decode early (for example, if a desired field is found).
361 virtual Status ProcessField(CallbackDecoder& decoder,
362 uint32_t field_number) = 0;
363};
364
366
367} // namespace pw::protobuf
Definition: result.h:145
Definition: status.h:120
Definition: decoder.h:267
Definition: decoder.h:350
Definition: decoder.h:50
friend Result< ConstByteSpan > FindRaw(ConstByteSpan, uint32_t)
Returns a span containing the raw bytes of the value.
Definition: find.h:837
Definition: span_impl.h:235