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