C/C++ API Reference
Loading...
Searching...
No Matches
message.h
1// Copyright 2021 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//
15// The header provides a set of helper utils for protobuf related operations.
16// The APIs may not be finalized yet.
17
18#pragma once
19
20#include <cstddef>
21#include <string_view>
22
23#include "pw_assert/check.h"
24#include "pw_protobuf/internal/proto_integer_base.h"
25#include "pw_protobuf/stream_decoder.h"
26#include "pw_status/status.h"
27#include "pw_status/try.h"
28#include "pw_stream/interval_reader.h"
29#include "pw_stream/stream.h"
30
31namespace pw::protobuf {
32
34
35// The following defines classes that represent various parsed proto integer
36// types or an error code to indicate parsing failure.
37//
38// For normal uses, the class should be created from `class Message`. See
39// comment for `class Message` for usage.
40
41class Uint32 : public internal::ProtoIntegerBase<uint32_t> {
42 public:
43 using ProtoIntegerBase<uint32_t>::ProtoIntegerBase;
44};
45
46class Int32 : public internal::ProtoIntegerBase<int32_t> {
47 public:
48 using ProtoIntegerBase<int32_t>::ProtoIntegerBase;
49};
50
51class Sint32 : public internal::ProtoIntegerBase<int32_t> {
52 public:
53 using ProtoIntegerBase<int32_t>::ProtoIntegerBase;
54};
55
56class Fixed32 : public internal::ProtoIntegerBase<uint32_t> {
57 public:
58 using ProtoIntegerBase<uint32_t>::ProtoIntegerBase;
59};
60
61class Sfixed32 : public internal::ProtoIntegerBase<int32_t> {
62 public:
63 using ProtoIntegerBase<int32_t>::ProtoIntegerBase;
64};
65
66class Uint64 : public internal::ProtoIntegerBase<uint64_t> {
67 public:
68 using ProtoIntegerBase<uint64_t>::ProtoIntegerBase;
69};
70
71class Int64 : public internal::ProtoIntegerBase<int64_t> {
72 public:
73 using ProtoIntegerBase<int64_t>::ProtoIntegerBase;
74};
75
76class Sint64 : public internal::ProtoIntegerBase<int64_t> {
77 public:
78 using ProtoIntegerBase<int64_t>::ProtoIntegerBase;
79};
80
81class Fixed64 : public internal::ProtoIntegerBase<uint64_t> {
82 public:
83 using ProtoIntegerBase<uint64_t>::ProtoIntegerBase;
84};
85
86class Sfixed64 : public internal::ProtoIntegerBase<int64_t> {
87 public:
88 using ProtoIntegerBase<int64_t>::ProtoIntegerBase;
89};
90
91class Float : public internal::ProtoIntegerBase<float> {
92 public:
93 using ProtoIntegerBase<float>::ProtoIntegerBase;
94};
95
96class Double : public internal::ProtoIntegerBase<double> {
97 public:
98 using ProtoIntegerBase<double>::ProtoIntegerBase;
99};
100
101class Bool : public internal::ProtoIntegerBase<bool> {
102 public:
103 using ProtoIntegerBase<bool>::ProtoIntegerBase;
104};
105
106// An object that represents a parsed `bytes` field or an error code. The
107// bytes are available via an stream::IntervalReader by GetBytesReader().
108//
109// For normal uses, the class should be created from `class Message`. See
110// comment for `class Message` for usage.
111class Bytes {
112 public:
113 Bytes() = default;
114 Bytes(Status status) : reader_(status) {}
115 Bytes(stream::IntervalReader reader) : reader_(std::move(reader)) {}
116 stream::IntervalReader GetBytesReader() { return reader_; }
117
118 bool ok() { return reader_.ok(); }
119 Status status() { return reader_.status(); }
120
121 // Check whether the bytes value equals the given `bytes`.
122 Result<bool> Equal(ConstByteSpan bytes);
123
124 private:
126};
127
128// An object that represents a parsed `string` field or an error code. The
129// string value is available via an stream::IntervalReader by
130// GetBytesReader().
131//
132// For normal uses, the class should be created from `class Message`. See
133// comment for `class Message` for usage.
134class String : public Bytes {
135 public:
136 using Bytes::Bytes;
137
138 // Check whether the string value equals the given `str`
139 Result<bool> Equal(std::string_view str);
140};
141
142// Forward declaration of parser classes.
143template <typename FieldType>
145template <typename FieldType>
147template <typename FieldType>
148class StringMapParser;
149class Message;
150
160
161// Message - A helper class for parsing a proto message.
162//
163// Examples:
164//
165// message Nested {
166// string nested_str = 1;
167// bytes nested_bytes = 2;
168// }
169//
170// message {
171// string str = 1;
172// bytes bytes = 2;
173// uint32 integer = 3
174// repeated string rep_str = 4;
175// map<string, bytes> str_to_bytes = 5;
176// Nested nested = 6;
177// }
178//
179// // Given a seekable `reader` that reads the top-level proto message, and
180// // a <size> that gives the size of the proto message:
181// Message message(reader, <size>);
182//
183// // Prase simple basic value fields
184// String str = message.AsString(1); // string
185// Bytes bytes = message.AsBytes(2); // bytes
186// Uint32 integer = messasge_parser.AsUint32(3); // uint32 integer
187//
188// // Parse repeated field `repeated string rep_str = 4;`
189// RepeatedStrings rep_str = message.AsRepeatedString(4);
190// // Iterate through the entries. If proto is malformed when
191// // iterating, the next element (`str` in this case) will be invalid
192// // and loop will end in the iteration after.
193// for (String str : rep_str) {
194// // Check status
195// if (!str.ok()) {
196// // In the case of error, loop will end in the next iteration if
197// // continues. This is the chance for code to catch the error.
198// ...
199// }
200// ...
201// }
202//
203// // Parse map field `map<string, bytes> str_to_bytes = 5;`
204// StringToBytesMap str_to_bytes = message.AsStringToBytesMap(5);
205//
206// // Access the entry by a given key value
207// Bytes bytes_for_key = str_to_bytes["key"];
208//
209// // Or iterate through map entries
210// for (StringToBytesMapEntry entry : str_to_bytes) {
211// if (!entry.ok()) {
212// // In the case of error, loop will end in the next iteration if
213// // continues. This is the chance for code to catch the error.
214// ...
215// }
216// String key = entry.Key();
217// Bytes value = entry.Value();
218// ...
219// }
220//
221// // Parse nested message `Nested nested = 6;`
222// Message nested = message.AsMessage(6).
223// String nested_str = nested.AsString(1);
224// Bytes nested_bytes = nested.AsBytes(2);
225//
226// // The `AsXXX()` methods above internally traverse all the fields to find
227// // the one with the give field number. This can be expensive if called
228// // multiple times. Therefore, whenever possible, it is recommended to use
229// // the following iteration to iterate and process each field directly.
230// for (Message::Field field : message) {
231// if (!field.ok()) {
232// // In the case of error, loop will end in the next iteration if
233// // continues. This is the chance for code to catch the error.
234// ...
235// }
236// if (field.field_number() == 1) {
237// String str = field.As<String>();
238// ...
239// } else if (field.field_number() == 2) {
240// Bytes bytes = field.As<Bytes>();
241// ...
242// } else if (field.field_number() == 6) {
243// Message nested = field.As<Message>();
244// ...
245// }
246// }
247//
248// All parser objects created above internally hold the same reference
249// to `reader`. Therefore it needs to maintain valid lifespan throughout the
250// operations. The parser objects can work independently and without blocking
251// each other. All method calls and for-iterations above are re-enterable.
252class Message {
253 public:
254 class Field {
255 public:
256 uint32_t field_number() { return field_number_; }
257 const stream::IntervalReader& field_reader() { return field_reader_; }
258 bool ok() { return field_reader_.ok(); }
259 Status status() { return field_reader_.status(); }
260
261 // Create a helper parser type of `FieldType` for the field.
262 // The default implementation below assumes the field is a length-delimited
263 // field. Other cases such as primitive integer uint32 will be handled by
264 // template specialization.
265 template <typename FieldType>
266 FieldType As() {
267 if (!field_reader_.ok()) {
268 return FieldType(field_reader_.status());
269 }
270
271 StreamDecoder decoder(field_reader_.Reset());
272 PW_TRY(decoder.Next());
273 Result<StreamDecoder::Bounds> payload_bounds =
274 decoder.GetLengthDelimitedPayloadBounds();
275 PW_TRY(payload_bounds.status());
276 // The bounds is relative to the given stream::IntervalReader. Convert
277 // it to the interval relative to the source_reader.
278 return FieldType(stream::IntervalReader(
279 field_reader_.source_reader(),
280 payload_bounds.value().low + field_reader_.start(),
281 payload_bounds.value().high + field_reader_.start()));
282 }
283
284 private:
285 Field() = default;
286 Field(Status status) : field_reader_(status), field_number_(0) {}
287 Field(stream::IntervalReader reader, uint32_t field_number)
288 : field_reader_(std::move(reader)), field_number_(field_number) {}
289
290 stream::IntervalReader field_reader_;
291 uint32_t field_number_;
292
293 friend class Message;
294 };
295
296 class iterator {
297 public:
298 iterator& operator++();
299
300 iterator operator++(int) {
301 iterator iter = *this;
302 this->operator++();
303 return iter;
304 }
305
306 bool ok() { return status_.ok(); }
307 Status status() { return status_; }
308 Field operator*() { return current_; }
309 Field* operator->() { return &current_; }
310 bool operator!=(const iterator& other) const { return !(*this == other); }
311
312 bool operator==(const iterator& other) const {
313 return eof_ == other.eof_ && reader_ == other.reader_;
314 }
315
316 private:
318 bool eof_ = false;
319 Field current_;
320 Status status_ = OkStatus();
321
323 : reader_(std::move(reader)), status_(reader_.status()) {
324 this->operator++();
325 }
326
327 friend class Message;
328 };
329
330 Message() = default;
331 Message(Status status) : reader_(status) {}
332 Message(stream::IntervalReader reader) : reader_(std::move(reader)) {}
333 Message(stream::SeekableReader& proto_source, size_t size)
334 : reader_(proto_source, 0, size) {}
335
336 // Parse a sub-field in the message given by `field_number` as bytes.
337 Bytes AsBytes(uint32_t field_number) { return As<Bytes>(field_number); }
338
339 // Parse a sub-field in the message given by `field_number` as string.
340 String AsString(uint32_t field_number) { return As<String>(field_number); }
341
342 // Parse a sub-field in the message given by `field_number` as one of the
343 // proto integer type.
344 Int32 AsInt32(uint32_t field_number) { return As<Int32>(field_number); }
345 Sint32 AsSint32(uint32_t field_number) { return As<Sint32>(field_number); }
346 Uint32 AsUint32(uint32_t field_number) { return As<Uint32>(field_number); }
347 Fixed32 AsFixed32(uint32_t field_number) { return As<Fixed32>(field_number); }
348 Int64 AsInt64(uint32_t field_number) { return As<Int64>(field_number); }
349 Sint64 AsSint64(uint32_t field_number) { return As<Sint64>(field_number); }
350 Uint64 AsUint64(uint32_t field_number) { return As<Uint64>(field_number); }
351 Fixed64 AsFixed64(uint32_t field_number) { return As<Fixed64>(field_number); }
352
353 Sfixed32 AsSfixed32(uint32_t field_number) {
354 return As<Sfixed32>(field_number);
355 }
356
357 Sfixed64 AsSfixed64(uint32_t field_number) {
358 return As<Sfixed64>(field_number);
359 }
360
361 Float AsFloat(uint32_t field_number) { return As<Float>(field_number); }
362 Double AsDouble(uint32_t field_number) { return As<Double>(field_number); }
363
364 Bool AsBool(uint32_t field_number) { return As<Bool>(field_number); }
365
366 // Parse a sub-field in the message given by `field_number` as another
367 // message.
368 Message AsMessage(uint32_t field_number) { return As<Message>(field_number); }
369
370 // Parse a sub-field in the message given by `field_number` as `repeated
371 // string`.
372 RepeatedBytes AsRepeatedBytes(uint32_t field_number);
373
374 // Parse a sub-field in the message given by `field_number` as `repeated
375 // string`.
376 RepeatedStrings AsRepeatedStrings(uint32_t field_number);
377
378 // Parse a sub-field in the message given by `field_number` as `repeated
379 // message`.
380 RepeatedMessages AsRepeatedMessages(uint32_t field_number);
381
382 // Parse a sub-field in the message given by `field_number` as `map<string,
383 // message>`.
384 StringToMessageMap AsStringToMessageMap(uint32_t field_number);
385
386 // Parse a sub-field in the message given by `field_number` as
387 // `map<string, bytes>`.
388 StringToBytesMap AsStringToBytesMap(uint32_t field_number);
389
390 // Parse a sub-field in the message given by `field_number` as
391 // `map<string, string>`.
392 StringToStringMap AsStringToStringMap(uint32_t field_number);
393
394 // Convert the message to a Bytes that represents the raw bytes of this
395 // message. This can be used to obatained the serialized wire-format of the
396 // message.
397 Bytes ToBytes() { return Bytes(reader_.Reset()); }
398
399 bool ok() { return reader_.ok(); }
400 Status status() { return reader_.status(); }
401
402 iterator begin();
403 iterator end();
404
405 // Parse a field given by `field_number` as the target parser type
406 // `FieldType`.
407 //
408 // Note: This method assumes that the message has only 1 field with the given
409 // <field_number>. It returns the first matching it find. It does not perform
410 // value overridding or string concatenation for multiple fields with the same
411 // <field_number>.
412 //
413 // Since the method needs to traverse all fields, it can be inefficient if
414 // called multiple times exepcially on slow reader.
415 template <typename FieldType>
416 FieldType As(uint32_t field_number) {
417 for (Field field : *this) {
418 if (field.field_number() == field_number) {
419 return field.As<FieldType>();
420 }
421 }
422
423 return FieldType(Status::NotFound());
424 }
425
426 template <typename FieldType>
427 RepeatedFieldParser<FieldType> AsRepeated(uint32_t field_number) {
428 return RepeatedFieldParser<FieldType>(*this, field_number);
429 }
430
431 template <typename FieldParser>
432 StringMapParser<FieldParser> AsStringMap(uint32_t field_number) {
433 return StringMapParser<FieldParser>(*this, field_number);
434 }
435
436 private:
437 stream::IntervalReader reader_;
438
439 // Consume the current field. If the field has already been processed, i.e.
440 // by calling one of the Read..() method, nothing is done. After calling this
441 // method, the reader will be pointing either to the start of the next
442 // field (i.e. the starting offset of the field key), or the end of the
443 // stream. The method is for use by Message for computing field interval.
444 static Status ConsumeCurrentField(StreamDecoder& decoder) {
445 return decoder.field_consumed_ ? OkStatus() : decoder.SkipField();
446 }
447};
448
449// The following are template specialization for proto integer types.
450template <>
451Uint32 Message::Field::As<Uint32>();
452
453template <>
454Int32 Message::Field::As<Int32>();
455
456template <>
457Sint32 Message::Field::As<Sint32>();
458
459template <>
460Fixed32 Message::Field::As<Fixed32>();
461
462template <>
463Sfixed32 Message::Field::As<Sfixed32>();
464
465template <>
466Uint64 Message::Field::As<Uint64>();
467
468template <>
469Int64 Message::Field::As<Int64>();
470
471template <>
472Sint64 Message::Field::As<Sint64>();
473
474template <>
475Fixed64 Message::Field::As<Fixed64>();
476
477template <>
478Sfixed64 Message::Field::As<Sfixed64>();
479
480template <>
481Float Message::Field::As<Float>();
482
483template <>
484Double Message::Field::As<Double>();
485
486template <>
487Bool Message::Field::As<Bool>();
488
489// A helper for parsing `repeated` field. It implements an iterator interface
490// that only iterates through the fields of a given `field_number`.
491//
492// For normal uses, the class should be created from `class Message`. See
493// comment for `class Message` for usage.
494template <typename FieldType>
496 public:
497 class iterator {
498 public:
499 // Precondition: iter_ is not pointing to the end.
500 iterator& operator++() {
501 iter_++;
502 MoveToNext();
503 return *this;
504 }
505
506 iterator operator++(int) {
507 iterator iter = *this;
508 this->operator++();
509 return iter;
510 }
511
512 bool ok() { return iter_.ok(); }
513 Status status() { return iter_.status(); }
514 FieldType operator*() { return current_; }
515 FieldType* operator->() { return &current_; }
516 bool operator!=(const iterator& other) const { return !(*this == other); }
517 bool operator==(const iterator& other) const {
518 return &host_ == &other.host_ && iter_ == other.iter_;
519 }
520
521 private:
522 RepeatedFieldParser& host_;
523 Message::iterator iter_;
524 FieldType current_ = FieldType(Status::Unavailable());
525
527 : host_(host),
528 iter_(std::move(init_iter)),
529 current_(Status::Unavailable()) {
530 // Move to the first element of the target field number.
531 MoveToNext();
532 }
533
534 void MoveToNext() {
535 // Move the iterator to the next element with the target field number
536 for (; iter_ != host_.message_.end(); ++iter_) {
537 if (!iter_.ok() || iter_->field_number() == host_.field_number_) {
538 current_ = iter_->As<FieldType>();
539 break;
540 }
541 }
542 }
543
544 friend class RepeatedFieldParser;
545 };
546
547 // `message` -- The containing message.
548 // `field_number` -- The field number of the repeated field.
549 RepeatedFieldParser(Message& message, uint32_t field_number)
550 : message_(message), field_number_(field_number) {}
551
552 RepeatedFieldParser(Status status) : message_(status) {}
553
554 bool ok() { return message_.ok(); }
555 Status status() { return message_.status(); }
556
557 iterator begin() { return iterator(*this, message_.begin()); }
558 iterator end() { return iterator(*this, message_.end()); }
559
560 private:
561 Message message_;
562 uint32_t field_number_ = 0;
563};
564
565// A helper for pasring the entry type of map<string, <value>>.
566// An entry for a proto map is essentially a message of a key(k=1) and
567// value(k=2) field, i.e.:
568//
569// message Entry {
570// string key = 1;
571// bytes value = 2;
572// }
573//
574// For normal uses, the class should be created from `class Message`. See
575// comment for `class Message` for usage.
576template <typename ValueParser>
578 public:
579 bool ok() { return entry_.ok(); }
580 Status status() { return entry_.status(); }
581 StringMapEntryParser(Status status) : entry_(status) {}
582 StringMapEntryParser(stream::IntervalReader reader) : entry_(reader) {}
583 String Key() { return entry_.AsString(kMapKeyFieldNumber); }
584 ValueParser Value() { return entry_.As<ValueParser>(kMapValueFieldNumber); }
585
586 private:
587 static constexpr uint32_t kMapKeyFieldNumber = 1;
588 static constexpr uint32_t kMapValueFieldNumber = 2;
589 Message entry_;
590};
591
592// A helper class for parsing a string-keyed map field. i.e. map<string,
593// <value>>. The template argument `ValueParser` indicates the type the value
594// will be parsed as, i.e. String, Bytes, Uint32, Message etc.
595//
596// For normal uses, the class should be created from `class Message`. See
597// comment for `class Message` for usage.
598template <typename ValueParser>
600 : public RepeatedFieldParser<StringMapEntryParser<ValueParser>> {
601 public:
603 StringMapEntryParser<ValueParser>>::RepeatedFieldParser;
604
605 // Operator overload for value access of a given key.
606 ValueParser operator[](std::string_view target) {
607 // Iterate over all entries and find the one whose key matches `target`
608 for (StringMapEntryParser<ValueParser> entry : *this) {
609 String key = entry.Key();
610 PW_TRY(key.status());
611
612 // Compare key value with the given string
613 Result<bool> cmp_res = key.Equal(target);
614 PW_TRY(cmp_res.status());
615 if (cmp_res.value()) {
616 return entry.Value();
617 }
618 }
619
620 return ValueParser(Status::NotFound());
621 }
622};
623
625
626} // namespace pw::protobuf
Definition: poll.h:25
Definition: status.h:109
constexpr bool ok() const
Definition: status.h:214
static constexpr Status Unavailable()
Requested operation can’t finish now, but may at a later time.
Definition: status.h:187
static constexpr Status NotFound()
The entity that the caller requested (e.g. file or directory) is not found.
Definition: status.h:141
Definition: message.h:101
Definition: message.h:111
Definition: message.h:96
Definition: message.h:56
Definition: message.h:81
Definition: message.h:91
Definition: message.h:46
Definition: message.h:71
Definition: message.h:254
Definition: message.h:296
Definition: message.h:252
Definition: message.h:495
Definition: message.h:61
Definition: message.h:86
Definition: message.h:51
Definition: message.h:76
Definition: stream_decoder.h:69
Definition: message.h:134
Definition: message.h:577
Definition: message.h:600
Definition: message.h:41
Definition: message.h:66
Definition: interval_reader.h:36
Definition: stream.h:400
#define PW_TRY(expr)
Returns early if expr is a non-OK Status or Result.
Definition: try.h:27
constexpr Status OkStatus()
Definition: status.h:297