• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1.. _module-pw_protobuf:
2
3===========
4pw_protobuf
5===========
6.. pigweed-module::
7   :name: pw_protobuf
8
9``pw_protobuf`` provides an expressive interface for encoding and decoding
10the Protocol Buffer wire format with a lightweight code and data footprint.
11
12.. note::
13
14   The protobuf module is a work in progress. Wire format encoding and decoding
15   is supported, though the APIs are not final. C++ code generation exists for
16   encoding and decoding, but not yet optimized for in-memory decoding.
17
18--------
19Overview
20--------
21Unlike protobuf libraries which require protobuf messages be represented by
22in-memory data structures, ``pw_protobuf`` provides a progressive flexible API
23that allows the user to choose the data storage format and tradeoffs most
24suitable for their product on top of the implementation.
25
26The API is designed in three layers, which can be freely intermixed with each
27other in your code, depending on point of use requirements:
28
291. Message Structures,
302. Per-Field Writers and Readers,
313. Direct Writers and Readers.
32
33This has a few benefits. The primary one is that it allows the core proto
34serialization and deserialization libraries to be relatively small.
35
36.. include:: size_report/protobuf_overview
37
38To demonstrate these layers, we use the following protobuf message definition
39in the examples:
40
41.. code-block:: protobuf
42
43   message Customer {
44     enum Status {
45       NEW = 1;
46       ACTIVE = 2;
47       INACTIVE = 3;
48     }
49     int32 age = 1;
50     string name = 2;
51     Status status = 3;
52   }
53
54And the following accompanying options file:
55
56.. code-block:: text
57
58   Customer.name max_size:32
59
60.. toctree::
61   :maxdepth: 1
62
63   size_report
64
65Message Structures
66==================
67The highest level API is based around message structures created through C++
68code generation, integrated with Pigweed's build system.
69
70This results in the following generated structure:
71
72.. code-block:: c++
73
74   enum class Customer::Status : uint32_t {
75     NEW = 1,
76     ACTIVE = 2,
77     INACTIVE = 3,
78
79     kNew = NEW,
80     kActive = ACTIVE,
81     kInactive = INACTIVE,
82   };
83
84   struct Customer::Message {
85     int32_t age;
86     pw::InlineString<32> name;
87     Customer::Status status;
88   };
89
90Which can be encoded with the code:
91
92.. code-block:: c++
93
94   #include "example_protos/customer.pwpb.h"
95
96   pw::Status EncodeCustomer(Customer::StreamEncoder& encoder) {
97     return encoder.Write({
98       age = 33,
99       name = "Joe Bloggs",
100       status = Customer::Status::INACTIVE
101     });
102   }
103
104And decoded into a struct with the code:
105
106.. code-block:: c++
107
108   #include "example_protos/customer.pwpb.h"
109
110   pw::Status DecodeCustomer(Customer::StreamDecoder& decoder) {
111     Customer::Message customer{};
112     PW_TRY(decoder.Read(customer));
113     // Read fields from customer
114     return pw::OkStatus();
115   }
116
117These structures can be moved, copied, and compared with each other for
118equality.
119
120The encoder and decoder code is generic and implemented in the core C++ module.
121A small overhead for each message type used in your code describes the structure
122to the generic encoder and decoders.
123
124Message comparison
125------------------
126Message structures implement ``operator==`` and ``operator!=`` for equality and
127inequality comparisons. However, these can only work on trivial scalar fields
128and fixed-size strings within the message. Fields using a callback are not
129considered in the comparison.
130
131To check if the equality operator of a generated message covers all fields,
132``pw_protobuf`` provides an ``IsTriviallyComparable`` function.
133
134.. code-block:: c++
135
136   template <typename Message>
137   constexpr bool IsTriviallyComparable<Message>();
138
139For example, given the following protobuf definitions:
140
141.. code-block:: protobuf
142
143   message Point {
144     int32 x = 1;
145     int32 y = 2;
146   }
147
148   message Label {
149     Point point = 1;
150     string label = 2;
151   }
152
153And the accompanying options file:
154
155.. code-block:: text
156
157   Label.label use_callback:true
158
159The ``Point`` message can be fully compared for equality, but ``Label`` cannot.
160``Label`` still defines an ``operator==``, but it ignores the ``label`` string.
161
162.. code-block:: c++
163
164   Point::Message one = {.x = 5, .y = 11};
165   Point::Message two = {.x = 5, .y = 11};
166
167   static_assert(pw::protobuf::IsTriviallyComparable<Point::Message>());
168   ASSERT_EQ(one, two);
169   static_assert(!pw::protobuf::IsTriviallyComparable<Label::Message>());
170
171Buffer Sizes
172------------
173Initializing a ``MemoryEncoder`` requires that you specify the size of the
174buffer to encode to. The code generation includes a ``kMaxEncodedSizeBytes``
175constant that represents the maximum encoded size of the protobuf message,
176excluding the contents of any field values which require a callback.
177
178.. code-block:: c++
179
180   #include "example_protos/customer.pwpb.h"
181
182   std::byte buffer[Customer::kMaxEncodedSizeBytes];
183   Customer::MemoryEncoder encoder(buffer);
184   const auto status = encoder.Write({
185     age = 22,
186     name = "Wolfgang Bjornson",
187     status = Customer::Status::ACTIVE
188   });
189
190   // Always check the encoder status or return values from Write calls.
191   if (!status.ok()) {
192     PW_LOG_INFO("Failed to encode proto; %s", encoder.status().str());
193   }
194
195In the above example, because the ``name`` field has a ``max_size`` specified
196in the accompanying options file, ``kMaxEncodedSizeBytes`` includes the maximum
197length of the value for that field.
198
199Where the maximum length of a field value is not known, indicated by the
200structure requiring a callback for that field, the constant includes
201all relevant overhead and only requires that you add the length of the field
202values.
203
204For example if a ``bytes`` field length is not specified in the options file,
205but is known to your code (``kMaxImageDataSize`` in this example being a
206constant in your own code), you can simply add it to the generated constant:
207
208.. code-block:: c++
209
210   #include "example_protos/store.pwpb.h"
211
212   const std::byte image_data[kMaxImageDataSize] = { ... };
213
214   Store::Message store{};
215   // Calling SetEncoder means we must always extend the buffer size.
216   store.image_data.SetEncoder([](Store::StreamEncoder& encoder) {
217     return encoder.WriteImageData(image_data);
218   });
219
220   std::byte buffer[Store::kMaxEncodedSizeBytes + kMaxImageDataSize];
221   Store::MemoryEncoder encoder(buffer);
222   const auto status = encoder.Write(store);
223
224   // Always check the encoder status or return values from Write calls.
225   if (!status.ok()) {
226     PW_LOG_INFO("Failed to encode proto; %s", encoder.status().str());
227   }
228
229Or when using a variable number of repeated submessages, where the maximum
230number is known to your code but not to the proto, you can add the constants
231from one message type to another:
232
233.. code-block:: c++
234
235   #include "example_protos/person.pwpb.h"
236
237   Person::Message grandchild{};
238   // Calling SetEncoder means we must always extend the buffer size.
239   grandchild.grandparent.SetEncoder([](Person::StreamEncoder& encoder) {
240     PW_TRY(encoder.GetGrandparentEncoder().Write(maternal_grandma));
241     PW_TRY(encoder.GetGrandparentEncoder().Write(maternal_grandpa));
242     PW_TRY(encoder.GetGrandparentEncoder().Write(paternal_grandma));
243     PW_TRY(encoder.GetGrandparentEncoder().Write(paternal_grandpa));
244     return pw::OkStatus();
245   });
246
247   std::byte buffer[Person::kMaxEncodedSizeBytes +
248                    Grandparent::kMaxEncodedSizeBytes * 4];
249   Person::MemoryEncoder encoder(buffer);
250   const auto status = encoder.Write(grandchild);
251
252   // Always check the encoder status or return values from Write calls.
253   if (!status.ok()) {
254     PW_LOG_INFO("Failed to encode proto; %s", encoder.status().str());
255   }
256
257.. warning::
258  Encoding to a buffer that is insufficiently large will return
259  ``Status::ResourceExhausted()`` from ``Write`` calls, and from the
260  encoder's ``status()`` call. Always check the status of calls or the encoder,
261  as in the case of error, the encoded data will be invalid.
262
263Per-Field Writers and Readers
264=============================
265The middle level API is based around typed methods to write and read each
266field of the message directly to the final serialized form, again created
267through C++ code generation.
268
269Encoding
270--------
271Given the same message structure, in addition to the ``Write()`` method that
272accepts a message structure, the following additional methods are also
273generated in the typed ``StreamEncoder`` class.
274
275There are lightweight wrappers around the core implementation, calling the
276underlying methods with the correct field numbers and value types, and result
277in no additional binary code over correctly using the core implementation.
278
279.. code-block:: c++
280
281   class Customer::StreamEncoder : pw::protobuf::StreamEncoder {
282    public:
283     // Message Structure Writer.
284     pw::Status Write(const Customer::Message&);
285
286     // Per-Field Typed Writers.
287     pw::Status WriteAge(int32_t);
288
289     pw::Status WriteName(std::string_view);
290     pw::Status WriteName(const char*, size_t);
291
292     pw::Status WriteStatus(Customer::Status);
293   };
294
295So the same encoding method could be written as:
296
297.. code-block:: c++
298
299   #include "example_protos/customer.pwpb.h"
300
301   Status EncodeCustomer(Customer::StreamEncoder& encoder) {
302     PW_TRY(encoder.WriteAge(33));
303     PW_TRY(encoder.WriteName("Joe Bloggs"sv));
304     PW_TRY(encoder.WriteStatus(Customer::Status::INACTIVE));
305   }
306
307Pigweed's protobuf encoders encode directly to the wire format of a proto rather
308than staging information to a mutable datastructure. This means any writes of a
309value are final, and can't be referenced or modified as a later step in the
310encode process.
311
312Casting between generated StreamEncoder types
313^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
314pw_protobuf guarantees that all generated ``StreamEncoder`` classes can be
315converted among each other. It's also safe to convert any ``MemoryEncoder`` to
316any other ``StreamEncoder``.
317
318This guarantee exists to facilitate usage of protobuf overlays. Protobuf
319overlays are protobuf message definitions that deliberately ensure that
320fields defined in one message will not conflict with fields defined in other
321messages.
322
323For example:
324
325.. code-block:: protobuf
326
327   // The first half of the overlaid message.
328   message BaseMessage {
329     uint32 length = 1;
330     reserved 2;  // Reserved for Overlay
331   }
332
333   // OK: The second half of the overlaid message.
334   message Overlay {
335     reserved 1;  // Reserved for BaseMessage
336     uint32 height = 2;
337   }
338
339   // OK: A message that overlays and bundles both types together.
340   message Both {
341     uint32 length = 1;  // Defined independently by BaseMessage
342     uint32 height = 2;  // Defined independently by Overlay
343   }
344
345   // BAD: Diverges from BaseMessage's definition, and can cause decode
346   // errors/corruption.
347   message InvalidOverlay {
348     fixed32 length = 1;
349   }
350
351The ``StreamEncoderCast<>()`` helper template reduces very messy casting into
352a much easier to read syntax:
353
354.. code-block:: c++
355
356   #include "pw_protobuf/encoder.h"
357   #include "pw_protobuf_test_protos/full_test.pwpb.h"
358
359   Result<ConstByteSpan> EncodeOverlaid(uint32_t height,
360                                        uint32_t length,
361                                        ConstByteSpan encode_buffer) {
362     BaseMessage::MemoryEncoder base(encode_buffer);
363
364     // Without StreamEncoderCast<>(), this line would be:
365     //   Overlay::StreamEncoder& overlay =
366     //       *static_cast<Overlay::StreamEncoder*>(
367     //           static_cast<pw::protobuf::StreamEncoder*>(&base)
368     Overlay::StreamEncoder& overlay =
369         StreamEncoderCast<Overlay::StreamEncoder>(base);
370     if (!overlay.WriteHeight(height).ok()) {
371       return overlay.status();
372     }
373     if (!base.WriteLength(length).ok()) {
374       return base.status();
375     }
376     return ConstByteSpan(base);
377   }
378
379While this use case is somewhat uncommon, it's a core supported use case of
380pw_protobuf.
381
382.. warning::
383
384   Using this to convert one stream encoder to another when the messages
385   themselves do not safely overlay will result in corrupt protos. Be careful
386   when doing this as there's no compile-time way to detect whether or not two
387   messages are meant to overlay.
388
389Decoding
390--------
391For decoding, in addition to the ``Read()`` method that populates a message
392structure, the following additional methods are also generated in the typed
393``StreamDecoder`` class.
394
395.. code-block:: c++
396
397   class Customer::StreamDecoder : pw::protobuf::StreamDecoder {
398    public:
399     // Message Structure Reader.
400     pw::Status Read(Customer::Message&);
401
402     // Returns the identity of the current field.
403     ::pw::Result<Fields> Field();
404
405     // Per-Field Typed Readers.
406     pw::Result<int32_t> ReadAge();
407
408     pw::StatusWithSize ReadName(pw::span<char>);
409     BytesReader GetNameReader(); // Read name as a stream of bytes.
410
411     pw::Result<Customer::Status> ReadStatus();
412   };
413
414Complete and correct decoding requires looping through the fields, so is more
415complex than encoding or using the message structure.
416
417.. code-block:: c++
418
419   pw::Status DecodeCustomer(Customer::StreamDecoder& decoder) {
420     uint32_t age;
421     char name[32];
422     Customer::Status status;
423
424     while ((status = decoder.Next()).ok()) {
425       switch (decoder.Field().value()) {
426         case Customer::Fields::kAge: {
427           PW_TRY_ASSIGN(age, decoder.ReadAge());
428           break;
429         }
430         case Customer::Fields::kName: {
431           PW_TRY(decoder.ReadName(name));
432           break;
433         }
434         case Customer::Fields::kStatus: {
435           PW_TRY_ASSIGN(status, decoder.ReadStatus());
436           break;
437         }
438       }
439     }
440
441     return status.IsOutOfRange() ? OkStatus() : status;
442   }
443
444.. warning:: ``Fields::SNAKE_CASE`` is deprecated. Use ``Fields::kCamelCase``.
445
446   Transitional support for ``Fields::SNAKE_CASE`` will soon only be available by
447   explicitly setting the following GN variable in your project:
448   ``pw_protobuf_compiler_GENERATE_LEGACY_ENUM_SNAKE_CASE_NAMES=true``
449
450   This support will be removed after downstream projects have been migrated.
451
452
453Reading a single field
454----------------------
455Sometimes, only a single field from a serialized message needs to be read. In
456these cases, setting up a decoder and iterating through the message is a lot of
457boilerplate. ``pw_protobuf`` generates convenient ``Find*()`` functions for
458most fields in a message which handle this for you.
459
460.. code-block:: c++
461
462   pw::Status ReadCustomerData(pw::ConstByteSpan serialized_customer) {
463     pw::Result<uint32_t> age = Customer::FindAge(serialized_customer);
464     if (!age.ok()) {
465       return age.status();
466     }
467
468     // This will scan the buffer again from the start, which is less efficient
469     // than writing a custom decoder loop.
470     pw::Result<std::string_view> name = Customer::FindName(serialized_customer);
471     if (!age.ok()) {
472       return age.status();
473     }
474
475     DoStuff(age, name);
476     return pw::OkStatus();
477   }
478
479The ``Find`` APIs also work with streamed data, as shown below.
480
481.. code-block:: c++
482
483   pw::Status ReadCustomerData(pw::stream::Reader& customer_stream) {
484     pw::Result<uint32_t> age = Customer::FindAge(customer_stream);
485     if (!age.ok()) {
486       return age.status();
487     }
488
489     // This will begin scanning for `name` from the current position of the
490     // stream (following the `age` field). If `name` appeared before `age` in
491     // the serialized data, it will not be found.
492     //
493     // Note that unlike with the buffer APIs, stream Find methods copy `string`
494     // and `bytes` fields into a user-provided buffer.
495     char name[32];
496     pw::StatusWithSize sws = Customer::FindName(serialized_customer, name);
497     if (!sws.ok()) {
498       return sws.status();
499     }
500     if (sws.size() >= sizeof(name)) {
501       return pw::Status::OutOfRange();
502     }
503     name[sws.size()] = '\0';
504
505     DoStuff(age, name);
506     return pw::OkStatus();
507   }
508
509.. note::
510
511   Each call to ``Find*()`` linearly scans through the message. If you have to
512   read multiple fields, it is more efficient to instantiate your own decoder as
513   described above. Additionally, to avoid confusion, ``Find*()`` methods are
514   not generated for repeated fields.
515
516
517Direct Writers and Readers
518==========================
519The lowest level API is provided by the core C++ implementation, and requires
520the caller to provide the correct field number and value types for encoding, or
521check the same when decoding.
522
523Encoding
524--------
525The two fundamental classes are ``MemoryEncoder`` which directly encodes a proto
526to an in-memory buffer, and ``StreamEncoder`` that operates on
527``pw::stream::Writer`` objects to serialize proto data.
528
529``StreamEncoder`` allows you encode a proto to something like ``pw::sys_io``
530without needing to build the complete message in memory
531
532To encode the same message we've used in the examples thus far, we would use
533the following parts of the core API:
534
535.. code-block:: c++
536
537   class pw::protobuf::StreamEncoder {
538    public:
539     Status WriteInt32(uint32_t field_number, int32_t);
540     Status WriteUint32(uint32_t field_number, uint32_t);
541
542     Status WriteString(uint32_t field_number, std::string_view);
543     Status WriteString(uint32_t field_number, const char*, size_t);
544
545     // And many other methods, see pw_protobuf/encoder.h
546   };
547
548Encoding the same message requires that we specify the field numbers, which we
549can hardcode, or supplement using the C++ code generated ``Fields`` enum, and
550cast the enumerated type.
551
552.. code-block:: c++
553
554   #include "pw_protobuf/encoder.h"
555   #include "example_protos/customer.pwpb.h"
556
557   Status EncodeCustomer(pw::protobuf::StreamEncoder& encoder) {
558     PW_TRY(encoder.WriteInt32(static_cast<uint32_t>(Customer::Fields::kAge),
559                               33));
560     PW_TRY(encoder.WriteString(static_cast<uint32_t>(Customer::Fields::kName),
561                                "Joe Bloggs"sv));
562     PW_TRY(encoder.WriteUint32(
563         static_cast<uint32_t>(Customer::Fields::kStatus),
564         static_cast<uint32_t>(Customer::Status::INACTIVE)));
565   }
566
567Decoding
568--------
569``StreamDecoder`` reads data from a ``pw::stream::Reader`` and mirrors the API
570of the encoders.
571
572To decode the same message we would use the following parts of the core API:
573
574.. code-block:: c++
575
576   class pw::protobuf::StreamDecoder {
577    public:
578     // Returns the identity of the current field.
579     ::pw::Result<uint32_t> FieldNumber();
580
581     Result<int32_t> ReadInt32();
582     Result<uint32_t> ReadUint32();
583
584     StatusWithSize ReadString(pw::span<char>);
585
586     // And many other methods, see pw_protobuf/stream_decoder.h
587   };
588
589As with the typed per-field API, complete and correct decoding requires looping
590through the fields and checking the field numbers, along with casting types.
591
592.. code-block:: c++
593
594   pw::Status DecodeCustomer(pw::protobuf::StreamDecoder& decoder) {
595     uint32_t age;
596     char name[32];
597     Customer::Status status;
598
599     while ((status = decoder.Next()).ok()) {
600       switch (decoder.FieldNumber().value()) {
601         case static_cast<uint32_t>(Customer::Fields::kAge): {
602           PW_TRY_ASSIGN(age, decoder.ReadInt32());
603           break;
604         }
605         case static_cast<uint32_t>(Customer::Fields::kName): {
606           PW_TRY(decoder.ReadString(name));
607           break;
608         }
609         case static_cast<uint32_t>(Customer::Fields::kStatus): {
610           uint32_t status_value;
611           PW_TRY_ASSIGN(status_value, decoder.ReadUint32());
612           status = static_cast<Customer::Status>(status_value);
613           break;
614         }
615       }
616     }
617
618     return status.IsOutOfRange() ? OkStatus() : status;
619   }
620
621Find APIs
622---------
623
624.. doxygenfile:: pw_protobuf/public/pw_protobuf/find.h
625
626
627Handling of packages
628====================
629
630Package declarations in ``.proto`` files are converted to namespace
631declarations. Unlike ``protoc``'s native C++ codegen, pw_protobuf appends an
632additional ``::pwpb`` namespace after the user-specified package name: for
633example, ``package my.cool.project`` becomes ``namespace
634my::cool::project::pwpb``. We emit a different package name than stated, in
635order to avoid clashes for projects that link against multiple C++ proto
636libraries in the same library.
637
638..
639  TODO: b/258832150 - Remove this section, if possible
640
641In some cases, pw_protobuf codegen may encounter external message references
642during parsing, where it is unable to resolve the package name of the message.
643In these situations, the codegen is instead forced to emit the package name as
644``pw::pwpb_xxx::my::cool::project``, where "pwpb_xxx" is the name of some
645unspecified private namespace. Users are expected to manually identify the
646intended namespace name of that symbol, as described above, and must not rely
647on any such private namespaces, even if they appear in codegen output.
648
649-------
650Codegen
651-------
652pw_protobuf codegen integration is supported in GN, Bazel, and CMake.
653
654This module's codegen is available through the ``*.pwpb`` sub-target of a
655``pw_proto_library`` in GN, CMake, and Bazel. See :ref:`pw_protobuf_compiler's
656documentation <module-pw_protobuf_compiler>` for more information on build
657system integration for pw_protobuf codegen.
658
659Example ``BUILD.gn``:
660
661.. code-block::
662
663   import("//build_overrides/pigweed.gni")
664
665   import("$dir_pw_build/target_types.gni")
666   import("$dir_pw_protobuf_compiler/proto.gni")
667
668   # This target controls where the *.pwpb.h headers end up on the include path.
669   # In this example, it's at "pet_daycare_protos/client.pwpb.h".
670   pw_proto_library("pet_daycare_protos") {
671     sources = [
672       "pet_daycare_protos/client.proto",
673     ]
674   }
675
676   pw_source_set("example_client") {
677     sources = [ "example_client.cc" ]
678     deps = [
679       ":pet_daycare_protos.pwpb",
680       dir_pw_bytes,
681       dir_pw_stream,
682     ]
683   }
684
685-------------
686Configuration
687-------------
688``pw_protobuf`` supports the following configuration options.
689
690* ``PW_PROTOBUF_CFG_MAX_VARINT_SIZE``:
691  When encoding nested messages, the number of bytes to reserve for the varint
692  submessage length. Nested messages are limited in size to the maximum value
693  that can be varint-encoded into this reserved space.
694
695  The values that can be set, and their corresponding maximum submessage
696  lengths, are outlined below.
697
698  +-------------------+----------------------------------------+
699  | MAX_VARINT_SIZE   | Maximum submessage length              |
700  +===================+========================================+
701  | 1 byte            | 127                                    |
702  +-------------------+----------------------------------------+
703  | 2 bytes           | 16,383 or < 16KiB                      |
704  +-------------------+----------------------------------------+
705  | 3 bytes           | 2,097,151 or < 2048KiB                 |
706  +-------------------+----------------------------------------+
707  | 4 bytes (default) | 268,435,455 or < 256MiB                |
708  +-------------------+----------------------------------------+
709  | 5 bytes           | 4,294,967,295 or < 4GiB (max uint32_t) |
710  +-------------------+----------------------------------------+
711
712Field Options
713=============
714``pw_protobuf`` supports the following field options for specifying
715protocol-level limitations, rather than code generation parameters (although
716they do influence code generation):
717
718
719* ``max_count``:
720  Maximum number of entries for repeated fields.
721
722* ``max_size``:
723  Maximum size of `bytes` or `string` fields.
724
725Even though other proto codegen implementations do not respect these field
726options, they can still compile protos which use these options. This is
727especially useful for host builds using upstream protoc code generation, where
728host software can use the reflection API to query for the options and validate
729messages comply with the specified limitations.
730
731.. code-block:: text
732
733   import "pw_protobuf_protos/field_options.proto";
734
735   message Demo {
736     string size_limited_string = 1 [(pw.protobuf.pwpb).max_size = 16];
737   };
738
739Options Files
740=============
741Code generation can be configured using a separate ``.options`` file placed
742alongside the relevant ``.proto`` file.
743
744The format of this file is a series of fully qualified field names, or patterns,
745followed by one or more options. Lines starting with ``#`` or ``//`` are
746comments, and blank lines are ignored.
747
748Example:
749
750.. code-block::
751
752   // Set an option for a specific field.
753   fuzzy_friends.Client.visit_dates max_count:16
754
755   // Set options for multiple fields by wildcard matching.
756   fuzzy_friends.Pet.* max_size:32
757
758   // Set multiple options in one go.
759   fuzzy_friends.Dog.paws max_count:4 fixed_count:true
760
761Options files should be listed as ``inputs`` when defining ``pw_proto_library``,
762e.g.
763
764.. code-block::
765
766   pw_proto_library("pet_daycare_protos") {
767     sources = [
768       "pet_daycare_protos/client.proto",
769     ]
770     inputs = [
771       "pet_daycare_protos/client.options",
772     ]
773   }
774
775Valid options are:
776
777* ``max_count``:
778  Maximum number of entries for repeated fields. When set, repeated scalar
779  fields will use the ``pw::Vector`` container type instead of a callback.
780
781* ``fixed_count``:
782  Specified with ``max_count`` to use a fixed length ``std::array`` container
783  instead of ``pw::Vector``.
784
785* ``max_size``:
786  Maximum size of `bytes` or `string` fields. When set, `bytes` fields use
787  ``pw::Vector`` and `string` fields use ``pw::InlineString`` instead of a
788  callback.
789
790* ``fixed_size``:
791  Specified with ``max_size`` to use a fixed length ``std::array`` container
792  instead of ``pw::Vector`` for `bytes` fields.
793
794* ``use_callback``:
795  Replaces the structure member for the field with a callback function even
796  where a simpler type could be used. This can be useful to ignore fields, to
797  stop decoding of complex structures if certain values are not as expected, or
798  to provide special handling for nested messages.
799
800.. admonition:: Rationale
801
802  The choice of a separate options file, over embedding options within the proto
803  file, are driven by the need for proto files to be shared across multiple
804  contexts.
805
806  A typical product would require the same proto be used on a hardware
807  component, running Pigweed; a server-side component, running on a cloud
808  platform; and an app component, running on a Phone OS.
809
810  While related, each of these will likely have different source projects and
811  build systems.
812
813  Were the Pigweed options embedded in the protos, it would be necessary for
814  both the cloud platform and Phone OS to be able to ``"import pigweed"`` ---
815  and equivalently for options relevant to their platforms in the embedded
816  software project.
817
818------------------
819Message Structures
820------------------
821The C++ code generator creates a ``struct Message`` for each protobuf message
822that can hold the set of values encoded by it, following these rules.
823
824* Scalar fields are represented by their appropriate C++ type.
825
826  .. code-block:: protobuf
827
828     message Customer {
829       int32 age = 1;
830       uint32 birth_year = 2;
831       sint64 rating = 3;
832       bool is_active = 4;
833     }
834
835  .. code-block:: c++
836
837     struct Customer::Message {
838       int32_t age;
839       uint32_t birth_year;
840       int64_t rating;
841       bool is_active;
842     };
843
844* Enumerations are represented by a code generated namespaced proto enum.
845
846  .. code-block:: protobuf
847
848     message Award {
849       enum Service {
850         BRONZE = 1;
851         SILVER = 2;
852         GOLD = 3;
853       }
854       Service service = 1;
855     }
856
857  .. code-block:: c++
858
859     enum class Award::Service : uint32_t {
860       BRONZE = 1,
861       SILVER = 2,
862       GOLD = 3,
863
864       kBronze = BRONZE,
865       kSilver = SILVER,
866       kGold = GOLD,
867     };
868
869     struct Award::Message {
870       Award::Service service;
871     };
872
873  Aliases to the enum values are also included in the "constant" style to match
874  your preferred coding style. These aliases have any common prefix to the
875  enumeration values removed, such that:
876
877  .. code-block:: protobuf
878
879     enum Activity {
880       ACTIVITY_CYCLING = 1;
881       ACTIVITY_RUNNING = 2;
882       ACTIVITY_SWIMMING = 3;
883     }
884
885  .. code-block:: c++
886
887     enum class Activity : uint32_t {
888       ACTIVITY_CYCLING = 1,
889       ACTIVITY_RUNNING = 2,
890       ACTIVITY_SWIMMING = 3,
891
892       kCycling = ACTIVITY_CYCLING,
893       kRunning = ACTIVITY_RUNNING,
894       kSwimming = ACTIVITY_SWIMMING,
895     };
896
897
898* Nested messages are represented by their own ``struct Message`` provided that
899  a reference cycle does not exist.
900
901  .. code-block:: protobuf
902
903     message Sale {
904       Customer customer = 1;
905       Product product = 2;
906     }
907
908  .. code-block:: c++
909
910     struct Sale::Message {
911       Customer::Message customer;
912       Product::Message product;
913     };
914
915* Optional scalar fields are represented by the appropriate C++ type wrapped in
916  ``std::optional``. Optional fields are not encoded when the value is not
917  present.
918
919  .. code-block:: protobuf
920
921     message Loyalty {
922       optional int32 points = 1;
923     }
924
925  .. code-block:: c++
926
927     struct Loyalty::Message {
928       std::optional<int32_t> points;
929     };
930
931* Repeated scalar fields are represented by ``pw::Vector`` when the
932  ``max_count`` option is set for that field, or by ``std::array`` when both
933  ``max_count`` and ``fixed_count:true`` are set.
934
935  The max count is exposed as an UpperCamelCase constant ``k{FieldName}MaxSize``.
936
937  .. code-block:: protobuf
938
939     message Register {
940       repeated int32 cash_in = 1;
941       repeated int32 cash_out = 2;
942     }
943
944  .. code-block:: text
945
946     Register.cash_in max_count:32 fixed_count:true
947     Register.cash_out max_count:64
948
949  .. code-block:: c++
950
951     namespace Register {
952       static constexpr size_t kCashInMaxSize = 32;
953       static constexpr size_t kCashOutMaxSize = 64;
954     }
955
956     struct Register::Message {
957       std::array<int32_t, kCashInMaxSize> cash_in;
958       pw::Vector<int32_t, kCashOutMaxSize> cash_out;
959     };
960
961* `bytes` fields are represented by ``pw::Vector`` when the ``max_size`` option
962  is set for that field, or by ``std::array`` when both ``max_size`` and
963  ``fixed_size:true`` are set.
964
965  The max size is exposed as an UpperCamelCase constant ``k{FieldName}MaxSize``.
966
967  .. code-block:: protobuf
968
969     message Product {
970       bytes sku = 1;
971       bytes serial_number = 2;
972     }
973
974  .. code-block:: text
975
976     Product.sku max_size:8 fixed_size:true
977     Product.serial_number max_size:64
978
979  .. code-block:: c++
980
981     namespace Product {
982       static constexpr size_t kSkuMaxSize = 8;
983       static constexpr size_t kSerialNumberMaxSize = 64;
984     }
985
986     struct Product::Message {
987       std::array<std::byte, kSkuMaxSize> sku;
988       pw::Vector<std::byte, kSerialNumberMaxSize> serial_number;
989     };
990
991* `string` fields are represented by a :cpp:type:`pw::InlineString` when the
992  ``max_size`` option is set for that field. The string can hold up to
993  ``max_size`` characters, and is always null terminated. The null terminator is
994  not counted in ``max_size``.
995
996  The max size is exposed as an UpperCamelCase constant ``k{FieldName}MaxSize``.
997
998  .. code-block:: protobuf
999
1000     message Employee {
1001       string name = 1;
1002     }
1003
1004  .. code-block:: text
1005
1006     Employee.name max_size:128
1007
1008  .. code-block:: c++
1009
1010     namespace Employee {
1011       static constexpr size_t kNameMaxSize = 128;
1012     }
1013
1014     struct Employee::Message {
1015       pw::InlineString<kNameMaxSize> name;
1016     };
1017
1018* Nested messages with a dependency cycle, repeated scalar fields without a
1019  ``max_count`` option set, `bytes` and `strings` fields without a ``max_size``
1020  option set, and repeated nested messages, repeated `bytes`, and repeated
1021  `strings` fields, are represented by a callback.
1022
1023  You set the callback to a custom function for encoding or decoding
1024  before passing the structure to ``Write()`` or ``Read()`` appropriately.
1025
1026  .. code-block:: protobuf
1027
1028     message Store {
1029       Store nearest_store = 1;
1030       repeated int32 employee_numbers = 2;
1031       string driections = 3;
1032       repeated string address = 4;
1033       repeated Employee employees = 5;
1034     }
1035
1036  .. code-block::
1037
1038     // No options set.
1039
1040  .. code-block:: c++
1041
1042     struct Store::Message {
1043       pw::protobuf::Callback<Store::StreamEncoder, Store::StreamDecoder> nearest_store;
1044       pw::protobuf::Callback<Store::StreamEncoder, Store::StreamDecoder> employee_numbers;
1045       pw::protobuf::Callback<Store::StreamEncoder, Store::StreamDecoder> directions;
1046       pw::protobuf::Callback<Store::StreamEncoder, Store::StreamDecoder> address;
1047       pw::protobuf::Callback<Store::StreamEncoder, Store::StreamDecoder> employees;
1048     };
1049
1050  A Callback object can be converted to a ``bool`` indicating whether a callback
1051  is set.
1052
1053Message structures can be copied, but doing so will clear any assigned
1054callbacks. To preserve functions applied to callbacks, ensure that the message
1055structure is moved.
1056
1057Message structures can also be compared with each other for equality. This
1058includes all repeated and nested fields represented by value types, but does not
1059compare any field represented by a callback.
1060
1061Reserved-Word Conflicts
1062=======================
1063Generated symbols whose names conflict with reserved C++ keywords or
1064standard-library macros are suffixed with underscores to avoid compilation
1065failures. This can be seen below in ``Channel.operator``, which is mapped to
1066``Channel::Message::operator_`` to avoid conflicting with the ``operator``
1067keyword.
1068
1069.. code-block:: protobuf
1070
1071   message Channel {
1072     int32 bitrate = 1;
1073     float signal_to_noise_ratio = 2;
1074     Company operator = 3;
1075   }
1076
1077.. code-block:: c++
1078
1079   struct Channel::Message {
1080     int32_t bitrate;
1081     float signal_to_noise_ratio;
1082     Company::Message operator_;
1083   };
1084
1085Similarly, as shown in the example below, some POSIX-signal names conflict with
1086macros defined by the standard-library header ``<csignal>`` and therefore
1087require underscore suffixes in the generated code. Note, however, that some
1088signal names are left alone. This is because ``<csignal>`` only defines a subset
1089of the POSIX signals as macros; the rest are perfectly valid identifiers that
1090won't cause any problems unless the user defines custom macros for them. Any
1091naming conflicts caused by user-defined macros are the user's responsibility
1092(https://google.github.io/styleguide/cppguide.html#Preprocessor_Macros).
1093
1094.. code-block:: protobuf
1095
1096   enum PosixSignal {
1097     NONE = 0;
1098     SIGHUP = 1;
1099     SIGINT = 2;
1100     SIGQUIT = 3;
1101     SIGILL = 4;
1102     SIGTRAP = 5;
1103     SIGABRT = 6;
1104     SIGFPE = 8;
1105     SIGKILL = 9;
1106     SIGSEGV = 11;
1107     SIGPIPE = 13;
1108     SIGALRM = 14;
1109     SIGTERM = 15;
1110   }
1111
1112.. code-block:: c++
1113
1114   enum class PosixSignal : uint32_t {
1115     NONE = 0,
1116     SIGHUP = 1,
1117     SIGINT_ = 2,
1118     SIGQUIT = 3,
1119     SIGILL_ = 4,
1120     SIGTRAP = 5,
1121     SIGABRT_ = 6,
1122     SIGFPE_ = 8,
1123     SIGKILL = 9,
1124     SIGSEGV_ = 11,
1125     SIGPIPE = 13,
1126     SIGALRM = 14,
1127     SIGTERM_ = 15,
1128
1129     kNone = NONE,
1130     kSighup = SIGHUP,
1131     kSigint = SIGINT_,
1132     kSigquit = SIGQUIT,
1133     kSigill = SIGILL_,
1134     kSigtrap = SIGTRAP,
1135     kSigabrt = SIGABRT_,
1136     kSigfpe = SIGFPE_,
1137     kSigkill = SIGKILL,
1138     kSigsegv = SIGSEGV_,
1139     kSigpipe = SIGPIPE,
1140     kSigalrm = SIGALRM,
1141     kSigterm = SIGTERM_,
1142   };
1143
1144Much like reserved words and macros, the names ``Message`` and ``Fields`` are
1145suffixed with underscores in generated C++ code. This is to prevent name
1146conflicts with the codegen internals if they're used in a nested context as in
1147the example below.
1148
1149.. code-block:: protobuf
1150
1151   message Function {
1152     message Message {
1153       string content = 1;
1154     }
1155
1156     enum Fields {
1157       NONE = 0;
1158       COMPLEX_NUMBERS = 1;
1159       INTEGERS_MOD_5 = 2;
1160       MEROMORPHIC_FUNCTIONS_ON_COMPLEX_PLANE = 3;
1161       OTHER = 4;
1162     }
1163
1164     Message description = 1;
1165     Fields domain = 2;
1166     Fields codomain = 3;
1167   }
1168
1169.. code-block::
1170
1171   Function.Message.content max_size:128
1172
1173.. code-block:: c++
1174
1175   struct Function::Message_::Message {
1176     pw::InlineString<128> content;
1177   };
1178
1179   enum class Function::Message_::Fields : uint32_t {
1180     CONTENT = 1,
1181   };
1182
1183   enum class Function::Fields_ uint32_t {
1184     NONE = 0,
1185     COMPLEX_NUMBERS = 1,
1186     INTEGERS_MOD_5 = 2,
1187     MEROMORPHIC_FUNCTIONS_ON_COMPLEX_PLANE = 3,
1188     OTHER = 4,
1189
1190     kNone = NONE,
1191     kComplexNumbers = COMPLEX_NUMBERS,
1192     kIntegersMod5 = INTEGERS_MOD_5,
1193     kMeromorphicFunctionsOnComplexPlane =
1194         MEROMORPHIC_FUNCTIONS_ON_COMPLEX_PLANE,
1195     kOther = OTHER,
1196   };
1197
1198   struct Function::Message {
1199     Function::Message_::Message description;
1200     Function::Fields_ domain;
1201     Function::Fields_ codomain;
1202   };
1203
1204   enum class Function::Fields : uint32_t {
1205     DESCRIPTION = 1,
1206     DOMAIN = 2,
1207     CODOMAIN = 3,
1208   };
1209
1210.. warning::
1211   Note that the C++ spec also reserves two categories of identifiers for the
1212   compiler to use in ways that may conflict with generated code:
1213
1214   * Any identifier that contains two consecutive underscores anywhere in it.
1215   * Any identifier that starts with an underscore followed by a capital letter.
1216
1217   Appending underscores to symbols in these categories wouldn't change the fact
1218   that they match patterns reserved for the compiler, so the codegen does not
1219   currently attempt to fix them. Such names will therefore result in
1220   non-portable code that may or may not work depending on the compiler. These
1221   naming patterns are of course strongly discouraged in any protobufs that will
1222   be used with ``pw_protobuf`` codegen.
1223
1224Overhead
1225========
1226A single encoder and decoder is used for these structures, with a one-time code
1227cost. When the code generator creates the ``struct Message``, it also creates
1228a description of this structure that the shared encoder and decoder use.
1229
1230The cost of this description is a shared table for each protobuf message
1231definition used, with four words per field within the protobuf message, and an
1232addition word to store the size of the table.
1233
1234--------
1235Encoding
1236--------
1237The simplest way to use ``MemoryEncoder`` to encode a proto is from its code
1238generated ``Message`` structure into an in-memory buffer.
1239
1240.. code-block:: c++
1241
1242   #include "my_protos/my_proto.pwpb.h"
1243   #include "pw_bytes/span.h"
1244   #include "pw_protobuf/encoder.h"
1245   #include "pw_status/status_with_size.h"
1246
1247   // Writes a proto response to the provided buffer, returning the encode
1248   // status and number of bytes written.
1249   pw::StatusWithSize WriteProtoResponse(pw::ByteSpan response) {
1250     MyProto::Message message{}
1251     message.magic_number = 0x1a1a2b2b;
1252     message.favorite_food = "cookies";
1253     message.calories = 600;
1254
1255     // All proto writes are directly written to the `response` buffer.
1256     MyProto::MemoryEncoder encoder(response);
1257     encoder.Write(message);
1258
1259     return pw::StatusWithSize(encoder.status(), encoder.size());
1260   }
1261
1262All fields of a message are written, including those initialized to their
1263default values.
1264
1265Alternatively, for example if only a subset of fields are required to be
1266encoded, fields can be written a field at a time through the code generated
1267or lower-level APIs. This can be more convenient if finer grained control or
1268other custom handling is required.
1269
1270.. code-block:: c++
1271
1272   #include "my_protos/my_proto.pwpb.h"
1273   #include "pw_bytes/span.h"
1274   #include "pw_protobuf/encoder.h"
1275   #include "pw_status/status_with_size.h"
1276
1277   // Writes a proto response to the provided buffer, returning the encode
1278   // status and number of bytes written.
1279   pw::StatusWithSize WriteProtoResponse(pw::ByteSpan response) {
1280     // All proto writes are directly written to the `response` buffer.
1281     MyProto::MemoryEncoder encoder(response);
1282     encoder.WriteMagicNumber(0x1a1a2b2b);
1283     encoder.WriteFavoriteFood("cookies");
1284     // Only conditionally write calories.
1285     if (on_diet) {
1286       encoder.WriteCalories(600);
1287     }
1288     return pw::StatusWithSize(encoder.status(), encoder.size());
1289   }
1290
1291StreamEncoder
1292=============
1293``StreamEncoder`` is constructed with the destination stream, and a scratch
1294buffer used to handle nested submessages.
1295
1296.. code-block:: c++
1297
1298   #include "my_protos/my_proto.pwpb.h"
1299   #include "pw_bytes/span.h"
1300   #include "pw_protobuf/encoder.h"
1301   #include "pw_stream/sys_io_stream.h"
1302
1303   pw::stream::SysIoWriter sys_io_writer;
1304   MyProto::StreamEncoder encoder(sys_io_writer, pw::ByteSpan());
1305
1306   // Once this line returns, the field has been written to the Writer.
1307   encoder.WriteTimestamp(system::GetUnixEpoch());
1308
1309   // There's no intermediate buffering when writing a string directly to a
1310   // StreamEncoder.
1311   encoder.WriteWelcomeMessage("Welcome to Pigweed!");
1312
1313   if (!encoder.status().ok()) {
1314     PW_LOG_INFO("Failed to encode proto; %s", encoder.status().str());
1315   }
1316
1317Callbacks
1318=========
1319When using the ``Write()`` method with a ``struct Message``, certain fields may
1320require a callback function be set to encode the values for those fields.
1321Otherwise the values will be treated as an empty repeated field and not encoded.
1322
1323The callback is called with the cursor at the field in question, and passed
1324a reference to the typed encoder that can write the required values to the
1325stream or buffer.
1326
1327Callback implementations may use any level of API. For example a callback for a
1328nested submessage (with a dependency cycle, or repeated) can be implemented by
1329calling ``Write()`` on a nested encoder.
1330
1331.. code-block:: c++
1332
1333   Store::Message store{};
1334   store.employees.SetEncoder([](Store::StreamEncoder& encoder) {
1335     Employee::Message employee{};
1336     // Populate `employee`.
1337     return encoder.GetEmployeesEncoder().Write(employee);
1338   ));
1339
1340Nested submessages
1341==================
1342Code generated ``GetFieldEncoder`` methods are provided that return a correctly
1343typed ``StreamEncoder`` or ``MemoryEncoder`` for the message.
1344
1345.. code-block:: protobuf
1346
1347   message Owner {
1348     Animal pet = 1;
1349   }
1350
1351Note that the accessor method is named for the field, while the returned encoder
1352is named for the message type.
1353
1354.. cpp:function:: Animal::StreamEncoder Owner::StreamEncoder::GetPetEncoder()
1355
1356A lower-level API method returns an untyped encoder, which only provides the
1357lower-level API methods. This can be cast to a typed encoder if needed.
1358
1359.. cpp:function:: pw::protobuf::StreamEncoder pw::protobuf::StreamEncoder::GetNestedEncoder(uint32_t field_number, EmptyEncoderBehavior empty_encoder_behavior = EmptyEncoderBehavior::kWriteFieldNumber)
1360
1361(The optional `empty_encoder_behavior` parameter allows the user to disable
1362writing the tag number for the nested encoder, if no data was written to
1363that nested decoder.)
1364
1365.. warning::
1366   When a nested submessage is created, any use of the parent encoder that
1367   created the nested encoder will trigger a crash. To resume using the parent
1368   encoder, destroy the submessage encoder first.
1369
1370Buffering
1371---------
1372Writing proto messages with nested submessages requires buffering due to
1373limitations of the proto format. Every proto submessage must know the size of
1374the submessage before its final serialization can begin. A streaming encoder can
1375be passed a scratch buffer to use when constructing nested messages. All
1376submessage data is buffered to this scratch buffer until the submessage is
1377finalized. Note that the contents of this scratch buffer is not necessarily
1378valid proto data, so don't try to use it directly.
1379
1380The code generation includes a ``kScratchBufferSizeBytes`` constant that
1381represents the size of the largest submessage and all necessary overhead,
1382excluding the contents of any field values which require a callback.
1383
1384If a submessage field requires a callback, due to a dependency cycle, or a
1385repeated field of unknown length, the size of the submessage cannot be included
1386in the ``kScratchBufferSizeBytes`` constant. If you encode a submessage of this
1387type (which you'll know you're doing because you set an encoder callback for it)
1388simply add the appropriate structure's ``kMaxEncodedSizeBytes`` constant to the
1389scratch buffer size to guarantee enough space.
1390
1391When calculating yourself, the ``MaxScratchBufferSize()`` helper function can
1392also be useful in estimating how much space to allocate to account for nested
1393submessage encoding overhead.
1394
1395.. code-block:: c++
1396
1397   #include "my_protos/pets.pwpb.h"
1398   #include "pw_bytes/span.h"
1399   #include "pw_protobuf/encoder.h"
1400   #include "pw_stream/sys_io_stream.h"
1401
1402   pw::stream::SysIoWriter sys_io_writer;
1403   // The scratch buffer should be at least as big as the largest nested
1404   // submessage. It's a good idea to be a little generous.
1405   std::byte submessage_scratch_buffer[Owner::kScratchBufferSizeBytes];
1406
1407   // Provide the scratch buffer to the proto encoder. The buffer's lifetime must
1408   // match the lifetime of the encoder.
1409   Owner::StreamEncoder owner_encoder(sys_io_writer, submessage_scratch_buffer);
1410
1411   {
1412     // Note that the parent encoder, owner_encoder, cannot be used until the
1413     // nested encoder, pet_encoder, has been destroyed.
1414     Animal::StreamEncoder pet_encoder = owner_encoder.GetPetEncoder();
1415
1416     // There's intermediate buffering when writing to a nested encoder.
1417     pet_encoder.WriteName("Spot");
1418     pet_encoder.WriteType(Pet::Type::DOG);
1419
1420     // When this scope ends, the nested encoder is serialized to the Writer.
1421     // In addition, the parent encoder, owner_encoder, can be used again.
1422   }
1423
1424   // If an encode error occurs when encoding the nested messages, it will be
1425   // reflected at the root encoder.
1426   if (!owner_encoder.status().ok()) {
1427     PW_LOG_INFO("Failed to encode proto; %s", owner_encoder.status().str());
1428   }
1429
1430MemoryEncoder objects use the final destination buffer rather than relying on a
1431scratch buffer.  The ``kMaxEncodedSizeBytes`` constant takes into account the
1432overhead required for nesting submessages. If you calculate the buffer size
1433yourself, your destination buffer might need additional space.
1434
1435.. warning::
1436   If the scratch buffer size is not sufficient, the encoding will fail with
1437   ``Status::ResourceExhausted()``. Always check the results of ``Write`` calls
1438   or the encoder status to ensure success, as otherwise the encoded data will
1439   be invalid.
1440
1441Scalar Fields
1442=============
1443As shown, scalar fields are written using code generated ``WriteFoo``
1444methods that accept the appropriate type and automatically writes the correct
1445field number.
1446
1447.. cpp:function:: Status MyProto::StreamEncoder::WriteFoo(T)
1448
1449These can be freely intermixed with the lower-level API that provides a method
1450per field type, requiring that the field number be passed in. The code
1451generation includes a ``Fields`` enum to provide the field number values.
1452
1453.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteUint64(uint32_t field_number, uint64_t)
1454.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteSint64(uint32_t field_number, int64_t)
1455.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteInt64(uint32_t field_number, int64_t)
1456.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteUint32(uint32_t field_number, uint32_t)
1457.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteSint32(uint32_t field_number, int32_t)
1458.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteInt32(uint32_t field_number, int32_t)
1459.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteFixed64(uint32_t field_number, uint64_t)
1460.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteFixed32(uint32_t field_number, uint64_t)
1461.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteDouble(uint32_t field_number, double)
1462.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteFloat(uint32_t field_number, float)
1463.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteBool(uint32_t field_number, bool)
1464
1465The following two method calls are equivalent, where the first is using the
1466code generated API, and the second implemented by hand.
1467
1468.. code-block:: c++
1469
1470   my_proto_encoder.WriteAge(42);
1471   my_proto_encoder.WriteInt32(static_cast<uint32_t>(MyProto::Fields::kAge), 42);
1472
1473Repeated Fields
1474---------------
1475For repeated scalar fields, multiple code generated ``WriteFoos`` methods
1476are provided.
1477
1478.. cpp:function:: Status MyProto::StreamEncoder::WriteFoos(T)
1479
1480   This writes a single unpacked value.
1481
1482.. cpp:function:: Status MyProto::StreamEncoder::WriteFoos(pw::span<const T>)
1483.. cpp:function:: Status MyProto::StreamEncoder::WriteFoos(const pw::Vector<T>&)
1484
1485   These write a packed field containing all of the values in the provided span
1486   or vector.
1487
1488These too can be freely intermixed with the lower-level API methods, both to
1489write a single value, or to write packed values from either a ``pw::span`` or
1490``pw::Vector`` source.
1491
1492.. cpp:function:: Status pw::protobuf::StreamEncoder::WritePackedUint64(uint32_t field_number, pw::span<const uint64_t>)
1493.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteRepeatedUint64(uint32_t field_number, const pw::Vector<uint64_t>&)
1494.. cpp:function:: Status pw::protobuf::StreamEncoder::WritePackedSint64(uint32_t field_number, pw::span<const int64_t>)
1495.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteRepeatedSint64(uint32_t field_number, const pw::Vector<int64_t>&)
1496.. cpp:function:: Status pw::protobuf::StreamEncoder::WritePackedInt64(uint32_t field_number, pw::span<const int64_t>)
1497.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteRepeatedInt64(uint32_t field_number, const pw::Vector<int64_t>&)
1498.. cpp:function:: Status pw::protobuf::StreamEncoder::WritePackedUint32(uint32_t field_number, pw::span<const uint32_t>)
1499.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteRepeatedUint32(uint32_t field_number, const pw::Vector<uint32_t>&)
1500.. cpp:function:: Status pw::protobuf::StreamEncoder::WritePackedSint32(uint32_t field_number, pw::span<const int32_t>)
1501.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteRepeatedSint32(uint32_t field_number, const pw::Vector<int32_t>&)
1502.. cpp:function:: Status pw::protobuf::StreamEncoder::WritePackedInt32(uint32_t field_number, pw::span<const int32_t>)
1503.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteRepeatedInt32(uint32_t field_number, const pw::Vector<int32_t>&)
1504.. cpp:function:: Status pw::protobuf::StreamEncoder::WritePackedFixed64(uint32_t field_number, pw::span<const uint64_t>)
1505.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteRepeatedFixed64(uint32_t field_number, const pw::Vector<uint64_t>&)
1506.. cpp:function:: Status pw::protobuf::StreamEncoder::WritePackedFixed32(uint32_t field_number, pw::span<const uint64_t>)
1507.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteRepeatedFixed32(uint32_t field_number, const pw::Vector<uint64_t>&)
1508.. cpp:function:: Status pw::protobuf::StreamEncoder::WritePackedDouble(uint32_t field_number, pw::span<const double>)
1509.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteRepeatedDouble(uint32_t field_number, const pw::Vector<double>&)
1510.. cpp:function:: Status pw::protobuf::StreamEncoder::WritePackedFloat(uint32_t field_number, pw::span<const float>)
1511.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteRepeatedFloat(uint32_t field_number, const pw::Vector<float>&)
1512.. cpp:function:: Status pw::protobuf::StreamEncoder::WritePackedBool(uint32_t field_number, pw::span<const bool>)
1513.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteRepeatedBool(uint32_t field_number, const pw::Vector<bool>&)
1514
1515The following two method calls are equivalent, where the first is using the
1516code generated API, and the second implemented by hand.
1517
1518.. code-block:: c++
1519
1520   constexpr std::array<int32_t, 5> numbers = { 4, 8, 15, 16, 23, 42 };
1521
1522   my_proto_encoder.WriteNumbers(numbers);
1523   my_proto_encoder.WritePackedInt32(
1524       static_cast<uint32_t>(MyProto::Fields::kNumbers),
1525       numbers);
1526
1527Enumerations
1528============
1529Enumerations are written using code generated ``WriteEnum`` methods that
1530accept the code generated enumeration as the appropriate type and automatically
1531writes both the correct field number and corresponding value.
1532
1533.. cpp:function:: Status MyProto::StreamEncoder::WriteEnum(MyProto::Enum)
1534
1535To write enumerations with the lower-level API, you would need to cast both
1536the field number and value to the ``uint32_t`` type.
1537
1538The following two methods are equivalent, where the first is code generated,
1539and the second implemented by hand.
1540
1541.. code-block:: c++
1542
1543   my_proto_encoder.WriteAward(MyProto::Award::SILVER);
1544   my_proto_encoder.WriteUint32(
1545       static_cast<uint32_t>(MyProto::Fields::kAward),
1546       static_cast<uint32_t>(MyProto::Award::SILVER));
1547
1548Repeated Fields
1549---------------
1550For repeated enum fields, multiple code generated ``WriteEnums`` methods
1551are provided.
1552
1553.. cpp:function:: Status MyProto::StreamEncoder::WriteEnums(MyProto::Enums)
1554
1555   This writes a single unpacked value.
1556
1557.. cpp:function:: Status MyProto::StreamEncoder::WriteEnums(pw::span<const MyProto::Enums>)
1558.. cpp:function:: Status MyProto::StreamEncoder::WriteEnums(const pw::Vector<MyProto::Enums>&)
1559
1560   These write a packed field containing all of the values in the provided span
1561   or vector.
1562
1563Their use is as scalar fields.
1564
1565Strings
1566=======
1567Strings fields have multiple code generated methods provided.
1568
1569.. cpp:function:: Status MyProto::StreamEncoder::WriteName(std::string_view)
1570.. cpp:function:: Status MyProto::StreamEncoder::WriteName(const char*, size_t)
1571
1572These can be freely intermixed with the lower-level API methods.
1573
1574.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteString(uint32_t field_number, std::string_view)
1575.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteString(uint32_t field_number, const char*, size_t)
1576
1577A lower level API method is provided that can write a string from another
1578stream.
1579
1580.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteStringFromStream(uint32_t field_number, stream::Reader& bytes_reader, size_t num_bytes, ByteSpan stream_pipe_buffer)
1581
1582   The payload for the value is provided through the stream::Reader
1583   ``bytes_reader``. The method reads a chunk of the data from the reader using
1584   the ``stream_pipe_buffer`` and writes it to the encoder.
1585
1586Bytes
1587=====
1588Bytes fields provide the ``WriteData`` code generated method.
1589
1590.. cpp:function:: Status MyProto::StreamEncoder::WriteData(ConstByteSpan)
1591
1592This can be freely intermixed with the lower-level API method.
1593
1594.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteBytes(uint32_t field_number, ConstByteSpan)
1595
1596And with the API method that can write bytes from another stream.
1597
1598.. cpp:function:: Status pw::protobuf::StreamEncoder::WriteBytesFromStream(uint32_t field_number, stream::Reader& bytes_reader, size_t num_bytes, ByteSpan stream_pipe_buffer)
1599
1600   The payload for the value is provided through the stream::Reader
1601   ``bytes_reader``. The method reads a chunk of the data from the reader using
1602   the ``stream_pipe_buffer`` and writes it to the encoder.
1603
1604Error Handling
1605==============
1606While individual write calls on a proto encoder return ``pw::Status`` objects,
1607the encoder tracks all status returns and "latches" onto the first error
1608encountered. This status can be accessed via ``StreamEncoder::status()``.
1609
1610Proto map encoding utils
1611========================
1612Some additional helpers for encoding more complex but common protobuf
1613submessages (e.g. ``map<string, bytes>``) are provided in
1614``pw_protobuf/map_utils.h``.
1615
1616.. note::
1617   The helper API are currently in-development and may not remain stable.
1618
1619--------
1620Decoding
1621--------
1622The simplest way to use ``StreamDecoder`` is to decode a proto from the stream
1623into its code generated ``Message`` structure.
1624
1625.. code-block:: c++
1626
1627   #include "my_protos/my_proto.pwpb.h"
1628   #include "pw_protobuf/stream_decoder.h"
1629   #include "pw_status/status.h"
1630   #include "pw_stream/stream.h"
1631
1632   pw::Status DecodeProtoFromStream(pw::stream::Reader& reader) {
1633     MyProto::Message message{};
1634     MyProto::StreamDecoder decoder(reader);
1635     decoder.Read(message);
1636     return decoder.status();
1637   }
1638
1639In the case of errors, the decoding will stop and return with the cursor on the
1640field that caused the error. It is valid in some cases to inspect the error and
1641continue decoding by calling ``Read()`` again on the same structure, or fall
1642back to using the lower-level APIs.
1643
1644Unknown fields in the wire encoding are skipped.
1645
1646If finer-grained control is required, the ``StreamDecoder`` class provides an
1647iterator-style API for processing a message a field at a time where calling
1648``Next()`` advances the decoder to the next proto field.
1649
1650.. cpp:function:: Status pw::protobuf::StreamDecoder::Next()
1651
1652In the code generated classes the ``Field()`` method returns the current field
1653as a typed ``Fields`` enumeration member, while the lower-level API provides a
1654``FieldNumber()`` method that returns the number of the field.
1655
1656.. cpp:function:: Result<MyProto::Fields> MyProto::StreamDecoder::Field()
1657.. cpp:function:: Result<uint32_t> pw::protobuf::StreamDecoder::FieldNumber()
1658
1659.. code-block:: c++
1660
1661   #include "my_protos/my_proto.pwpb.h"
1662   #include "pw_protobuf/strema_decoder.h"
1663   #include "pw_status/status.h"
1664   #include "pw_status/try.h"
1665   #include "pw_stream/stream.h"
1666
1667   pw::Status DecodeProtoFromStream(pw::stream::Reader& reader) {
1668     MyProto::StreamDecoder decoder(reader);
1669     pw::Status status;
1670
1671     uint32_t age;
1672     char name[16];
1673
1674     // Iterate over the fields in the message. A return value of OK indicates
1675     // that a valid field has been found and can be read. When the decoder
1676     // reaches the end of the message, Next() will return OUT_OF_RANGE.
1677     // Other return values indicate an error trying to decode the message.
1678     while ((status = decoder.Next()).ok()) {
1679       // Field() returns a Result<Fields> as it may fail sometimes.
1680       // However, Field() is guaranteed to be valid after a call to Next()
1681       // that returns OK, so the value can be used directly here.
1682       switch (decoder.Field().value()) {
1683         case MyProto::Fields::kAge: {
1684           PW_TRY_ASSIGN(age, decoder.ReadAge());
1685           break;
1686         }
1687         case MyProto::Fields::kName:
1688           // The string field is copied into the provided buffer. If the buffer
1689           // is too small to fit the string, RESOURCE_EXHAUSTED is returned and
1690           // the decoder is not advanced, allowing the field to be re-read.
1691           PW_TRY(decoder.ReadName(name));
1692           break;
1693       }
1694     }
1695
1696     // Do something with the fields...
1697
1698     return status.IsOutOfRange() ? OkStatus() : status;
1699   }
1700
1701Callbacks
1702=========
1703When using the ``Read()`` method with a ``struct Message``, certain fields may
1704require a callback function be set, otherwise a ``DataLoss`` error will be
1705returned should that field be encountered in the wire encoding.
1706
1707The callback is called with the cursor at the field in question, and passed
1708a reference to the typed decoder that can examine the field and be used to
1709decode it.
1710
1711Callback implementations may use any level of API. For example a callback for a
1712nested submessage (with a dependency cycle, or repeated) can be implemented by
1713calling ``Read()`` on a nested decoder.
1714
1715.. code-block:: c++
1716
1717   Store::Message store{};
1718   store.employees.SetDecoder([](Store::StreamDecoder& decoder) {
1719     PW_ASSERT(decoder.Field().value() == Store::Fields::kEmployees);
1720
1721     Employee::Message employee{};
1722     // Set any callbacks on `employee`.
1723     PW_TRY(decoder.GetEmployeesDecoder().Read(employee));
1724     // Do things with `employee`.
1725     return OkStatus();
1726   ));
1727
1728Nested submessages
1729==================
1730Code generated ``GetFieldDecoder`` methods are provided that return a correctly
1731typed ``StreamDecoder`` for the message.
1732
1733.. code-block:: protobuf
1734
1735   message Owner {
1736     Animal pet = 1;
1737   }
1738
1739As with encoding, note that the accessor method is named for the field, while
1740the returned decoder is named for the message type.
1741
1742.. cpp:function:: Animal::StreamDecoder Owner::StreamDecoder::GetPetDecoder()
1743
1744A lower-level API method returns an untyped decoder, which only provides the
1745lower-level API methods. This can be moved to a typed decoder later.
1746
1747.. cpp:function:: pw::protobuf::StreamDecoder pw::protobuf::StreamDecoder::GetNestedDecoder()
1748
1749.. warning::
1750   When a nested submessage is being decoded, any use of the parent decoder that
1751   created the nested decoder will trigger a crash. To resume using the parent
1752   decoder, destroy the submessage decoder first.
1753
1754
1755.. code-block:: c++
1756
1757   case Owner::Fields::kPet: {
1758     // Note that the parent decoder, owner_decoder, cannot be used until the
1759     // nested decoder, pet_decoder, has been destroyed.
1760     Animal::StreamDecoder pet_decoder = owner_decoder.GetPetDecoder();
1761
1762     while ((status = pet_decoder.Next()).ok()) {
1763       switch (pet_decoder.Field().value()) {
1764         // Decode pet fields...
1765       }
1766     }
1767
1768     // When this scope ends, the nested decoder is destroyed and the
1769     // parent decoder, owner_decoder, can be used again.
1770     break;
1771   }
1772
1773Scalar Fields
1774=============
1775Scalar fields are read using code generated ``ReadFoo`` methods that return the
1776appropriate type and assert that the correct field number ie being read.
1777
1778.. cpp:function:: Result<T> MyProto::StreamDecoder::ReadFoo()
1779
1780These can be freely intermixed with the lower-level API that provides a method
1781per field type, requiring that the caller first check the field number.
1782
1783.. cpp:function:: Result<uint64_t> pw::protobuf::StreamDecoder::ReadUint64()
1784.. cpp:function:: Result<int64_t> pw::protobuf::StreamDecoder::ReadSint64()
1785.. cpp:function:: Result<int64_t> pw::protobuf::StreamDecoder::ReadInt64()
1786.. cpp:function:: Result<uint32_t> pw::protobuf::StreamDecoder::ReadUint32()
1787.. cpp:function:: Result<int32_t> pw::protobuf::StreamDecoder::ReadSint32()
1788.. cpp:function:: Result<int32_t> pw::protobuf::StreamDecoder::ReadInt32()
1789.. cpp:function:: Result<uint64_t> pw::protobuf::StreamDecoder::ReadFixed64()
1790.. cpp:function:: Result<uint64_t> pw::protobuf::StreamDecoder::ReadFixed32()
1791.. cpp:function:: Result<double> pw::protobuf::StreamDecoder::ReadDouble()
1792.. cpp:function:: Result<float> pw::protobuf::StreamDecoder::ReadFloat()
1793.. cpp:function:: Result<bool> pw::protobuf::StreamDecoder::ReadBool()
1794
1795The following two code snippets are equivalent, where the first uses the code
1796generated API, and the second implemented by hand.
1797
1798.. code-block:: c++
1799
1800   pw::Result<int32_t> age = my_proto_decoder.ReadAge();
1801
1802.. code-block:: c++
1803
1804   PW_ASSERT(my_proto_decoder.FieldNumber().value() ==
1805       static_cast<uint32_t>(MyProto::Fields::kAge));
1806   pw::Result<int32_t> my_proto_decoder.ReadInt32();
1807
1808Repeated Fields
1809---------------
1810For repeated scalar fields, multiple code generated ``ReadFoos`` methods
1811are provided.
1812
1813.. cpp:function:: Result<T> MyProto::StreamDecoder::ReadFoos()
1814
1815   This reads a single unpacked value.
1816
1817.. cpp:function:: StatusWithSize MyProto::StreamDecoder::ReadFoos(pw::span<T>)
1818
1819   This reads a packed field containing all of the values into the provided span.
1820
1821.. cpp:function:: Status MyProto::StreamDecoder::ReadFoos(pw::Vector<T>&)
1822
1823   Protobuf encoders are permitted to choose either repeating single unpacked
1824   values, or a packed field, including splitting repeated fields up into
1825   multiple packed fields.
1826
1827   This method supports either format, appending values to the provided
1828   ``pw::Vector``.
1829
1830These too can be freely intermixed with the lower-level API methods, to read a
1831single value, a field of packed values into a ``pw::span``, or support both
1832formats appending to a ``pw::Vector`` source.
1833
1834.. cpp:function:: StatusWithSize pw::protobuf::StreamDecoder::ReadPackedUint64(pw::span<uint64_t>)
1835.. cpp:function:: Status pw::protobuf::StreamDecoder::ReadRepeatedUint64(pw::Vector<uint64_t>&)
1836.. cpp:function:: StatusWithSize pw::protobuf::StreamDecoder::ReadPackedSint64(pw::span<int64_t>)
1837.. cpp:function:: Status pw::protobuf::StreamDecoder::ReadRepeatedSint64(pw::Vector<int64_t>&)
1838.. cpp:function:: StatusWithSize pw::protobuf::StreamDecoder::ReadPackedInt64(pw::span<int64_t>)
1839.. cpp:function:: Status pw::protobuf::StreamDecoder::ReadRepeatedInt64(pw::Vector<int64_t>&)
1840.. cpp:function:: StatusWithSize pw::protobuf::StreamDecoder::ReadPackedUint32(pw::span<uint32_t>)
1841.. cpp:function:: Status pw::protobuf::StreamDecoder::ReadRepeatedUint32(pw::Vector<uint32_t>&)
1842.. cpp:function:: StatusWithSize pw::protobuf::StreamDecoder::ReadPackedSint32(pw::span<int32_t>)
1843.. cpp:function:: Status pw::protobuf::StreamDecoder::ReadRepeatedSint32(pw::Vector<int32_t>&)
1844.. cpp:function:: StatusWithSize pw::protobuf::StreamDecoder::ReadPackedInt32(pw::span<int32_t>)
1845.. cpp:function:: Status pw::protobuf::StreamDecoder::ReadRepeatedInt32(pw::Vector<int32_t>&)
1846.. cpp:function:: StatusWithSize pw::protobuf::StreamDecoder::ReadPackedFixed64(pw::span<uint64_t>)
1847.. cpp:function:: Status pw::protobuf::StreamDecoder::ReadRepeatedFixed64(pw::Vector<uint64_t>&)
1848.. cpp:function:: StatusWithSize pw::protobuf::StreamDecoder::ReadPackedFixed32(pw::span<uint64_t>)
1849.. cpp:function:: Status pw::protobuf::StreamDecoder::ReadRepeatedFixed32(pw::Vector<uint64_t>&)
1850.. cpp:function:: StatusWithSize pw::protobuf::StreamDecoder::ReadPackedDouble(pw::span<double>)
1851.. cpp:function:: Status pw::protobuf::StreamDecoder::ReadRepeatedDouble(pw::Vector<double>&)
1852.. cpp:function:: StatusWithSize pw::protobuf::StreamDecoder::ReadPackedFloat(pw::span<float>)
1853.. cpp:function:: Status pw::protobuf::StreamDecoder::ReadRepeatedFloat(pw::Vector<float>&)
1854.. cpp:function:: StatusWithSize pw::protobuf::StreamDecoder::ReadPackedBool(pw::span<bool>)
1855.. cpp:function:: Status pw::protobuf::StreamDecoder::ReadRepeatedBool(pw::Vector<bool>&)
1856
1857The following two code blocks are equivalent, where the first uses the code
1858generated API, and the second is implemented by hand.
1859
1860.. code-block:: c++
1861
1862   pw::Vector<int32_t, 8> numbers;
1863
1864   my_proto_decoder.ReadNumbers(numbers);
1865
1866.. code-block:: c++
1867
1868   pw::Vector<int32_t, 8> numbers;
1869
1870   PW_ASSERT(my_proto_decoder.FieldNumber().value() ==
1871       static_cast<uint32_t>(MyProto::Fields::kNumbers));
1872   my_proto_decoder.ReadRepeatedInt32(numbers);
1873
1874Enumerations
1875============
1876``pw_protobuf`` generates a few functions for working with enumerations.
1877Most importantly, enumerations are read using generated ``ReadEnum`` methods
1878that return the enumeration as the appropriate generated type.
1879
1880.. cpp:function:: Result<MyProto::Enum> MyProto::StreamDecoder::ReadEnum()
1881
1882   Decodes an enum from the stream.
1883
1884.. cpp:function:: constexpr bool MyProto::IsValidEnum(MyProto::Enum value)
1885
1886   Validates the value encoded in the wire format against the known set of
1887   enumerates.
1888
1889.. cpp:function:: constexpr const char* MyProto::EnumToString(MyProto::Enum value)
1890
1891   Returns the string representation of the enum value. For example,
1892   ``FooToString(Foo::kBarBaz)`` returns ``"BAR_BAZ"``. Returns the empty string
1893   if the value is not a valid value.
1894
1895To read enumerations with the lower-level API, you would need to cast the
1896retured value from the ``uint32_t``.
1897
1898The following two code blocks are equivalent, where the first is using the code
1899generated API, and the second implemented by hand.
1900
1901.. code-block:: c++
1902
1903   pw::Result<MyProto::Award> award = my_proto_decoder.ReadAward();
1904   if (!MyProto::IsValidAward(award)) {
1905     PW_LOG_DBG("Unknown award");
1906   }
1907
1908.. code-block:: c++
1909
1910   PW_ASSERT(my_proto_decoder.FieldNumber().value() ==
1911       static_cast<uint32_t>(MyProto::Fields::kAward));
1912   pw::Result<uint32_t> award_value = my_proto_decoder.ReadUint32();
1913   if (award_value.ok()) {
1914     MyProto::Award award = static_cast<MyProto::Award>(award_value);
1915   }
1916
1917Repeated Fields
1918---------------
1919For repeated enum fields, multiple code generated ``ReadEnums`` methods
1920are provided.
1921
1922.. cpp:function:: Result<MyProto::Enums> MyProto::StreamDecoder::ReadEnums()
1923
1924   This reads a single unpacked value.
1925
1926.. cpp:function:: StatusWithSize MyProto::StreamDecoder::ReadEnums(pw::span<MyProto::Enums>)
1927
1928   This reads a packed field containing all of the checked values into the
1929   provided span.
1930
1931.. cpp:function:: Status MyProto::StreamDecoder::ReadEnums(pw::Vector<MyProto::Enums>&)
1932
1933   This method supports either repeated unpacked or packed formats, appending
1934   checked values to the provided ``pw::Vector``.
1935
1936Their use is as scalar fields.
1937
1938Strings
1939=======
1940Strings fields provide a code generated method to read the string into the
1941provided span. Since the span is updated with the size of the string, the string
1942is not automatically null-terminated. :ref:`module-pw_string` provides utility
1943methods to copy string data from spans into other targets.
1944
1945.. cpp:function:: StatusWithSize MyProto::StreamDecoder::ReadName(pw::span<char>)
1946
1947An additional code generated method is provided to return a nested
1948``BytesReader`` to access the data as a stream. As with nested submessage
1949decoders, any use of the parent decoder that created the bytes reader will
1950trigger a crash. To resume using the parent decoder, destroy the bytes reader
1951first.
1952
1953.. cpp:function:: pw::protobuf::StreamDecoder::BytesReader MyProto::StreamDecoder::GetNameReader()
1954
1955These can be freely intermixed with the lower-level API method:
1956
1957.. cpp:function:: StatusWithSize pw::protobuf::StreamDecoder::ReadString(pw::span<char>)
1958
1959The lower-level ``GetBytesReader()`` method can also be used to read string data
1960as bytes.
1961
1962Bytes
1963=====
1964Bytes fields provide the ``WriteData`` code generated method to read the bytes
1965into the provided span.
1966
1967.. cpp:function:: StatusWithSize MyProto::StreamDecoder::ReadData(ByteSpan)
1968
1969An additional code generated method is provided to return a nested
1970``BytesReader`` to access the data as a stream. As with nested submessage
1971decoders, any use of the parent decoder that created the bytes reader will
1972trigger a crash. To resume using the parent decoder, destroy the bytes reader
1973first.
1974
1975.. cpp:function:: pw::protobuf::StreamDecoder::BytesReader MyProto::StreamDecoder::GetDataReader()
1976
1977These can be freely intermixed with the lower-level API methods.
1978
1979.. cpp:function:: StatusWithSize pw::protobuf::StreamDecoder::ReadBytes(ByteSpan)
1980.. cpp:function:: pw::protobuf::StreamDecoder::BytesReader pw::protobuf::StreamDecoder::GetBytesReader()
1981
1982The ``BytesReader`` supports seeking only if the ``StreamDecoder``'s reader
1983supports seeking.
1984
1985Error Handling
1986==============
1987While individual read calls on a proto decoder return ``pw::Result``,
1988``pw::StatusWithSize``, or ``pw::Status`` objects, the decoder tracks all status
1989returns and "latches" onto the first error encountered. This status can be
1990accessed via ``StreamDecoder::status()``.
1991
1992Length Limited Decoding
1993=======================
1994Where the length of the protobuf message is known in advance, the decoder can
1995be prevented from reading from the stream beyond the known bounds by specifying
1996the known length to the decoder:
1997
1998.. code-block:: c++
1999
2000   pw::protobuf::StreamDecoder decoder(reader, message_length);
2001
2002When a decoder constructed in this way goes out of scope, it will consume any
2003remaining bytes in ``message_length`` allowing the next ``Read()`` on the stream
2004to be data after the protobuf, even when it was not fully parsed.
2005
2006-----------------
2007In-memory Decoder
2008-----------------
2009The separate ``Decoder`` class operates on an protobuf message located in a
2010buffer in memory. It is more efficient than the ``StreamDecoder`` in cases
2011where the complete protobuf data can be stored in memory. The tradeoff of this
2012efficiency is that no code generation is provided, so all decoding must be
2013performed by hand.
2014
2015As ``StreamDecoder``, it provides an iterator-style API for processing a
2016message. Calling ``Next()`` advances the decoder to the next proto field, which
2017can then be read by calling the appropriate ``Read*`` function for the field
2018number.
2019
2020When reading ``bytes`` and ``string`` fields, the decoder returns a view of that
2021field within the buffer; no data is copied out.
2022
2023.. code-block:: c++
2024
2025   #include "pw_protobuf/decoder.h"
2026   #include "pw_status/try.h"
2027
2028   pw::Status DecodeProtoFromBuffer(pw::span<const std::byte> buffer) {
2029     pw::protobuf::Decoder decoder(buffer);
2030     pw::Status status;
2031
2032     uint32_t uint32_field;
2033     std::string_view string_field;
2034
2035     // Iterate over the fields in the message. A return value of OK indicates
2036     // that a valid field has been found and can be read. When the decoder
2037     // reaches the end of the message, Next() will return OUT_OF_RANGE.
2038     // Other return values indicate an error trying to decode the message.
2039     while ((status = decoder.Next()).ok()) {
2040       switch (decoder.FieldNumber()) {
2041         case 1:
2042           PW_TRY(decoder.ReadUint32(&uint32_field));
2043           break;
2044         case 2:
2045           // The passed-in string_view will point to the contents of the string
2046           // field within the buffer.
2047           PW_TRY(decoder.ReadString(&string_field));
2048           break;
2049       }
2050     }
2051
2052     // Do something with the fields...
2053
2054     return status.IsOutOfRange() ? OkStatus() : status;
2055   }
2056
2057---------------
2058Message Decoder
2059---------------
2060
2061.. note::
2062
2063   ``pw::protobuf::Message`` is unrelated to the codegen ``struct Message``
2064   used with ``StreamDecoder``.
2065
2066The module implements a message parsing helper class ``Message``, in
2067``pw_protobuf/message.h``, to faciliate proto message parsing and field access.
2068The class provides interfaces for searching fields in a proto message and
2069creating helper classes for it according to its interpreted field type, i.e.
2070uint32, bytes, string, map<>, repeated etc. The class works on top of
2071``StreamDecoder`` and thus requires a ``pw::stream::SeekableReader`` for proto
2072message access. The following gives examples for using the class to process
2073different fields in a proto message:
2074
2075.. code-block:: c++
2076
2077   // Consider the proto messages defined as follows:
2078   //
2079   // message Nested {
2080   //   string nested_str = 1;
2081   //   bytes nested_bytes = 2;
2082   // }
2083   //
2084   // message {
2085   //   uint32 integer = 1;
2086   //   string str = 2;
2087   //   bytes bytes = 3;
2088   //   Nested nested = 4;
2089   //   repeated string rep_str = 5;
2090   //   repeated Nested rep_nested  = 6;
2091   //   map<string, bytes> str_to_bytes = 7;
2092   //   map<string, Nested> str_to_nested = 8;
2093   // }
2094
2095   // Given a seekable `reader` that reads the top-level proto message, and
2096   // a <proto_size> that gives the size of the proto message:
2097   Message message(reader, proto_size);
2098
2099   // Parse a proto integer field
2100   Uint32 integer = messasge_parser.AsUint32(1);
2101   if (!integer.ok()) {
2102     // handle parsing error. i.e. return integer.status().
2103   }
2104   uint32_t integer_value = integer.value(); // obtained the value
2105
2106   // Parse a string field
2107   String str = message.AsString(2);
2108   if (!str.ok()) {
2109     // handle parsing error. i.e. return str.status();
2110   }
2111
2112   // check string equal
2113   Result<bool> str_check = str.Equal("foo");
2114
2115   // Parse a bytes field
2116   Bytes bytes = message.AsBytes(3);
2117   if (!bytes.ok()) {
2118     // handle parsing error. i.e. return bytes.status();
2119   }
2120
2121   // Get a reader to the bytes.
2122   stream::IntervalReader bytes_reader = bytes.GetBytesReader();
2123
2124   // Parse nested message `Nested nested = 4;`
2125   Message nested = message.AsMessage(4).
2126   // Get the fields in the nested message.
2127   String nested_str = nested.AsString(1);
2128   Bytes nested_bytes = nested.AsBytes(2);
2129
2130   // Parse repeated field `repeated string rep_str = 5;`
2131   RepeatedStrings rep_str = message.AsRepeatedString(5);
2132   // Iterate through the entries. If proto is malformed when
2133   // iterating, the next element (`str` in this case) will be invalid
2134   // and loop will end in the iteration after.
2135   for (String element : rep_str) {
2136     // Check status
2137     if (!str.ok()) {
2138       // In the case of error, loop will end in the next iteration if
2139       // continues. This is the chance for code to catch the error.
2140     }
2141     // Process str
2142   }
2143
2144   // Parse repeated field `repeated Nested rep_nested = 6;`
2145   RepeatedStrings rep_str = message.AsRepeatedString(6);
2146   // Iterate through the entries. For iteration
2147   for (Message element : rep_rep_nestedstr) {
2148     // Check status
2149     if (!element.ok()) {
2150       // In the case of error, loop will end in the next iteration if
2151       // continues. This is the chance for code to catch the error.
2152     }
2153     // Process element
2154   }
2155
2156   // Parse map field `map<string, bytes> str_to_bytes = 7;`
2157   StringToBytesMap str_to_bytes = message.AsStringToBytesMap(7);
2158   // Access the entry by a given key value
2159   Bytes bytes_for_key = str_to_bytes["key"];
2160   // Or iterate through map entries
2161   for (StringToBytesMapEntry entry : str_to_bytes) {
2162     // Check status
2163     if (!entry.ok()) {
2164       // In the case of error, loop will end in the next iteration if
2165       // continues. This is the chance for code to catch the error.
2166     }
2167     String key = entry.Key();
2168     Bytes value = entry.Value();
2169     // process entry
2170   }
2171
2172   // Parse map field `map<string, Nested> str_to_nested = 8;`
2173   StringToMessageMap str_to_nested = message.AsStringToBytesMap(8);
2174   // Access the entry by a given key value
2175   Message nested_for_key = str_to_nested["key"];
2176   // Or iterate through map entries
2177   for (StringToMessageMapEntry entry : str_to_nested) {
2178     // Check status
2179     if (!entry.ok()) {
2180       // In the case of error, loop will end in the next iteration if
2181       // continues. This is the chance for code to catch the error.
2182       // However it is still recommended that the user breaks here.
2183       break;
2184     }
2185     String key = entry.Key();
2186     Message value = entry.Value();
2187     // process entry
2188   }
2189
2190The methods in ``Message`` for parsing a single field, i.e. everty `AsXXX()`
2191method except AsRepeatedXXX() and AsStringMapXXX(), internally performs a
2192linear scan of the entire proto message to find the field with the given
2193field number. This can be expensive if performed multiple times, especially
2194on slow reader. The same applies to the ``operator[]`` of StringToXXXXMap
2195helper class. Therefore, for performance consideration, whenever possible, it
2196is recommended to use the following for-range style to iterate and process
2197single fields directly.
2198
2199
2200.. code-block:: c++
2201
2202   for (Message::Field field : message) {
2203     // Check status
2204     if (!field.ok()) {
2205       // In the case of error, loop will end in the next iteration if
2206       // continues. This is the chance for code to catch the error.
2207     }
2208     if (field.field_number() == 1) {
2209       Uint32 integer = field.As<Uint32>();
2210       ...
2211     } else if (field.field_number() == 2) {
2212       String str = field.As<String>();
2213       ...
2214     } else if (field.field_number() == 3) {
2215       Bytes bytes = field.As<Bytes>();
2216       ...
2217     } else if (field.field_number() == 4) {
2218       Message nested = field.As<Message>();
2219       ...
2220     }
2221   }
2222
2223
2224.. Note::
2225  The helper API are currently in-development and may not remain stable.
2226
2227-----------
2228Size report
2229-----------
2230
2231Full size report
2232================
2233
2234This report demonstrates the size of using the entire decoder with all of its
2235decode methods and a decode callback for a proto message containing each of the
2236protobuf field types.
2237
2238.. include:: size_report/decoder_partial
2239
2240
2241Incremental size report
2242=======================
2243
2244This report is generated using the full report as a base and adding some int32
2245fields to the decode callback to demonstrate the incremental cost of decoding
2246fields in a message.
2247
2248.. include:: size_report/decoder_incremental
2249
2250---------------------------
2251Serialized size calculation
2252---------------------------
2253``pw_protobuf/serialized_size.h`` provides a set of functions for calculating
2254how much memory serialized protocol buffer fields require. The
2255``kMaxSizeBytes*`` variables provide the maximum encoded sizes of each field
2256type. The ``SizeOfField*()`` functions calculate the encoded size of a field of
2257the specified type, given a particular key and, for variable length fields
2258(varint or delimited), a value. The ``SizeOf*Field`` functions calculate the
2259encoded size of fields with a particular wire format (delimited, varint).
2260
2261In the rare event that you need to know the serialized size of a field's tag
2262(field number and wire type), you can use ``TagSizeBytes()`` to calculate the
2263tag size for a given field number.
2264
2265--------------------------
2266Available protobuf modules
2267--------------------------
2268There are a handful of messages ready to be used in Pigweed projects. These are
2269located in ``pw_protobuf/pw_protobuf_protos``.
2270
2271common.proto
2272============
2273Contains Empty message proto used in many RPC calls.
2274
2275
2276status.proto
2277============
2278Contains the enum for pw::Status.
2279
2280.. note::
2281   ``pw::protobuf::StatusCode`` values should not be used outside of a .proto
2282   file. Instead, the StatusCodes should be converted to the Status type in the
2283   language. In C++, this would be:
2284
2285   .. code-block:: c++
2286
2287      // Reading from a proto
2288      pw::Status status = static_cast<pw::Status::Code>(proto.status_field));
2289      // Writing to a proto
2290      proto.status_field = static_cast<pw::protobuf::StatusCode>(status.code()));
2291
2292----------------------------------------
2293Comparison with other protobuf libraries
2294----------------------------------------
2295
2296protobuf-lite
2297=============
2298protobuf-lite is the official reduced-size C++ implementation of protobuf. It
2299uses a restricted subset of the protobuf library's features to minimize code
2300size. However, is is still around 150K in size and requires dynamic memory
2301allocation, making it unsuitable for many embedded systems.
2302
2303nanopb
2304======
2305`nanopb <https://github.com/nanopb/nanopb>`_ is a commonly used embedded
2306protobuf library with very small code size and full code generation. It provides
2307both encoding/decoding functionality and in-memory C structs representing
2308protobuf messages.
2309
2310nanopb works well for many embedded products; however, using its generated code
2311can run into RAM usage issues when processing nontrivial protobuf messages due
2312to the necessity of defining a struct capable of storing all configurations of
2313the message, which can grow incredibly large. In one project, Pigweed developers
2314encountered an 11K struct statically allocated for a single message---over twice
2315the size of the final encoded output! (This was what prompted the development of
2316``pw_protobuf``.)
2317
2318To avoid this issue, it is possible to use nanopb's low-level encode/decode
2319functions to process individual message fields directly, but this loses all of
2320the useful semantics of code generation. ``pw_protobuf`` is designed to optimize
2321for this use case; it allows for efficient operations on the wire format with an
2322intuitive user interface.
2323
2324Depending on the requirements of a project, either of these libraries could be
2325suitable.
2326