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