1# Copyright 2020 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14"""This module defines the generated code for pw_protobuf C++ classes.""" 15 16import abc 17import enum 18 19# Type ignore here for graphlib-backport on Python 3.8 20from graphlib import CycleError, TopologicalSorter # type: ignore 21from itertools import takewhile 22import os 23import sys 24from typing import Dict, Iterable, List, Optional, Tuple 25from typing import cast 26 27from google.protobuf import descriptor_pb2 28 29from pw_protobuf.output_file import OutputFile 30from pw_protobuf.proto_tree import ProtoEnum, ProtoMessage, ProtoMessageField 31from pw_protobuf.proto_tree import ProtoNode 32from pw_protobuf.proto_tree import build_node_tree 33from pw_protobuf.proto_tree import EXTERNAL_SYMBOL_WORKAROUND_NAMESPACE 34 35PLUGIN_NAME = 'pw_protobuf' 36PLUGIN_VERSION = '0.1.0' 37 38PROTO_H_EXTENSION = '.pwpb.h' 39PROTO_CC_EXTENSION = '.pwpb.cc' 40 41PROTOBUF_NAMESPACE = '::pw::protobuf' 42_INTERNAL_NAMESPACE = '::pw::protobuf::internal' 43 44 45class ClassType(enum.Enum): 46 """Type of class.""" 47 48 MEMORY_ENCODER = 1 49 STREAMING_ENCODER = 2 50 # MEMORY_DECODER = 3 51 STREAMING_DECODER = 4 52 53 def base_class_name(self) -> str: 54 """Returns the base class used by this class type.""" 55 if self is self.STREAMING_ENCODER: 56 return 'StreamEncoder' 57 if self is self.MEMORY_ENCODER: 58 return 'MemoryEncoder' 59 if self is self.STREAMING_DECODER: 60 return 'StreamDecoder' 61 62 raise ValueError('Unknown class type') 63 64 def codegen_class_name(self) -> str: 65 """Returns the base class used by this class type.""" 66 if self is self.STREAMING_ENCODER: 67 return 'StreamEncoder' 68 if self is self.MEMORY_ENCODER: 69 return 'MemoryEncoder' 70 if self is self.STREAMING_DECODER: 71 return 'StreamDecoder' 72 73 raise ValueError('Unknown class type') 74 75 def is_encoder(self) -> bool: 76 """Returns True if this class type is an encoder.""" 77 if self is self.STREAMING_ENCODER: 78 return True 79 if self is self.MEMORY_ENCODER: 80 return True 81 if self is self.STREAMING_DECODER: 82 return False 83 84 raise ValueError('Unknown class type') 85 86 87# protoc captures stdout, so we need to printf debug to stderr. 88def debug_print(*args, **kwargs): 89 print(*args, file=sys.stderr, **kwargs) 90 91 92class ProtoMember(abc.ABC): 93 """Base class for a C++ class member for a field in a protobuf message.""" 94 95 def __init__( 96 self, field: ProtoMessageField, scope: ProtoNode, root: ProtoNode 97 ): 98 """Creates an instance of a class member. 99 100 Args: 101 field: the ProtoMessageField to which the method belongs. 102 scope: the ProtoNode namespace in which the method is being defined. 103 """ 104 self._field: ProtoMessageField = field 105 self._scope: ProtoNode = scope 106 self._root: ProtoNode = root 107 108 @abc.abstractmethod 109 def name(self) -> str: 110 """Returns the name of the member, e.g. DoSomething.""" 111 112 @abc.abstractmethod 113 def should_appear(self) -> bool: # pylint: disable=no-self-use 114 """Whether the member should be generated.""" 115 116 def field_cast(self) -> str: 117 return 'static_cast<uint32_t>(Fields::{})'.format( 118 self._field.enum_name() 119 ) 120 121 def _relative_type_namespace(self, from_root: bool = False) -> str: 122 """Returns relative namespace between member's scope and field type.""" 123 scope = self._root if from_root else self._scope 124 type_node = self._field.type_node() 125 assert type_node is not None 126 127 # If a class method is referencing its class, the namespace provided 128 # must be from the root or it will be empty. 129 if type_node == scope: 130 scope = self._root 131 132 ancestor = scope.common_ancestor(type_node) 133 namespace = type_node.cpp_namespace(ancestor) 134 135 assert namespace 136 return namespace 137 138 139class ProtoMethod(ProtoMember): 140 """Base class for a C++ method for a field in a protobuf message.""" 141 142 def __init__( 143 self, 144 field: ProtoMessageField, 145 scope: ProtoNode, 146 root: ProtoNode, 147 base_class: str, 148 ): 149 super().__init__(field, scope, root) 150 self._base_class: str = base_class 151 152 @abc.abstractmethod 153 def params(self) -> List[Tuple[str, str]]: 154 """Returns the parameters of the method as a list of (type, name) pairs. 155 156 e.g. 157 [('int', 'foo'), ('const char*', 'bar')] 158 """ 159 160 @abc.abstractmethod 161 def body(self) -> List[str]: 162 """Returns the method body as a list of source code lines. 163 164 e.g. 165 [ 166 'int baz = bar[foo];', 167 'return (baz ^ foo) >> 3;' 168 ] 169 """ 170 171 @abc.abstractmethod 172 def return_type(self, from_root: bool = False) -> str: 173 """Returns the return type of the method, e.g. int. 174 175 For non-primitive return types, the from_root argument determines 176 whether the namespace should be relative to the message's scope 177 (default) or the root scope. 178 """ 179 180 @abc.abstractmethod 181 def in_class_definition(self) -> bool: 182 """Determines where the method should be defined. 183 184 Returns True if the method definition should be inlined in its class 185 definition, or False if it should be declared in the class and defined 186 later. 187 """ 188 189 def should_appear(self) -> bool: # pylint: disable=no-self-use 190 """Whether the method should be generated.""" 191 return True 192 193 def param_string(self) -> str: 194 return ', '.join([f'{type} {name}' for type, name in self.params()]) 195 196 197class WriteMethod(ProtoMethod): 198 """Base class representing an encoder write method. 199 200 Write methods have following format (for the proto field foo): 201 202 Status WriteFoo({params...}) { 203 return encoder_->Write{type}(kFoo, {params...}); 204 } 205 206 """ 207 208 def name(self) -> str: 209 return 'Write{}'.format(self._field.name()) 210 211 def return_type(self, from_root: bool = False) -> str: 212 return '::pw::Status' 213 214 def body(self) -> List[str]: 215 params = ', '.join([pair[1] for pair in self.params()]) 216 line = 'return {}::{}({}, {});'.format( 217 self._base_class, self._encoder_fn(), self.field_cast(), params 218 ) 219 return [line] 220 221 def params(self) -> List[Tuple[str, str]]: 222 """Method parameters, defined in subclasses.""" 223 raise NotImplementedError() 224 225 def in_class_definition(self) -> bool: 226 return True 227 228 def _encoder_fn(self) -> str: 229 """The encoder function to call. 230 231 Defined in subclasses. 232 233 e.g. 'WriteUint32', 'WriteBytes', etc. 234 """ 235 raise NotImplementedError() 236 237 238class PackedWriteMethod(WriteMethod): 239 """A method for a writing a packed repeated field. 240 241 Same as a WriteMethod, but is only generated for repeated fields. 242 """ 243 244 def should_appear(self) -> bool: 245 return self._field.is_repeated() 246 247 def _encoder_fn(self) -> str: 248 raise NotImplementedError() 249 250 251class ReadMethod(ProtoMethod): 252 """Base class representing an decoder read method. 253 254 Read methods have following format (for the proto field foo): 255 256 Result<{ctype}> ReadFoo({params...}) { 257 Result<uint32_t> field_number = FieldNumber(); 258 PW_ASSERT(field_number.ok()); 259 PW_ASSERT(field_number.value() == 260 static_cast<uint32_t>(Fields::kFoo)); 261 return decoder_->Read{type}({params...}); 262 } 263 264 """ 265 266 def name(self) -> str: 267 return 'Read{}'.format(self._field.name()) 268 269 def return_type(self, from_root: bool = False) -> str: 270 return '::pw::Result<{}>'.format(self._result_type()) 271 272 def _result_type(self) -> str: 273 """The type returned by the deoder function. 274 275 Defined in subclasses. 276 277 e.g. 'uint32_t', 'pw::span<std::byte>', etc. 278 """ 279 raise NotImplementedError() 280 281 def body(self) -> List[str]: 282 lines: List[str] = [] 283 lines += ['::pw::Result<uint32_t> field_number = FieldNumber();'] 284 lines += ['PW_ASSERT(field_number.ok());'] 285 lines += [ 286 'PW_ASSERT(field_number.value() == {});'.format(self.field_cast()) 287 ] 288 lines += self._decoder_body() 289 return lines 290 291 def _decoder_body(self) -> List[str]: 292 """Returns the decoder body part as a list of source code lines.""" 293 params = ', '.join([pair[1] for pair in self.params()]) 294 line = 'return {}::{}({});'.format( 295 self._base_class, self._decoder_fn(), params 296 ) 297 return [line] 298 299 def _decoder_fn(self) -> str: 300 """The decoder function to call. 301 302 Defined in subclasses. 303 304 e.g. 'ReadUint32', 'ReadBytes', etc. 305 """ 306 raise NotImplementedError() 307 308 def params(self) -> List[Tuple[str, str]]: 309 """Method parameters, can be overriden in subclasses.""" 310 return [] 311 312 def in_class_definition(self) -> bool: 313 return True 314 315 316class PackedReadMethod(ReadMethod): 317 """A method for a reading a packed repeated field. 318 319 Same as ReadMethod, but is only generated for repeated fields. 320 """ 321 322 def should_appear(self) -> bool: 323 return self._field.is_repeated() 324 325 def return_type(self, from_root: bool = False) -> str: 326 return '::pw::StatusWithSize' 327 328 def params(self) -> List[Tuple[str, str]]: 329 return [('pw::span<{}>'.format(self._result_type()), 'out')] 330 331 332class PackedReadVectorMethod(ReadMethod): 333 """A method for a reading a packed repeated field. 334 335 An alternative to ReadMethod for repeated fields that appends values into 336 a pw::Vector. 337 """ 338 339 def should_appear(self) -> bool: 340 return self._field.is_repeated() 341 342 def return_type(self, from_root: bool = False) -> str: 343 return '::pw::Status' 344 345 def params(self) -> List[Tuple[str, str]]: 346 return [('::pw::Vector<{}>&'.format(self._result_type()), 'out')] 347 348 349class MessageProperty(ProtoMember): 350 """Base class for a C++ property for a field in a protobuf message.""" 351 352 def name(self) -> str: 353 return self._field.field_name() 354 355 def should_appear(self) -> bool: 356 return True 357 358 @abc.abstractmethod 359 def type_name(self, from_root: bool = False) -> str: 360 """Returns the type of the property, e.g. uint32_t.""" 361 362 @abc.abstractmethod 363 def wire_type(self) -> str: 364 """Returns the wire type of the property, e.g. kVarint.""" 365 366 def varint_decode_type(self) -> str: 367 """Returns the varint decoding type of the property, e.g. kZigZag. 368 369 Defined in subclasses that return kVarint for wire_type(). 370 """ 371 raise NotImplementedError() 372 373 def is_string(self) -> bool: # pylint: disable=no-self-use 374 """True if this field is a string field (as opposed to bytes).""" 375 return False 376 377 @staticmethod 378 def repeated_field_container(type_name: str, max_size: int) -> str: 379 """Returns the container type used for repeated fields. 380 381 Defaults to ::pw::Vector<type, max_size>. String fields use 382 ::pw::InlineString<max_size> instead. 383 """ 384 return f'::pw::Vector<{type_name}, {max_size}>' 385 386 def use_callback(self) -> bool: # pylint: disable=no-self-use 387 """Returns whether the decoder should use a callback.""" 388 options = self._field.options() 389 assert options is not None 390 return options.use_callback or ( 391 self._field.is_repeated() and self.max_size() == 0 392 ) 393 394 def is_optional(self) -> bool: 395 """Returns whether the decoder should use std::optional.""" 396 return ( 397 self._field.is_optional() 398 and self.max_size() == 0 399 and self.wire_type() != 'kDelimited' 400 ) 401 402 def is_repeated(self) -> bool: 403 return self._field.is_repeated() 404 405 def max_size(self) -> int: 406 """Returns the maximum size of the field.""" 407 if self._field.is_repeated(): 408 options = self._field.options() 409 assert options is not None 410 return options.max_count 411 412 return 0 413 414 def is_fixed_size(self) -> bool: 415 """Returns whether the decoder should use a fixed sized field.""" 416 if self._field.is_repeated(): 417 options = self._field.options() 418 assert options is not None 419 return options.fixed_count 420 421 return False 422 423 def sub_table(self) -> str: # pylint: disable=no-self-use 424 return '{}' 425 426 def struct_member(self, from_root: bool = False) -> Tuple[str, str]: 427 """Returns the structure member.""" 428 if self.use_callback(): 429 return ( 430 f'{PROTOBUF_NAMESPACE}::Callback' 431 '<StreamEncoder, StreamDecoder>', 432 self.name(), 433 ) 434 435 # Optional fields are wrapped in std::optional 436 if self.is_optional(): 437 return ( 438 'std::optional<{}>'.format(self.type_name(from_root)), 439 self.name(), 440 ) 441 442 # Non-repeated fields have a member of just the type name. 443 max_size = self.max_size() 444 if max_size == 0: 445 return (self.type_name(from_root), self.name()) 446 447 # Fixed size fields use std::array. 448 if self.is_fixed_size(): 449 return ( 450 'std::array<{}, {}>'.format( 451 self.type_name(from_root), max_size 452 ), 453 self.name(), 454 ) 455 456 # Otherwise prefer pw::Vector for repeated fields. 457 return ( 458 self.repeated_field_container(self.type_name(from_root), max_size), 459 self.name(), 460 ) 461 462 def _varint_type_table_entry(self) -> str: 463 if self.wire_type() == 'kVarint': 464 return '{}::VarintType::{}'.format( 465 _INTERNAL_NAMESPACE, self.varint_decode_type() 466 ) 467 468 return f'static_cast<{_INTERNAL_NAMESPACE}::VarintType>(0)' 469 470 def _wire_type_table_entry(self) -> str: 471 return '{}::WireType::{}'.format(PROTOBUF_NAMESPACE, self.wire_type()) 472 473 def _elem_size_table_entry(self) -> str: 474 return 'sizeof({})'.format(self.type_name()) 475 476 def _bool_attr(self, attr: str) -> str: 477 """C++ string for a bool argument that includes the argument name.""" 478 return f'/*{attr}=*/{bool(getattr(self, attr)())}'.lower() 479 480 def table_entry(self) -> List[str]: 481 """Table entry.""" 482 return [ 483 self.field_cast(), 484 self._wire_type_table_entry(), 485 self._elem_size_table_entry(), 486 self._varint_type_table_entry(), 487 self._bool_attr('is_string'), 488 self._bool_attr('is_fixed_size'), 489 self._bool_attr('is_repeated'), 490 self._bool_attr('is_optional'), 491 self._bool_attr('use_callback'), 492 'offsetof(Message, {})'.format(self.name()), 493 'sizeof(Message::{})'.format(self.name()), 494 self.sub_table(), 495 ] 496 497 @abc.abstractmethod 498 def _size_fn(self) -> str: 499 """Returns the name of the field size function.""" 500 501 def _size_length(self) -> Optional[str]: # pylint: disable=no-self-use 502 """Returns the length to add to the maximum encoded size.""" 503 return None 504 505 def max_encoded_size(self) -> str: 506 """Returns a constant expression for field's maximum encoded size.""" 507 size_call = '{}::{}({})'.format( 508 PROTOBUF_NAMESPACE, self._size_fn(), self.field_cast() 509 ) 510 511 size_length: Optional[str] = self._size_length() 512 if size_length is None: 513 return size_call 514 515 return f'{size_call} + {size_length}' 516 517 def include_in_scratch_size(self) -> bool: # pylint: disable=no-self-use 518 """Returns whether the field contributes to the scratch buffer size.""" 519 return False 520 521 522# 523# The following code defines write and read methods for each of the 524# complex protobuf types. 525# 526 527 528class SubMessageEncoderMethod(ProtoMethod): 529 """Method which returns a sub-message encoder.""" 530 531 def name(self) -> str: 532 return 'Get{}Encoder'.format(self._field.name()) 533 534 def return_type(self, from_root: bool = False) -> str: 535 return '{}::StreamEncoder'.format( 536 self._relative_type_namespace(from_root) 537 ) 538 539 def params(self) -> List[Tuple[str, str]]: 540 return [] 541 542 def body(self) -> List[str]: 543 line = 'return {}::StreamEncoder({}::GetNestedEncoder({}));'.format( 544 self._relative_type_namespace(), self._base_class, self.field_cast() 545 ) 546 return [line] 547 548 # Submessage methods are not defined within the class itself because the 549 # submessage class may not yet have been defined. 550 def in_class_definition(self) -> bool: 551 return False 552 553 554class SubMessageDecoderMethod(ReadMethod): 555 """Method which returns a sub-message decoder.""" 556 557 def name(self) -> str: 558 return 'Get{}Decoder'.format(self._field.name()) 559 560 def return_type(self, from_root: bool = False) -> str: 561 return '{}::StreamDecoder'.format( 562 self._relative_type_namespace(from_root) 563 ) 564 565 def _decoder_body(self) -> List[str]: 566 line = 'return {}::StreamDecoder(GetNestedDecoder());'.format( 567 self._relative_type_namespace() 568 ) 569 return [line] 570 571 # Submessage methods are not defined within the class itself because the 572 # submessage class may not yet have been defined. 573 def in_class_definition(self) -> bool: 574 return False 575 576 577class SubMessageProperty(MessageProperty): 578 """Property which contains a sub-message.""" 579 580 def _dependency_removed(self) -> bool: 581 """Returns true if the message dependency was removed to break a cycle. 582 583 Proto allows cycles between messages, but C++ doesn't allow cycles 584 between class references. So when we're forced to break one, the 585 struct member is replaced with a callback. 586 """ 587 type_node = self._field.type_node() 588 assert type_node is not None 589 return type_node in cast(ProtoMessage, self._scope).dependency_cycles() 590 591 def _elem_size_table_entry(self) -> str: 592 # Since messages can't be repeated (as we couldn't set callbacks), 593 # only field size is used. Set elem_size to 0 so space can be saved by 594 # not using more than 4 bits for it. 595 return '0' 596 597 def type_name(self, from_root: bool = False) -> str: 598 return '{}::Message'.format(self._relative_type_namespace(from_root)) 599 600 def use_callback(self) -> bool: 601 # Always use a callback for a message dependency removed to break a 602 # cycle, and for repeated fields, since in both cases there's no way 603 # to handle the size of nested field. 604 options = self._field.options() 605 assert options is not None 606 return ( 607 options.use_callback 608 or self._dependency_removed() 609 or self._field.is_repeated() 610 ) 611 612 def wire_type(self) -> str: 613 return 'kDelimited' 614 615 def sub_table(self) -> str: 616 if self.use_callback(): 617 return 'nullptr' 618 619 return '&{}::kMessageFields'.format(self._relative_type_namespace()) 620 621 def _size_fn(self) -> str: 622 # This uses the WithoutValue method to ensure that the maximum length 623 # of the delimited field size varint is used. This is because the nested 624 # message might include callbacks and be longer than we expect, and to 625 # account for scratch overhead when used with MemoryEncoder. 626 return 'SizeOfDelimitedFieldWithoutValue' 627 628 def _size_length(self) -> Optional[str]: 629 if self.use_callback(): 630 return None 631 632 return '{}::kMaxEncodedSizeBytes'.format( 633 self._relative_type_namespace() 634 ) 635 636 def include_in_scratch_size(self) -> bool: 637 return True 638 639 640class BytesReaderMethod(ReadMethod): 641 """Method which returns a bytes reader.""" 642 643 def name(self) -> str: 644 return 'Get{}Reader'.format(self._field.name()) 645 646 def return_type(self, from_root: bool = False) -> str: 647 return f'{PROTOBUF_NAMESPACE}::StreamDecoder::BytesReader' 648 649 def _decoder_fn(self) -> str: 650 return 'GetBytesReader' 651 652 653# 654# The following code defines write and read methods for each of the 655# primitive protobuf types. 656# 657 658 659class DoubleWriteMethod(WriteMethod): 660 """Method which writes a proto double value.""" 661 662 def params(self) -> List[Tuple[str, str]]: 663 return [('double', 'value')] 664 665 def _encoder_fn(self) -> str: 666 return 'WriteDouble' 667 668 669class PackedDoubleWriteMethod(PackedWriteMethod): 670 """Method which writes a packed list of doubles.""" 671 672 def params(self) -> List[Tuple[str, str]]: 673 return [('pw::span<const double>', 'values')] 674 675 def _encoder_fn(self) -> str: 676 return 'WritePackedDouble' 677 678 679class PackedDoubleWriteVectorMethod(PackedWriteMethod): 680 """Method which writes a packed vector of doubles.""" 681 682 def params(self) -> List[Tuple[str, str]]: 683 return [('const ::pw::Vector<double>&', 'values')] 684 685 def _encoder_fn(self) -> str: 686 return 'WriteRepeatedDouble' 687 688 689class DoubleReadMethod(ReadMethod): 690 """Method which reads a proto double value.""" 691 692 def _result_type(self) -> str: 693 return 'double' 694 695 def _decoder_fn(self) -> str: 696 return 'ReadDouble' 697 698 699class PackedDoubleReadMethod(PackedReadMethod): 700 """Method which reads packed double values.""" 701 702 def _result_type(self) -> str: 703 return 'double' 704 705 def _decoder_fn(self) -> str: 706 return 'ReadPackedDouble' 707 708 709class PackedDoubleReadVectorMethod(PackedReadVectorMethod): 710 """Method which reads packed double values.""" 711 712 def _result_type(self) -> str: 713 return 'double' 714 715 def _decoder_fn(self) -> str: 716 return 'ReadRepeatedDouble' 717 718 719class DoubleProperty(MessageProperty): 720 """Property which holds a proto double value.""" 721 722 def type_name(self, from_root: bool = False) -> str: 723 return 'double' 724 725 def wire_type(self) -> str: 726 return 'kFixed64' 727 728 def _size_fn(self) -> str: 729 return 'SizeOfFieldDouble' 730 731 732class FloatWriteMethod(WriteMethod): 733 """Method which writes a proto float value.""" 734 735 def params(self) -> List[Tuple[str, str]]: 736 return [('float', 'value')] 737 738 def _encoder_fn(self) -> str: 739 return 'WriteFloat' 740 741 742class PackedFloatWriteMethod(PackedWriteMethod): 743 """Method which writes a packed list of floats.""" 744 745 def params(self) -> List[Tuple[str, str]]: 746 return [('pw::span<const float>', 'values')] 747 748 def _encoder_fn(self) -> str: 749 return 'WritePackedFloat' 750 751 752class PackedFloatWriteVectorMethod(PackedWriteMethod): 753 """Method which writes a packed vector of floats.""" 754 755 def params(self) -> List[Tuple[str, str]]: 756 return [('const ::pw::Vector<float>&', 'values')] 757 758 def _encoder_fn(self) -> str: 759 return 'WriteRepeatedFloat' 760 761 762class FloatReadMethod(ReadMethod): 763 """Method which reads a proto float value.""" 764 765 def _result_type(self) -> str: 766 return 'float' 767 768 def _decoder_fn(self) -> str: 769 return 'ReadFloat' 770 771 772class PackedFloatReadMethod(PackedReadMethod): 773 """Method which reads packed float values.""" 774 775 def _result_type(self) -> str: 776 return 'float' 777 778 def _decoder_fn(self) -> str: 779 return 'ReadPackedFloat' 780 781 782class PackedFloatReadVectorMethod(PackedReadVectorMethod): 783 """Method which reads packed float values.""" 784 785 def _result_type(self) -> str: 786 return 'float' 787 788 def _decoder_fn(self) -> str: 789 return 'ReadRepeatedFloat' 790 791 792class FloatProperty(MessageProperty): 793 """Property which holds a proto float value.""" 794 795 def type_name(self, from_root: bool = False) -> str: 796 return 'float' 797 798 def wire_type(self) -> str: 799 return 'kFixed32' 800 801 def _size_fn(self) -> str: 802 return 'SizeOfFieldFloat' 803 804 805class Int32WriteMethod(WriteMethod): 806 """Method which writes a proto int32 value.""" 807 808 def params(self) -> List[Tuple[str, str]]: 809 return [('int32_t', 'value')] 810 811 def _encoder_fn(self) -> str: 812 return 'WriteInt32' 813 814 815class PackedInt32WriteMethod(PackedWriteMethod): 816 """Method which writes a packed list of int32.""" 817 818 def params(self) -> List[Tuple[str, str]]: 819 return [('pw::span<const int32_t>', 'values')] 820 821 def _encoder_fn(self) -> str: 822 return 'WritePackedInt32' 823 824 825class PackedInt32WriteVectorMethod(PackedWriteMethod): 826 """Method which writes a packed vector of int32.""" 827 828 def params(self) -> List[Tuple[str, str]]: 829 return [('const ::pw::Vector<int32_t>&', 'values')] 830 831 def _encoder_fn(self) -> str: 832 return 'WriteRepeatedInt32' 833 834 835class Int32ReadMethod(ReadMethod): 836 """Method which reads a proto int32 value.""" 837 838 def _result_type(self) -> str: 839 return 'int32_t' 840 841 def _decoder_fn(self) -> str: 842 return 'ReadInt32' 843 844 845class PackedInt32ReadMethod(PackedReadMethod): 846 """Method which reads packed int32 values.""" 847 848 def _result_type(self) -> str: 849 return 'int32_t' 850 851 def _decoder_fn(self) -> str: 852 return 'ReadPackedInt32' 853 854 855class PackedInt32ReadVectorMethod(PackedReadVectorMethod): 856 """Method which reads packed int32 values.""" 857 858 def _result_type(self) -> str: 859 return 'int32_t' 860 861 def _decoder_fn(self) -> str: 862 return 'ReadRepeatedInt32' 863 864 865class Int32Property(MessageProperty): 866 """Property which holds a proto int32 value.""" 867 868 def type_name(self, from_root: bool = False) -> str: 869 return 'int32_t' 870 871 def wire_type(self) -> str: 872 return 'kVarint' 873 874 def varint_decode_type(self) -> str: 875 return 'kNormal' 876 877 def _size_fn(self) -> str: 878 return 'SizeOfFieldInt32' 879 880 881class Sint32WriteMethod(WriteMethod): 882 """Method which writes a proto sint32 value.""" 883 884 def params(self) -> List[Tuple[str, str]]: 885 return [('int32_t', 'value')] 886 887 def _encoder_fn(self) -> str: 888 return 'WriteSint32' 889 890 891class PackedSint32WriteMethod(PackedWriteMethod): 892 """Method which writes a packed list of sint32.""" 893 894 def params(self) -> List[Tuple[str, str]]: 895 return [('pw::span<const int32_t>', 'values')] 896 897 def _encoder_fn(self) -> str: 898 return 'WritePackedSint32' 899 900 901class PackedSint32WriteVectorMethod(PackedWriteMethod): 902 """Method which writes a packed vector of sint32.""" 903 904 def params(self) -> List[Tuple[str, str]]: 905 return [('const ::pw::Vector<int32_t>&', 'values')] 906 907 def _encoder_fn(self) -> str: 908 return 'WriteRepeatedSint32' 909 910 911class Sint32ReadMethod(ReadMethod): 912 """Method which reads a proto sint32 value.""" 913 914 def _result_type(self) -> str: 915 return 'int32_t' 916 917 def _decoder_fn(self) -> str: 918 return 'ReadSint32' 919 920 921class PackedSint32ReadMethod(PackedReadMethod): 922 """Method which reads packed sint32 values.""" 923 924 def _result_type(self) -> str: 925 return 'int32_t' 926 927 def _decoder_fn(self) -> str: 928 return 'ReadPackedSint32' 929 930 931class PackedSint32ReadVectorMethod(PackedReadVectorMethod): 932 """Method which reads packed sint32 values.""" 933 934 def _result_type(self) -> str: 935 return 'int32_t' 936 937 def _decoder_fn(self) -> str: 938 return 'ReadRepeatedSint32' 939 940 941class Sint32Property(MessageProperty): 942 """Property which holds a proto sint32 value.""" 943 944 def type_name(self, from_root: bool = False) -> str: 945 return 'int32_t' 946 947 def wire_type(self) -> str: 948 return 'kVarint' 949 950 def varint_decode_type(self) -> str: 951 return 'kZigZag' 952 953 def _size_fn(self) -> str: 954 return 'SizeOfFieldSint32' 955 956 957class Sfixed32WriteMethod(WriteMethod): 958 """Method which writes a proto sfixed32 value.""" 959 960 def params(self) -> List[Tuple[str, str]]: 961 return [('int32_t', 'value')] 962 963 def _encoder_fn(self) -> str: 964 return 'WriteSfixed32' 965 966 967class PackedSfixed32WriteMethod(PackedWriteMethod): 968 """Method which writes a packed list of sfixed32.""" 969 970 def params(self) -> List[Tuple[str, str]]: 971 return [('pw::span<const int32_t>', 'values')] 972 973 def _encoder_fn(self) -> str: 974 return 'WritePackedSfixed32' 975 976 977class PackedSfixed32WriteVectorMethod(PackedWriteMethod): 978 """Method which writes a packed vector of sfixed32.""" 979 980 def params(self) -> List[Tuple[str, str]]: 981 return [('const ::pw::Vector<int32_t>&', 'values')] 982 983 def _encoder_fn(self) -> str: 984 return 'WriteRepeatedSfixed32' 985 986 987class Sfixed32ReadMethod(ReadMethod): 988 """Method which reads a proto sfixed32 value.""" 989 990 def _result_type(self) -> str: 991 return 'int32_t' 992 993 def _decoder_fn(self) -> str: 994 return 'ReadSfixed32' 995 996 997class PackedSfixed32ReadMethod(PackedReadMethod): 998 """Method which reads packed sfixed32 values.""" 999 1000 def _result_type(self) -> str: 1001 return 'int32_t' 1002 1003 def _decoder_fn(self) -> str: 1004 return 'ReadPackedSfixed32' 1005 1006 1007class PackedSfixed32ReadVectorMethod(PackedReadVectorMethod): 1008 """Method which reads packed sfixed32 values.""" 1009 1010 def _result_type(self) -> str: 1011 return 'int32_t' 1012 1013 def _decoder_fn(self) -> str: 1014 return 'ReadRepeatedSfixed32' 1015 1016 1017class Sfixed32Property(MessageProperty): 1018 """Property which holds a proto sfixed32 value.""" 1019 1020 def type_name(self, from_root: bool = False) -> str: 1021 return 'int32_t' 1022 1023 def wire_type(self) -> str: 1024 return 'kFixed32' 1025 1026 def _size_fn(self) -> str: 1027 return 'SizeOfFieldSfixed32' 1028 1029 1030class Int64WriteMethod(WriteMethod): 1031 """Method which writes a proto int64 value.""" 1032 1033 def params(self) -> List[Tuple[str, str]]: 1034 return [('int64_t', 'value')] 1035 1036 def _encoder_fn(self) -> str: 1037 return 'WriteInt64' 1038 1039 1040class PackedInt64WriteMethod(PackedWriteMethod): 1041 """Method which writes a packed list of int64.""" 1042 1043 def params(self) -> List[Tuple[str, str]]: 1044 return [('pw::span<const int64_t>', 'values')] 1045 1046 def _encoder_fn(self) -> str: 1047 return 'WritePackedInt64' 1048 1049 1050class PackedInt64WriteVectorMethod(PackedWriteMethod): 1051 """Method which writes a packed vector of int64.""" 1052 1053 def params(self) -> List[Tuple[str, str]]: 1054 return [('const ::pw::Vector<int64_t>&', 'values')] 1055 1056 def _encoder_fn(self) -> str: 1057 return 'WriteRepeatedInt64' 1058 1059 1060class Int64ReadMethod(ReadMethod): 1061 """Method which reads a proto int64 value.""" 1062 1063 def _result_type(self) -> str: 1064 return 'int64_t' 1065 1066 def _decoder_fn(self) -> str: 1067 return 'ReadInt64' 1068 1069 1070class PackedInt64ReadMethod(PackedReadMethod): 1071 """Method which reads packed int64 values.""" 1072 1073 def _result_type(self) -> str: 1074 return 'int64_t' 1075 1076 def _decoder_fn(self) -> str: 1077 return 'ReadPackedInt64' 1078 1079 1080class PackedInt64ReadVectorMethod(PackedReadVectorMethod): 1081 """Method which reads packed int64 values.""" 1082 1083 def _result_type(self) -> str: 1084 return 'int64_t' 1085 1086 def _decoder_fn(self) -> str: 1087 return 'ReadRepeatedInt64' 1088 1089 1090class Int64Property(MessageProperty): 1091 """Property which holds a proto int64 value.""" 1092 1093 def type_name(self, from_root: bool = False) -> str: 1094 return 'int64_t' 1095 1096 def wire_type(self) -> str: 1097 return 'kVarint' 1098 1099 def varint_decode_type(self) -> str: 1100 return 'kNormal' 1101 1102 def _size_fn(self) -> str: 1103 return 'SizeOfFieldInt64' 1104 1105 1106class Sint64WriteMethod(WriteMethod): 1107 """Method which writes a proto sint64 value.""" 1108 1109 def params(self) -> List[Tuple[str, str]]: 1110 return [('int64_t', 'value')] 1111 1112 def _encoder_fn(self) -> str: 1113 return 'WriteSint64' 1114 1115 1116class PackedSint64WriteMethod(PackedWriteMethod): 1117 """Method which writes a packst list of sint64.""" 1118 1119 def params(self) -> List[Tuple[str, str]]: 1120 return [('pw::span<const int64_t>', 'values')] 1121 1122 def _encoder_fn(self) -> str: 1123 return 'WritePackedSint64' 1124 1125 1126class PackedSint64WriteVectorMethod(PackedWriteMethod): 1127 """Method which writes a packed vector of sint64.""" 1128 1129 def params(self) -> List[Tuple[str, str]]: 1130 return [('const ::pw::Vector<int64_t>&', 'values')] 1131 1132 def _encoder_fn(self) -> str: 1133 return 'WriteRepeatedSint64' 1134 1135 1136class Sint64ReadMethod(ReadMethod): 1137 """Method which reads a proto sint64 value.""" 1138 1139 def _result_type(self) -> str: 1140 return 'int64_t' 1141 1142 def _decoder_fn(self) -> str: 1143 return 'ReadSint64' 1144 1145 1146class PackedSint64ReadMethod(PackedReadMethod): 1147 """Method which reads packed sint64 values.""" 1148 1149 def _result_type(self) -> str: 1150 return 'int64_t' 1151 1152 def _decoder_fn(self) -> str: 1153 return 'ReadPackedSint64' 1154 1155 1156class PackedSint64ReadVectorMethod(PackedReadVectorMethod): 1157 """Method which reads packed sint64 values.""" 1158 1159 def _result_type(self) -> str: 1160 return 'int64_t' 1161 1162 def _decoder_fn(self) -> str: 1163 return 'ReadRepeatedSint64' 1164 1165 1166class Sint64Property(MessageProperty): 1167 """Property which holds a proto sint64 value.""" 1168 1169 def type_name(self, from_root: bool = False) -> str: 1170 return 'int64_t' 1171 1172 def wire_type(self) -> str: 1173 return 'kVarint' 1174 1175 def varint_decode_type(self) -> str: 1176 return 'kZigZag' 1177 1178 def _size_fn(self) -> str: 1179 return 'SizeOfFieldSint64' 1180 1181 1182class Sfixed64WriteMethod(WriteMethod): 1183 """Method which writes a proto sfixed64 value.""" 1184 1185 def params(self) -> List[Tuple[str, str]]: 1186 return [('int64_t', 'value')] 1187 1188 def _encoder_fn(self) -> str: 1189 return 'WriteSfixed64' 1190 1191 1192class PackedSfixed64WriteMethod(PackedWriteMethod): 1193 """Method which writes a packed list of sfixed64.""" 1194 1195 def params(self) -> List[Tuple[str, str]]: 1196 return [('pw::span<const int64_t>', 'values')] 1197 1198 def _encoder_fn(self) -> str: 1199 return 'WritePackedSfixed4' 1200 1201 1202class PackedSfixed64WriteVectorMethod(PackedWriteMethod): 1203 """Method which writes a packed vector of sfixed64.""" 1204 1205 def params(self) -> List[Tuple[str, str]]: 1206 return [('const ::pw::Vector<int64_t>&', 'values')] 1207 1208 def _encoder_fn(self) -> str: 1209 return 'WriteRepeatedSfixed4' 1210 1211 1212class Sfixed64ReadMethod(ReadMethod): 1213 """Method which reads a proto sfixed64 value.""" 1214 1215 def _result_type(self) -> str: 1216 return 'int64_t' 1217 1218 def _decoder_fn(self) -> str: 1219 return 'ReadSfixed64' 1220 1221 1222class PackedSfixed64ReadMethod(PackedReadMethod): 1223 """Method which reads packed sfixed64 values.""" 1224 1225 def _result_type(self) -> str: 1226 return 'int64_t' 1227 1228 def _decoder_fn(self) -> str: 1229 return 'ReadPackedSfixed64' 1230 1231 1232class PackedSfixed64ReadVectorMethod(PackedReadVectorMethod): 1233 """Method which reads packed sfixed64 values.""" 1234 1235 def _result_type(self) -> str: 1236 return 'int64_t' 1237 1238 def _decoder_fn(self) -> str: 1239 return 'ReadRepeatedSfixed64' 1240 1241 1242class Sfixed64Property(MessageProperty): 1243 """Property which holds a proto sfixed64 value.""" 1244 1245 def type_name(self, from_root: bool = False) -> str: 1246 return 'int64_t' 1247 1248 def wire_type(self) -> str: 1249 return 'kFixed64' 1250 1251 def _size_fn(self) -> str: 1252 return 'SizeOfFieldSfixed64' 1253 1254 1255class Uint32WriteMethod(WriteMethod): 1256 """Method which writes a proto uint32 value.""" 1257 1258 def params(self) -> List[Tuple[str, str]]: 1259 return [('uint32_t', 'value')] 1260 1261 def _encoder_fn(self) -> str: 1262 return 'WriteUint32' 1263 1264 1265class PackedUint32WriteMethod(PackedWriteMethod): 1266 """Method which writes a packed list of uint32.""" 1267 1268 def params(self) -> List[Tuple[str, str]]: 1269 return [('pw::span<const uint32_t>', 'values')] 1270 1271 def _encoder_fn(self) -> str: 1272 return 'WritePackedUint32' 1273 1274 1275class PackedUint32WriteVectorMethod(PackedWriteMethod): 1276 """Method which writes a packed vector of uint32.""" 1277 1278 def params(self) -> List[Tuple[str, str]]: 1279 return [('const ::pw::Vector<uint32_t>&', 'values')] 1280 1281 def _encoder_fn(self) -> str: 1282 return 'WriteRepeatedUint32' 1283 1284 1285class Uint32ReadMethod(ReadMethod): 1286 """Method which reads a proto uint32 value.""" 1287 1288 def _result_type(self) -> str: 1289 return 'uint32_t' 1290 1291 def _decoder_fn(self) -> str: 1292 return 'ReadUint32' 1293 1294 1295class PackedUint32ReadMethod(PackedReadMethod): 1296 """Method which reads packed uint32 values.""" 1297 1298 def _result_type(self) -> str: 1299 return 'uint32_t' 1300 1301 def _decoder_fn(self) -> str: 1302 return 'ReadPackedUint32' 1303 1304 1305class PackedUint32ReadVectorMethod(PackedReadVectorMethod): 1306 """Method which reads packed uint32 values.""" 1307 1308 def _result_type(self) -> str: 1309 return 'uint32_t' 1310 1311 def _decoder_fn(self) -> str: 1312 return 'ReadRepeatedUint32' 1313 1314 1315class Uint32Property(MessageProperty): 1316 """Property which holds a proto uint32 value.""" 1317 1318 def type_name(self, from_root: bool = False) -> str: 1319 return 'uint32_t' 1320 1321 def wire_type(self) -> str: 1322 return 'kVarint' 1323 1324 def varint_decode_type(self) -> str: 1325 return 'kUnsigned' 1326 1327 def _size_fn(self) -> str: 1328 return 'SizeOfFieldUint32' 1329 1330 1331class Fixed32WriteMethod(WriteMethod): 1332 """Method which writes a proto fixed32 value.""" 1333 1334 def params(self) -> List[Tuple[str, str]]: 1335 return [('uint32_t', 'value')] 1336 1337 def _encoder_fn(self) -> str: 1338 return 'WriteFixed32' 1339 1340 1341class PackedFixed32WriteMethod(PackedWriteMethod): 1342 """Method which writes a packed list of fixed32.""" 1343 1344 def params(self) -> List[Tuple[str, str]]: 1345 return [('pw::span<const uint32_t>', 'values')] 1346 1347 def _encoder_fn(self) -> str: 1348 return 'WritePackedFixed32' 1349 1350 1351class PackedFixed32WriteVectorMethod(PackedWriteMethod): 1352 """Method which writes a packed vector of fixed32.""" 1353 1354 def params(self) -> List[Tuple[str, str]]: 1355 return [('const ::pw::Vector<uint32_t>&', 'values')] 1356 1357 def _encoder_fn(self) -> str: 1358 return 'WriteRepeatedFixed32' 1359 1360 1361class Fixed32ReadMethod(ReadMethod): 1362 """Method which reads a proto fixed32 value.""" 1363 1364 def _result_type(self) -> str: 1365 return 'uint32_t' 1366 1367 def _decoder_fn(self) -> str: 1368 return 'ReadFixed32' 1369 1370 1371class PackedFixed32ReadMethod(PackedReadMethod): 1372 """Method which reads packed fixed32 values.""" 1373 1374 def _result_type(self) -> str: 1375 return 'uint32_t' 1376 1377 def _decoder_fn(self) -> str: 1378 return 'ReadPackedFixed32' 1379 1380 1381class PackedFixed32ReadVectorMethod(PackedReadVectorMethod): 1382 """Method which reads packed fixed32 values.""" 1383 1384 def _result_type(self) -> str: 1385 return 'uint32_t' 1386 1387 def _decoder_fn(self) -> str: 1388 return 'ReadRepeatedFixed32' 1389 1390 1391class Fixed32Property(MessageProperty): 1392 """Property which holds a proto fixed32 value.""" 1393 1394 def type_name(self, from_root: bool = False) -> str: 1395 return 'uint32_t' 1396 1397 def wire_type(self) -> str: 1398 return 'kFixed32' 1399 1400 def _size_fn(self) -> str: 1401 return 'SizeOfFieldFixed32' 1402 1403 1404class Uint64WriteMethod(WriteMethod): 1405 """Method which writes a proto uint64 value.""" 1406 1407 def params(self) -> List[Tuple[str, str]]: 1408 return [('uint64_t', 'value')] 1409 1410 def _encoder_fn(self) -> str: 1411 return 'WriteUint64' 1412 1413 1414class PackedUint64WriteMethod(PackedWriteMethod): 1415 """Method which writes a packed list of uint64.""" 1416 1417 def params(self) -> List[Tuple[str, str]]: 1418 return [('pw::span<const uint64_t>', 'values')] 1419 1420 def _encoder_fn(self) -> str: 1421 return 'WritePackedUint64' 1422 1423 1424class PackedUint64WriteVectorMethod(PackedWriteMethod): 1425 """Method which writes a packed vector of uint64.""" 1426 1427 def params(self) -> List[Tuple[str, str]]: 1428 return [('const ::pw::Vector<uint64_t>&', 'values')] 1429 1430 def _encoder_fn(self) -> str: 1431 return 'WriteRepeatedUint64' 1432 1433 1434class Uint64ReadMethod(ReadMethod): 1435 """Method which reads a proto uint64 value.""" 1436 1437 def _result_type(self) -> str: 1438 return 'uint64_t' 1439 1440 def _decoder_fn(self) -> str: 1441 return 'ReadUint64' 1442 1443 1444class PackedUint64ReadMethod(PackedReadMethod): 1445 """Method which reads packed uint64 values.""" 1446 1447 def _result_type(self) -> str: 1448 return 'uint64_t' 1449 1450 def _decoder_fn(self) -> str: 1451 return 'ReadPackedUint64' 1452 1453 1454class PackedUint64ReadVectorMethod(PackedReadVectorMethod): 1455 """Method which reads packed uint64 values.""" 1456 1457 def _result_type(self) -> str: 1458 return 'uint64_t' 1459 1460 def _decoder_fn(self) -> str: 1461 return 'ReadRepeatedUint64' 1462 1463 1464class Uint64Property(MessageProperty): 1465 """Property which holds a proto uint64 value.""" 1466 1467 def type_name(self, from_root: bool = False) -> str: 1468 return 'uint64_t' 1469 1470 def wire_type(self) -> str: 1471 return 'kVarint' 1472 1473 def varint_decode_type(self) -> str: 1474 return 'kUnsigned' 1475 1476 def _size_fn(self) -> str: 1477 return 'SizeOfFieldUint64' 1478 1479 1480class Fixed64WriteMethod(WriteMethod): 1481 """Method which writes a proto fixed64 value.""" 1482 1483 def params(self) -> List[Tuple[str, str]]: 1484 return [('uint64_t', 'value')] 1485 1486 def _encoder_fn(self) -> str: 1487 return 'WriteFixed64' 1488 1489 1490class PackedFixed64WriteMethod(PackedWriteMethod): 1491 """Method which writes a packed list of fixed64.""" 1492 1493 def params(self) -> List[Tuple[str, str]]: 1494 return [('pw::span<const uint64_t>', 'values')] 1495 1496 def _encoder_fn(self) -> str: 1497 return 'WritePackedFixed64' 1498 1499 1500class PackedFixed64WriteVectorMethod(PackedWriteMethod): 1501 """Method which writes a packed list of fixed64.""" 1502 1503 def params(self) -> List[Tuple[str, str]]: 1504 return [('const ::pw::Vector<uint64_t>&', 'values')] 1505 1506 def _encoder_fn(self) -> str: 1507 return 'WriteRepeatedFixed64' 1508 1509 1510class Fixed64ReadMethod(ReadMethod): 1511 """Method which reads a proto fixed64 value.""" 1512 1513 def _result_type(self) -> str: 1514 return 'uint64_t' 1515 1516 def _decoder_fn(self) -> str: 1517 return 'ReadFixed64' 1518 1519 1520class PackedFixed64ReadMethod(PackedReadMethod): 1521 """Method which reads packed fixed64 values.""" 1522 1523 def _result_type(self) -> str: 1524 return 'uint64_t' 1525 1526 def _decoder_fn(self) -> str: 1527 return 'ReadPackedFixed64' 1528 1529 1530class PackedFixed64ReadVectorMethod(PackedReadVectorMethod): 1531 """Method which reads packed fixed64 values.""" 1532 1533 def _result_type(self) -> str: 1534 return 'uint64_t' 1535 1536 def _decoder_fn(self) -> str: 1537 return 'ReadRepeatedFixed64' 1538 1539 1540class Fixed64Property(MessageProperty): 1541 """Property which holds a proto fixed64 value.""" 1542 1543 def type_name(self, from_root: bool = False) -> str: 1544 return 'uint64_t' 1545 1546 def wire_type(self) -> str: 1547 return 'kFixed64' 1548 1549 def _size_fn(self) -> str: 1550 return 'SizeOfFieldFixed64' 1551 1552 1553class BoolWriteMethod(WriteMethod): 1554 """Method which writes a proto bool value.""" 1555 1556 def params(self) -> List[Tuple[str, str]]: 1557 return [('bool', 'value')] 1558 1559 def _encoder_fn(self) -> str: 1560 return 'WriteBool' 1561 1562 1563class PackedBoolWriteMethod(PackedWriteMethod): 1564 """Method which writes a packed list of bools.""" 1565 1566 def params(self) -> List[Tuple[str, str]]: 1567 return [('pw::span<const bool>', 'values')] 1568 1569 def _encoder_fn(self) -> str: 1570 return 'WritePackedBool' 1571 1572 1573class PackedBoolWriteVectorMethod(PackedWriteMethod): 1574 """Method which writes a packed vector of bools.""" 1575 1576 def params(self) -> List[Tuple[str, str]]: 1577 return [('const ::pw::Vector<bool>&', 'values')] 1578 1579 def _encoder_fn(self) -> str: 1580 return 'WriteRepeatedBool' 1581 1582 1583class BoolReadMethod(ReadMethod): 1584 """Method which reads a proto bool value.""" 1585 1586 def _result_type(self) -> str: 1587 return 'bool' 1588 1589 def _decoder_fn(self) -> str: 1590 return 'ReadBool' 1591 1592 1593class PackedBoolReadMethod(PackedReadMethod): 1594 """Method which reads packed bool values.""" 1595 1596 def _result_type(self) -> str: 1597 return 'bool' 1598 1599 def _decoder_fn(self) -> str: 1600 return 'ReadPackedBool' 1601 1602 1603class BoolProperty(MessageProperty): 1604 """Property which holds a proto bool value.""" 1605 1606 def type_name(self, from_root: bool = False) -> str: 1607 return 'bool' 1608 1609 def wire_type(self) -> str: 1610 return 'kVarint' 1611 1612 def varint_decode_type(self) -> str: 1613 return 'kUnsigned' 1614 1615 def _size_fn(self) -> str: 1616 return 'SizeOfFieldBool' 1617 1618 1619class BytesWriteMethod(WriteMethod): 1620 """Method which writes a proto bytes value.""" 1621 1622 def params(self) -> List[Tuple[str, str]]: 1623 return [('pw::span<const std::byte>', 'value')] 1624 1625 def _encoder_fn(self) -> str: 1626 return 'WriteBytes' 1627 1628 1629class BytesReadMethod(ReadMethod): 1630 """Method which reads a proto bytes value.""" 1631 1632 def return_type(self, from_root: bool = False) -> str: 1633 return '::pw::StatusWithSize' 1634 1635 def params(self) -> List[Tuple[str, str]]: 1636 return [('pw::span<std::byte>', 'out')] 1637 1638 def _decoder_fn(self) -> str: 1639 return 'ReadBytes' 1640 1641 1642class BytesProperty(MessageProperty): 1643 """Property which holds a proto bytes value.""" 1644 1645 def type_name(self, from_root: bool = False) -> str: 1646 return 'std::byte' 1647 1648 def use_callback(self) -> bool: 1649 return self.max_size() == 0 1650 1651 def max_size(self) -> int: 1652 if not self._field.is_repeated(): 1653 options = self._field.options() 1654 assert options is not None 1655 return options.max_size 1656 1657 return 0 1658 1659 def is_fixed_size(self) -> bool: 1660 if not self._field.is_repeated(): 1661 options = self._field.options() 1662 assert options is not None 1663 return options.fixed_size 1664 1665 return False 1666 1667 def wire_type(self) -> str: 1668 return 'kDelimited' 1669 1670 def _size_fn(self) -> str: 1671 # This uses the WithoutValue method to ensure that the maximum length 1672 # of the delimited field size varint is used. This accounts for scratch 1673 # overhead when used with MemoryEncoder. 1674 return 'SizeOfDelimitedFieldWithoutValue' 1675 1676 def _size_length(self) -> Optional[str]: 1677 if self.use_callback(): 1678 return None 1679 return f'{self.max_size()}' 1680 1681 1682class StringLenWriteMethod(WriteMethod): 1683 """Method which writes a proto string value with length.""" 1684 1685 def params(self) -> List[Tuple[str, str]]: 1686 return [('const char*', 'value'), ('size_t', 'len')] 1687 1688 def _encoder_fn(self) -> str: 1689 return 'WriteString' 1690 1691 1692class StringWriteMethod(WriteMethod): 1693 """Method which writes a proto string value.""" 1694 1695 def params(self) -> List[Tuple[str, str]]: 1696 return [('std::string_view', 'value')] 1697 1698 def _encoder_fn(self) -> str: 1699 return 'WriteString' 1700 1701 1702class StringReadMethod(ReadMethod): 1703 """Method which reads a proto string value.""" 1704 1705 def return_type(self, from_root: bool = False) -> str: 1706 return '::pw::StatusWithSize' 1707 1708 def params(self) -> List[Tuple[str, str]]: 1709 return [('pw::span<char>', 'out')] 1710 1711 def _decoder_fn(self) -> str: 1712 return 'ReadString' 1713 1714 1715class StringProperty(MessageProperty): 1716 """Property which holds a proto string value.""" 1717 1718 def type_name(self, from_root: bool = False) -> str: 1719 return 'char' 1720 1721 def use_callback(self) -> bool: 1722 return self.max_size() == 0 1723 1724 def max_size(self) -> int: 1725 if not self._field.is_repeated(): 1726 options = self._field.options() 1727 assert options is not None 1728 return options.max_size 1729 1730 return 0 1731 1732 def is_fixed_size(self) -> bool: 1733 return False 1734 1735 def wire_type(self) -> str: 1736 return 'kDelimited' 1737 1738 def is_string(self) -> bool: 1739 return True 1740 1741 @staticmethod 1742 def repeated_field_container(type_name: str, max_size: int) -> str: 1743 return f'::pw::InlineBasicString<{type_name}, {max_size}>' 1744 1745 def _size_fn(self) -> str: 1746 # This uses the WithoutValue method to ensure that the maximum length 1747 # of the delimited field size varint is used. This accounts for scratch 1748 # overhead when used with MemoryEncoder. 1749 return 'SizeOfDelimitedFieldWithoutValue' 1750 1751 def _size_length(self) -> Optional[str]: 1752 if self.use_callback(): 1753 return None 1754 return f'{self.max_size()}' 1755 1756 1757class EnumWriteMethod(WriteMethod): 1758 """Method which writes a proto enum value.""" 1759 1760 def params(self) -> List[Tuple[str, str]]: 1761 return [(self._relative_type_namespace(), 'value')] 1762 1763 def body(self) -> List[str]: 1764 line = ( 1765 'return {}::WriteUint32({}, ' 1766 'static_cast<uint32_t>(value));'.format( 1767 self._base_class, self.field_cast() 1768 ) 1769 ) 1770 return [line] 1771 1772 def in_class_definition(self) -> bool: 1773 return True 1774 1775 def _encoder_fn(self) -> str: 1776 raise NotImplementedError() 1777 1778 1779class PackedEnumWriteMethod(PackedWriteMethod): 1780 """Method which writes a packed list of enum.""" 1781 1782 def params(self) -> List[Tuple[str, str]]: 1783 return [ 1784 ( 1785 'pw::span<const {}>'.format(self._relative_type_namespace()), 1786 'values', 1787 ) 1788 ] 1789 1790 def body(self) -> List[str]: 1791 value_param = self.params()[0][1] 1792 line = ( 1793 f'return {self._base_class}::WritePackedUint32(' 1794 f'{self.field_cast()}, pw::span(reinterpret_cast<const uint32_t*>(' 1795 f'{value_param}.data()), {value_param}.size()));' 1796 ) 1797 return [line] 1798 1799 def in_class_definition(self) -> bool: 1800 return True 1801 1802 def _encoder_fn(self) -> str: 1803 raise NotImplementedError() 1804 1805 1806class PackedEnumWriteVectorMethod(PackedEnumWriteMethod): 1807 """Method which writes a packed vector of enum.""" 1808 1809 def params(self) -> List[Tuple[str, str]]: 1810 return [ 1811 ( 1812 'const ::pw::Vector<{}>&'.format( 1813 self._relative_type_namespace() 1814 ), 1815 'values', 1816 ) 1817 ] 1818 1819 1820class EnumReadMethod(ReadMethod): 1821 """Method which reads a proto enum value.""" 1822 1823 def _result_type(self): 1824 return self._relative_type_namespace() 1825 1826 def _decoder_body(self) -> List[str]: 1827 lines: List[str] = [] 1828 lines += ['::pw::Result<uint32_t> value = ReadUint32();'] 1829 lines += ['if (!value.ok()) {'] 1830 lines += [' return value.status();'] 1831 lines += ['}'] 1832 1833 lines += [f'return static_cast<{self._result_type()}>(value.value());'] 1834 return lines 1835 1836 1837class PackedEnumReadMethod(PackedReadMethod): 1838 """Method which reads packed enum values.""" 1839 1840 def _result_type(self): 1841 return self._relative_type_namespace() 1842 1843 def _decoder_body(self) -> List[str]: 1844 value_param = self.params()[0][1] 1845 return [ 1846 f'return ReadPackedUint32(' 1847 f'pw::span(reinterpret_cast<uint32_t*>({value_param}.data()), ' 1848 f'{value_param}.size()));' 1849 ] 1850 1851 1852class PackedEnumReadVectorMethod(PackedReadVectorMethod): 1853 """Method which reads packed enum values.""" 1854 1855 def _result_type(self): 1856 return self._relative_type_namespace() 1857 1858 def _decoder_body(self) -> List[str]: 1859 value_param = self.params()[0][1] 1860 return [ 1861 f'return ReadRepeatedUint32(' 1862 f'*reinterpret_cast<pw::Vector<uint32_t>*>(&{value_param}));' 1863 ] 1864 1865 1866class EnumProperty(MessageProperty): 1867 """Property which holds a proto enum value.""" 1868 1869 def type_name(self, from_root: bool = False) -> str: 1870 return self._relative_type_namespace(from_root=from_root) 1871 1872 def wire_type(self) -> str: 1873 return 'kVarint' 1874 1875 def varint_decode_type(self) -> str: 1876 return 'kUnsigned' 1877 1878 def _size_fn(self) -> str: 1879 return 'SizeOfFieldEnum' 1880 1881 1882# Mapping of protobuf field types to their method definitions. 1883PROTO_FIELD_WRITE_METHODS: Dict[int, List] = { 1884 descriptor_pb2.FieldDescriptorProto.TYPE_DOUBLE: [ 1885 DoubleWriteMethod, 1886 PackedDoubleWriteMethod, 1887 PackedDoubleWriteVectorMethod, 1888 ], 1889 descriptor_pb2.FieldDescriptorProto.TYPE_FLOAT: [ 1890 FloatWriteMethod, 1891 PackedFloatWriteMethod, 1892 PackedFloatWriteVectorMethod, 1893 ], 1894 descriptor_pb2.FieldDescriptorProto.TYPE_INT32: [ 1895 Int32WriteMethod, 1896 PackedInt32WriteMethod, 1897 PackedInt32WriteVectorMethod, 1898 ], 1899 descriptor_pb2.FieldDescriptorProto.TYPE_SINT32: [ 1900 Sint32WriteMethod, 1901 PackedSint32WriteMethod, 1902 PackedSint32WriteVectorMethod, 1903 ], 1904 descriptor_pb2.FieldDescriptorProto.TYPE_SFIXED32: [ 1905 Sfixed32WriteMethod, 1906 PackedSfixed32WriteMethod, 1907 PackedSfixed32WriteVectorMethod, 1908 ], 1909 descriptor_pb2.FieldDescriptorProto.TYPE_INT64: [ 1910 Int64WriteMethod, 1911 PackedInt64WriteMethod, 1912 PackedInt64WriteVectorMethod, 1913 ], 1914 descriptor_pb2.FieldDescriptorProto.TYPE_SINT64: [ 1915 Sint64WriteMethod, 1916 PackedSint64WriteMethod, 1917 PackedSint64WriteVectorMethod, 1918 ], 1919 descriptor_pb2.FieldDescriptorProto.TYPE_SFIXED64: [ 1920 Sfixed64WriteMethod, 1921 PackedSfixed64WriteMethod, 1922 PackedSfixed64WriteVectorMethod, 1923 ], 1924 descriptor_pb2.FieldDescriptorProto.TYPE_UINT32: [ 1925 Uint32WriteMethod, 1926 PackedUint32WriteMethod, 1927 PackedUint32WriteVectorMethod, 1928 ], 1929 descriptor_pb2.FieldDescriptorProto.TYPE_FIXED32: [ 1930 Fixed32WriteMethod, 1931 PackedFixed32WriteMethod, 1932 PackedFixed32WriteVectorMethod, 1933 ], 1934 descriptor_pb2.FieldDescriptorProto.TYPE_UINT64: [ 1935 Uint64WriteMethod, 1936 PackedUint64WriteMethod, 1937 PackedUint64WriteVectorMethod, 1938 ], 1939 descriptor_pb2.FieldDescriptorProto.TYPE_FIXED64: [ 1940 Fixed64WriteMethod, 1941 PackedFixed64WriteMethod, 1942 PackedFixed64WriteVectorMethod, 1943 ], 1944 descriptor_pb2.FieldDescriptorProto.TYPE_BOOL: [ 1945 BoolWriteMethod, 1946 PackedBoolWriteMethod, 1947 PackedBoolWriteVectorMethod, 1948 ], 1949 descriptor_pb2.FieldDescriptorProto.TYPE_BYTES: [BytesWriteMethod], 1950 descriptor_pb2.FieldDescriptorProto.TYPE_STRING: [ 1951 StringLenWriteMethod, 1952 StringWriteMethod, 1953 ], 1954 descriptor_pb2.FieldDescriptorProto.TYPE_MESSAGE: [SubMessageEncoderMethod], 1955 descriptor_pb2.FieldDescriptorProto.TYPE_ENUM: [ 1956 EnumWriteMethod, 1957 PackedEnumWriteMethod, 1958 PackedEnumWriteVectorMethod, 1959 ], 1960} 1961 1962PROTO_FIELD_READ_METHODS: Dict[int, List] = { 1963 descriptor_pb2.FieldDescriptorProto.TYPE_DOUBLE: [ 1964 DoubleReadMethod, 1965 PackedDoubleReadMethod, 1966 PackedDoubleReadVectorMethod, 1967 ], 1968 descriptor_pb2.FieldDescriptorProto.TYPE_FLOAT: [ 1969 FloatReadMethod, 1970 PackedFloatReadMethod, 1971 PackedFloatReadVectorMethod, 1972 ], 1973 descriptor_pb2.FieldDescriptorProto.TYPE_INT32: [ 1974 Int32ReadMethod, 1975 PackedInt32ReadMethod, 1976 PackedInt32ReadVectorMethod, 1977 ], 1978 descriptor_pb2.FieldDescriptorProto.TYPE_SINT32: [ 1979 Sint32ReadMethod, 1980 PackedSint32ReadMethod, 1981 PackedSint32ReadVectorMethod, 1982 ], 1983 descriptor_pb2.FieldDescriptorProto.TYPE_SFIXED32: [ 1984 Sfixed32ReadMethod, 1985 PackedSfixed32ReadMethod, 1986 PackedSfixed32ReadVectorMethod, 1987 ], 1988 descriptor_pb2.FieldDescriptorProto.TYPE_INT64: [ 1989 Int64ReadMethod, 1990 PackedInt64ReadMethod, 1991 PackedInt64ReadVectorMethod, 1992 ], 1993 descriptor_pb2.FieldDescriptorProto.TYPE_SINT64: [ 1994 Sint64ReadMethod, 1995 PackedSint64ReadMethod, 1996 PackedSint64ReadVectorMethod, 1997 ], 1998 descriptor_pb2.FieldDescriptorProto.TYPE_SFIXED64: [ 1999 Sfixed64ReadMethod, 2000 PackedSfixed64ReadMethod, 2001 PackedSfixed64ReadVectorMethod, 2002 ], 2003 descriptor_pb2.FieldDescriptorProto.TYPE_UINT32: [ 2004 Uint32ReadMethod, 2005 PackedUint32ReadMethod, 2006 PackedUint32ReadVectorMethod, 2007 ], 2008 descriptor_pb2.FieldDescriptorProto.TYPE_FIXED32: [ 2009 Fixed32ReadMethod, 2010 PackedFixed32ReadMethod, 2011 PackedFixed32ReadVectorMethod, 2012 ], 2013 descriptor_pb2.FieldDescriptorProto.TYPE_UINT64: [ 2014 Uint64ReadMethod, 2015 PackedUint64ReadMethod, 2016 PackedUint64ReadVectorMethod, 2017 ], 2018 descriptor_pb2.FieldDescriptorProto.TYPE_FIXED64: [ 2019 Fixed64ReadMethod, 2020 PackedFixed64ReadMethod, 2021 PackedFixed64ReadVectorMethod, 2022 ], 2023 descriptor_pb2.FieldDescriptorProto.TYPE_BOOL: [ 2024 BoolReadMethod, 2025 PackedBoolReadMethod, 2026 ], 2027 descriptor_pb2.FieldDescriptorProto.TYPE_BYTES: [ 2028 BytesReadMethod, 2029 BytesReaderMethod, 2030 ], 2031 descriptor_pb2.FieldDescriptorProto.TYPE_STRING: [ 2032 StringReadMethod, 2033 BytesReaderMethod, 2034 ], 2035 descriptor_pb2.FieldDescriptorProto.TYPE_MESSAGE: [SubMessageDecoderMethod], 2036 descriptor_pb2.FieldDescriptorProto.TYPE_ENUM: [ 2037 EnumReadMethod, 2038 PackedEnumReadMethod, 2039 PackedEnumReadVectorMethod, 2040 ], 2041} 2042 2043PROTO_FIELD_PROPERTIES: Dict[int, List] = { 2044 descriptor_pb2.FieldDescriptorProto.TYPE_DOUBLE: [DoubleProperty], 2045 descriptor_pb2.FieldDescriptorProto.TYPE_FLOAT: [FloatProperty], 2046 descriptor_pb2.FieldDescriptorProto.TYPE_INT32: [Int32Property], 2047 descriptor_pb2.FieldDescriptorProto.TYPE_SINT32: [Sint32Property], 2048 descriptor_pb2.FieldDescriptorProto.TYPE_SFIXED32: [Sfixed32Property], 2049 descriptor_pb2.FieldDescriptorProto.TYPE_INT64: [Int64Property], 2050 descriptor_pb2.FieldDescriptorProto.TYPE_SINT64: [Sint64Property], 2051 descriptor_pb2.FieldDescriptorProto.TYPE_SFIXED64: [Sfixed32Property], 2052 descriptor_pb2.FieldDescriptorProto.TYPE_UINT32: [Uint32Property], 2053 descriptor_pb2.FieldDescriptorProto.TYPE_FIXED32: [Fixed32Property], 2054 descriptor_pb2.FieldDescriptorProto.TYPE_UINT64: [Uint64Property], 2055 descriptor_pb2.FieldDescriptorProto.TYPE_FIXED64: [Fixed64Property], 2056 descriptor_pb2.FieldDescriptorProto.TYPE_BOOL: [BoolProperty], 2057 descriptor_pb2.FieldDescriptorProto.TYPE_BYTES: [BytesProperty], 2058 descriptor_pb2.FieldDescriptorProto.TYPE_STRING: [StringProperty], 2059 descriptor_pb2.FieldDescriptorProto.TYPE_MESSAGE: [SubMessageProperty], 2060 descriptor_pb2.FieldDescriptorProto.TYPE_ENUM: [EnumProperty], 2061} 2062 2063 2064def proto_field_methods(class_type: ClassType, field_type: int) -> List: 2065 return ( 2066 PROTO_FIELD_WRITE_METHODS[field_type] 2067 if class_type.is_encoder() 2068 else PROTO_FIELD_READ_METHODS[field_type] 2069 ) 2070 2071 2072def generate_class_for_message( 2073 message: ProtoMessage, 2074 root: ProtoNode, 2075 output: OutputFile, 2076 class_type: ClassType, 2077) -> None: 2078 """Creates a C++ class to encode or decoder a protobuf message.""" 2079 assert message.type() == ProtoNode.Type.MESSAGE 2080 2081 base_class_name = class_type.base_class_name() 2082 class_name = class_type.codegen_class_name() 2083 2084 # Message classes inherit from the base proto message class in codegen.h 2085 # and use its constructor. 2086 base_class = f'{PROTOBUF_NAMESPACE}::{base_class_name}' 2087 output.write_line( 2088 f'class {message.cpp_namespace(root=root)}::{class_name} ' 2089 f': public {base_class} {{' 2090 ) 2091 output.write_line(' public:') 2092 2093 with output.indent(): 2094 # Inherit the constructors from the base class. 2095 output.write_line(f'using {base_class}::{base_class_name};') 2096 2097 # Declare a move constructor that takes a base class. 2098 output.write_line( 2099 f'constexpr {class_name}({base_class}&& parent) ' 2100 f': {base_class}(std::move(parent)) {{}}' 2101 ) 2102 2103 # Allow MemoryEncoder& to be converted to StreamEncoder&. 2104 if class_type == ClassType.MEMORY_ENCODER: 2105 stream_type = ( 2106 f'::{message.cpp_namespace()}::' 2107 f'{ClassType.STREAMING_ENCODER.codegen_class_name()}' 2108 ) 2109 output.write_line( 2110 f'operator {stream_type}&() ' 2111 f' {{ return static_cast<{stream_type}&>(' 2112 f'*static_cast<{PROTOBUF_NAMESPACE}::StreamEncoder*>(this));}}' 2113 ) 2114 2115 # Add a typed Field() member to StreamDecoder 2116 if class_type == ClassType.STREAMING_DECODER: 2117 output.write_line() 2118 output.write_line('::pw::Result<Fields> Field() {') 2119 with output.indent(): 2120 output.write_line( 2121 '::pw::Result<uint32_t> result ' '= FieldNumber();' 2122 ) 2123 output.write_line('if (!result.ok()) {') 2124 with output.indent(): 2125 output.write_line('return result.status();') 2126 output.write_line('}') 2127 output.write_line('return static_cast<Fields>(result.value());') 2128 output.write_line('}') 2129 2130 # Generate entry for message table read or write methods. 2131 if class_type == ClassType.STREAMING_DECODER: 2132 output.write_line() 2133 output.write_line('::pw::Status Read(Message& message) {') 2134 with output.indent(): 2135 output.write_line( 2136 f'return {base_class}::Read(' 2137 'pw::as_writable_bytes(pw::span(&message, 1)), ' 2138 'kMessageFields);' 2139 ) 2140 output.write_line('}') 2141 elif class_type in ( 2142 ClassType.STREAMING_ENCODER, 2143 ClassType.MEMORY_ENCODER, 2144 ): 2145 output.write_line() 2146 output.write_line('::pw::Status Write(const Message& message) {') 2147 with output.indent(): 2148 output.write_line( 2149 f'return {base_class}::Write(' 2150 'pw::as_bytes(pw::span(&message, 1)), kMessageFields);' 2151 ) 2152 output.write_line('}') 2153 2154 # Generate methods for each of the message's fields. 2155 for field in message.fields(): 2156 for method_class in proto_field_methods(class_type, field.type()): 2157 method = method_class(field, message, root, base_class) 2158 if not method.should_appear(): 2159 continue 2160 2161 output.write_line() 2162 method_signature = ( 2163 f'{method.return_type()} ' 2164 f'{method.name()}({method.param_string()})' 2165 ) 2166 2167 if not method.in_class_definition(): 2168 # Method will be defined outside of the class at the end of 2169 # the file. 2170 output.write_line(f'{method_signature};') 2171 continue 2172 2173 output.write_line(f'{method_signature} {{') 2174 with output.indent(): 2175 for line in method.body(): 2176 output.write_line(line) 2177 output.write_line('}') 2178 2179 output.write_line('};') 2180 2181 2182def define_not_in_class_methods( 2183 message: ProtoMessage, 2184 root: ProtoNode, 2185 output: OutputFile, 2186 class_type: ClassType, 2187) -> None: 2188 """Defines methods for a message class that were previously declared.""" 2189 assert message.type() == ProtoNode.Type.MESSAGE 2190 2191 base_class_name = class_type.base_class_name() 2192 base_class = f'{PROTOBUF_NAMESPACE}::{base_class_name}' 2193 2194 for field in message.fields(): 2195 for method_class in proto_field_methods(class_type, field.type()): 2196 method = method_class(field, message, root, base_class) 2197 if not method.should_appear() or method.in_class_definition(): 2198 continue 2199 2200 output.write_line() 2201 class_name = ( 2202 f'{message.cpp_namespace(root=root)}::' 2203 f'{class_type.codegen_class_name()}' 2204 ) 2205 method_signature = ( 2206 f'inline {method.return_type(from_root=True)} ' 2207 f'{class_name}::{method.name()}({method.param_string()})' 2208 ) 2209 output.write_line(f'{method_signature} {{') 2210 with output.indent(): 2211 for line in method.body(): 2212 output.write_line(line) 2213 output.write_line('}') 2214 2215 2216def _common_value_prefix(proto_enum: ProtoEnum) -> str: 2217 """Calculate the common prefix of all enum values. 2218 2219 Given an enumeration: 2220 enum Thing { 2221 THING_ONE = 1; 2222 THING_TWO = 2; 2223 THING_THREE = 3; 2224 } 2225 2226 If will return 'THING_', resulting in generated "style" aliases of 2227 'kOne', 'kTwo', and 'kThree'. 2228 2229 The prefix is walked back to the last _, so that the enumeration: 2230 enum Activity { 2231 ACTIVITY_RUN = 1; 2232 ACTIVITY_ROW = 2; 2233 } 2234 2235 Returns 'ACTIVITY_' and not 'ACTIVITY_R'. 2236 """ 2237 if len(proto_enum.values()) <= 1: 2238 return '' 2239 2240 common_prefix = "".join( 2241 ch[0] 2242 for ch in takewhile( 2243 lambda ch: all(ch[0] == c for c in ch), 2244 zip(*[name for name, _ in proto_enum.values()]), 2245 ) 2246 ) 2247 (left, under, _) = common_prefix.rpartition('_') 2248 return left + under 2249 2250 2251def generate_code_for_enum( 2252 proto_enum: ProtoEnum, root: ProtoNode, output: OutputFile 2253) -> None: 2254 """Creates a C++ enum for a proto enum.""" 2255 assert proto_enum.type() == ProtoNode.Type.ENUM 2256 2257 common_prefix = _common_value_prefix(proto_enum) 2258 output.write_line( 2259 f'enum class {proto_enum.cpp_namespace(root=root)} ' f': uint32_t {{' 2260 ) 2261 with output.indent(): 2262 for name, number in proto_enum.values(): 2263 output.write_line(f'{name} = {number},') 2264 2265 style_name = 'k' + ProtoMessageField.upper_camel_case( 2266 name[len(common_prefix) :] 2267 ) 2268 if style_name != name: 2269 output.write_line(f'{style_name} = {name},') 2270 2271 output.write_line('};') 2272 2273 2274def generate_function_for_enum( 2275 proto_enum: ProtoEnum, root: ProtoNode, output: OutputFile 2276) -> None: 2277 """Creates a C++ validation function for a proto enum.""" 2278 assert proto_enum.type() == ProtoNode.Type.ENUM 2279 2280 enum_name = proto_enum.cpp_namespace(root=root) 2281 output.write_line( 2282 f'constexpr bool IsValid{enum_name}({enum_name} value) {{' 2283 ) 2284 with output.indent(): 2285 output.write_line('switch (value) {') 2286 with output.indent(): 2287 for name, _ in proto_enum.values(): 2288 output.write_line(f'case {enum_name}::{name}: return true;') 2289 output.write_line('default: return false;') 2290 output.write_line('}') 2291 output.write_line('}') 2292 2293 2294def generate_to_string_for_enum( 2295 proto_enum: ProtoEnum, root: ProtoNode, output: OutputFile 2296) -> None: 2297 """Creates a C++ to string function for a proto enum.""" 2298 assert proto_enum.type() == ProtoNode.Type.ENUM 2299 2300 enum_name = proto_enum.cpp_namespace(root=root) 2301 output.write_line( 2302 f'// Returns string names for {enum_name}; ' 2303 'returns "" for invalid enum values.' 2304 ) 2305 output.write_line( 2306 f'constexpr const char* {enum_name}ToString({enum_name} value) {{' 2307 ) 2308 with output.indent(): 2309 output.write_line('switch (value) {') 2310 with output.indent(): 2311 for name, _ in proto_enum.values(): 2312 output.write_line(f'case {enum_name}::{name}: return "{name}";') 2313 output.write_line('default: return "";') 2314 output.write_line('}') 2315 output.write_line('}') 2316 2317 2318def forward_declare( 2319 node: ProtoMessage, 2320 root: ProtoNode, 2321 output: OutputFile, 2322 exclude_legacy_snake_case_field_name_enums: bool, 2323) -> None: 2324 """Generates code forward-declaring entities in a message's namespace.""" 2325 namespace = node.cpp_namespace(root=root) 2326 output.write_line() 2327 output.write_line(f'namespace {namespace} {{') 2328 2329 # Define an enum defining each of the message's fields and their numbers. 2330 output.write_line('enum class Fields : uint32_t {') 2331 with output.indent(): 2332 for field in node.fields(): 2333 output.write_line(f'{field.enum_name()} = {field.number()},') 2334 2335 # Migration support from SNAKE_CASE to kConstantCase. 2336 if not exclude_legacy_snake_case_field_name_enums: 2337 for field in node.fields(): 2338 output.write_line( 2339 f'{field.legacy_enum_name()} = {field.number()},' 2340 ) 2341 2342 output.write_line('};') 2343 2344 # Declare the message's message struct. 2345 output.write_line() 2346 output.write_line('struct Message;') 2347 2348 # Declare the message's encoder classes. 2349 output.write_line() 2350 output.write_line('class StreamEncoder;') 2351 output.write_line('class MemoryEncoder;') 2352 2353 # Declare the message's decoder classes. 2354 output.write_line() 2355 output.write_line('class StreamDecoder;') 2356 2357 # Declare the message's enums. 2358 for child in node.children(): 2359 if child.type() == ProtoNode.Type.ENUM: 2360 output.write_line() 2361 generate_code_for_enum(cast(ProtoEnum, child), node, output) 2362 output.write_line() 2363 generate_function_for_enum(cast(ProtoEnum, child), node, output) 2364 output.write_line() 2365 generate_to_string_for_enum(cast(ProtoEnum, child), node, output) 2366 2367 output.write_line(f'}} // namespace {namespace}') 2368 2369 2370def generate_struct_for_message( 2371 message: ProtoMessage, root: ProtoNode, output: OutputFile 2372) -> None: 2373 """Creates a C++ struct to hold a protobuf message values.""" 2374 assert message.type() == ProtoNode.Type.MESSAGE 2375 2376 output.write_line(f'struct {message.cpp_namespace(root=root)}::Message {{') 2377 2378 # Generate members for each of the message's fields. 2379 with output.indent(): 2380 cmp: List[str] = [] 2381 for field in message.fields(): 2382 for property_class in PROTO_FIELD_PROPERTIES[field.type()]: 2383 prop = property_class(field, message, root) 2384 if not prop.should_appear(): 2385 continue 2386 2387 (type_name, name) = prop.struct_member() 2388 output.write_line(f'{type_name} {name};') 2389 2390 if not prop.use_callback(): 2391 cmp.append(f'{name} == other.{name}') 2392 2393 # Equality operator 2394 output.write_line() 2395 output.write_line('bool operator==(const Message& other) const {') 2396 with output.indent(): 2397 if len(cmp) > 0: 2398 output.write_line(f'return {" && ".join(cmp)};') 2399 else: 2400 output.write_line('static_cast<void>(other);') 2401 output.write_line('return true;') 2402 output.write_line('}') 2403 output.write_line( 2404 'bool operator!=(const Message& other) const ' 2405 '{ return !(*this == other); }' 2406 ) 2407 2408 output.write_line('};') 2409 2410 2411def generate_table_for_message( 2412 message: ProtoMessage, root: ProtoNode, output: OutputFile 2413) -> None: 2414 """Creates a C++ array to hold a protobuf message description.""" 2415 assert message.type() == ProtoNode.Type.MESSAGE 2416 2417 namespace = message.cpp_namespace(root=root) 2418 output.write_line(f'namespace {namespace} {{') 2419 2420 properties = [] 2421 for field in message.fields(): 2422 for property_class in PROTO_FIELD_PROPERTIES[field.type()]: 2423 prop = property_class(field, message, root) 2424 if prop.should_appear(): 2425 properties.append(prop) 2426 2427 output.write_line('PW_MODIFY_DIAGNOSTICS_PUSH();') 2428 output.write_line('PW_MODIFY_DIAGNOSTIC(ignored, "-Winvalid-offsetof");') 2429 2430 # Generate static_asserts to fail at compile-time if the structure cannot 2431 # be converted into a table. 2432 for idx, prop in enumerate(properties): 2433 if idx > 0: 2434 output.write_line( 2435 'static_assert(offsetof(Message, {}) > 0);'.format(prop.name()) 2436 ) 2437 output.write_line( 2438 'static_assert(sizeof(Message::{}) <= ' 2439 '{}::MessageField::kMaxFieldSize);'.format( 2440 prop.name(), _INTERNAL_NAMESPACE 2441 ) 2442 ) 2443 2444 # Zero-length C arrays are not permitted by the C++ standard, so only 2445 # generate the message fields array if it is non-empty. Zero-length 2446 # std::arrays are valid, but older toolchains may not support constexpr 2447 # std::arrays, even with -std=c++17. 2448 # 2449 # The kMessageFields span is generated whether the message has fields or 2450 # not. Only the span is referenced elsewhere. 2451 if properties: 2452 output.write_line( 2453 f'inline constexpr {_INTERNAL_NAMESPACE}::MessageField ' 2454 ' _kMessageFields[] = {' 2455 ) 2456 2457 # Generate members for each of the message's fields. 2458 with output.indent(): 2459 for prop in properties: 2460 table = ', '.join(prop.table_entry()) 2461 output.write_line(f'{{{table}}},') 2462 2463 output.write_line('};') 2464 output.write_line('PW_MODIFY_DIAGNOSTICS_POP();') 2465 2466 output.write_line( 2467 f'inline constexpr pw::span<const {_INTERNAL_NAMESPACE}::' 2468 'MessageField> kMessageFields = _kMessageFields;' 2469 ) 2470 2471 member_list = ', '.join( 2472 [f'message.{prop.struct_member()[1]}' for prop in properties] 2473 ) 2474 2475 # Generate std::tuple for Message fields. 2476 output.write_line( 2477 'inline constexpr auto ToTuple(const Message &message) {' 2478 ) 2479 output.write_line(f' return std::tie({member_list});') 2480 output.write_line('}') 2481 2482 # Generate mutable std::tuple for Message fields. 2483 output.write_line( 2484 'inline constexpr auto ToMutableTuple(Message &message) {' 2485 ) 2486 output.write_line(f' return std::tie({member_list});') 2487 output.write_line('}') 2488 else: 2489 output.write_line( 2490 f'inline constexpr pw::span<const {_INTERNAL_NAMESPACE}::' 2491 'MessageField> kMessageFields;' 2492 ) 2493 2494 output.write_line(f'}} // namespace {namespace}') 2495 2496 2497def generate_sizes_for_message( 2498 message: ProtoMessage, root: ProtoNode, output: OutputFile 2499) -> None: 2500 """Creates C++ constants for the encoded sizes of a protobuf message.""" 2501 assert message.type() == ProtoNode.Type.MESSAGE 2502 2503 namespace = message.cpp_namespace(root=root) 2504 output.write_line(f'namespace {namespace} {{') 2505 2506 property_sizes: List[str] = [] 2507 scratch_sizes: List[str] = [] 2508 for field in message.fields(): 2509 for property_class in PROTO_FIELD_PROPERTIES[field.type()]: 2510 prop = property_class(field, message, root) 2511 if not prop.should_appear(): 2512 continue 2513 2514 property_sizes.append(prop.max_encoded_size()) 2515 if prop.include_in_scratch_size(): 2516 scratch_sizes.append(prop.max_encoded_size()) 2517 2518 output.write_line('inline constexpr size_t kMaxEncodedSizeBytes =') 2519 with output.indent(): 2520 if len(property_sizes) == 0: 2521 output.write_line('0;') 2522 while len(property_sizes) > 0: 2523 property_size = property_sizes.pop(0) 2524 if len(property_sizes) > 0: 2525 output.write_line(f'{property_size} +') 2526 else: 2527 output.write_line(f'{property_size};') 2528 2529 output.write_line() 2530 output.write_line( 2531 'inline constexpr size_t kScratchBufferSizeBytes = ' 2532 + ('std::max({' if len(scratch_sizes) > 0 else '0;') 2533 ) 2534 with output.indent(): 2535 for scratch_size in scratch_sizes: 2536 output.write_line(f'{scratch_size},') 2537 if len(scratch_sizes) > 0: 2538 output.write_line('});') 2539 2540 output.write_line(f'}} // namespace {namespace}') 2541 2542 2543def generate_is_trivially_comparable_specialization( 2544 message: ProtoMessage, root: ProtoNode, output: OutputFile 2545) -> None: 2546 is_trivially_comparable = True 2547 for field in message.fields(): 2548 for property_class in PROTO_FIELD_PROPERTIES[field.type()]: 2549 prop = property_class(field, message, root) 2550 if not prop.should_appear(): 2551 continue 2552 2553 if prop.use_callback(): 2554 is_trivially_comparable = False 2555 break 2556 2557 qualified_message = f'{message.cpp_namespace()}::Message' 2558 2559 output.write_line('template <>') 2560 output.write_line( 2561 'constexpr bool IsTriviallyComparable' f'<{qualified_message}>() {{' 2562 ) 2563 output.write_line(f' return {str(is_trivially_comparable).lower()};') 2564 output.write_line('}') 2565 2566 2567def _proto_filename_to_generated_header(proto_file: str) -> str: 2568 """Returns the generated C++ header name for a .proto file.""" 2569 return os.path.splitext(proto_file)[0] + PROTO_H_EXTENSION 2570 2571 2572def dependency_sorted_messages(package: ProtoNode): 2573 """Yields the messages in the package sorted after their dependencies.""" 2574 2575 # Build the graph of dependencies between messages. 2576 graph: Dict[ProtoMessage, List[ProtoMessage]] = {} 2577 for node in package: 2578 if node.type() == ProtoNode.Type.MESSAGE: 2579 message = cast(ProtoMessage, node) 2580 graph[message] = message.dependencies() 2581 2582 # Repeatedly prepare a topological sort of the dependency graph, removing 2583 # a dependency each time a cycle is a detected, until we're left with a 2584 # fully directed graph. 2585 tsort: TopologicalSorter 2586 while True: 2587 tsort = TopologicalSorter(graph) 2588 try: 2589 tsort.prepare() 2590 break 2591 except CycleError as err: 2592 dependency, message = err.args[1][0], err.args[1][1] 2593 message.remove_dependency_cycle(dependency) 2594 graph[message] = message.dependencies() 2595 2596 # Yield the messages from the sorted graph. 2597 while tsort.is_active(): 2598 messages = tsort.get_ready() 2599 yield from messages 2600 tsort.done(*messages) 2601 2602 2603def generate_code_for_package( 2604 file_descriptor_proto, 2605 package: ProtoNode, 2606 output: OutputFile, 2607 suppress_legacy_namespace: bool, 2608 exclude_legacy_snake_case_field_name_enums: bool, 2609) -> None: 2610 """Generates code for a single .pb.h file corresponding to a .proto file.""" 2611 2612 assert package.type() == ProtoNode.Type.PACKAGE 2613 2614 output.write_line( 2615 f'// {os.path.basename(output.name())} automatically ' 2616 f'generated by {PLUGIN_NAME} {PLUGIN_VERSION}' 2617 ) 2618 output.write_line('#pragma once\n') 2619 output.write_line('#include <algorithm>') 2620 output.write_line('#include <array>') 2621 output.write_line('#include <cstddef>') 2622 output.write_line('#include <cstdint>') 2623 output.write_line('#include <optional>') 2624 output.write_line('#include <string_view>\n') 2625 output.write_line('#include "pw_assert/assert.h"') 2626 output.write_line('#include "pw_containers/vector.h"') 2627 output.write_line('#include "pw_preprocessor/compiler.h"') 2628 output.write_line('#include "pw_protobuf/encoder.h"') 2629 output.write_line('#include "pw_protobuf/internal/codegen.h"') 2630 output.write_line('#include "pw_protobuf/serialized_size.h"') 2631 output.write_line('#include "pw_protobuf/stream_decoder.h"') 2632 output.write_line('#include "pw_result/result.h"') 2633 output.write_line('#include "pw_span/span.h"') 2634 output.write_line('#include "pw_status/status.h"') 2635 output.write_line('#include "pw_status/status_with_size.h"') 2636 output.write_line('#include "pw_string/string.h"') 2637 2638 for imported_file in file_descriptor_proto.dependency: 2639 generated_header = _proto_filename_to_generated_header(imported_file) 2640 output.write_line(f'#include "{generated_header}"') 2641 2642 if package.cpp_namespace(): 2643 file_namespace = package.cpp_namespace() 2644 if file_namespace.startswith('::'): 2645 file_namespace = file_namespace[2:] 2646 2647 output.write_line(f'\nnamespace {file_namespace} {{') 2648 2649 for node in package: 2650 if node.type() == ProtoNode.Type.MESSAGE: 2651 forward_declare( 2652 cast(ProtoMessage, node), 2653 package, 2654 output, 2655 exclude_legacy_snake_case_field_name_enums, 2656 ) 2657 2658 # Define all top-level enums. 2659 for node in package.children(): 2660 if node.type() == ProtoNode.Type.ENUM: 2661 output.write_line() 2662 generate_code_for_enum(cast(ProtoEnum, node), package, output) 2663 output.write_line() 2664 generate_function_for_enum(cast(ProtoEnum, node), package, output) 2665 output.write_line() 2666 generate_to_string_for_enum(cast(ProtoEnum, node), package, output) 2667 2668 # Run through all messages, generating structs and classes for each. 2669 messages = [] 2670 for message in dependency_sorted_messages(package): 2671 output.write_line() 2672 generate_struct_for_message(message, package, output) 2673 output.write_line() 2674 generate_table_for_message(message, package, output) 2675 output.write_line() 2676 generate_sizes_for_message(message, package, output) 2677 output.write_line() 2678 generate_class_for_message( 2679 message, package, output, ClassType.STREAMING_ENCODER 2680 ) 2681 output.write_line() 2682 generate_class_for_message( 2683 message, package, output, ClassType.MEMORY_ENCODER 2684 ) 2685 output.write_line() 2686 generate_class_for_message( 2687 message, package, output, ClassType.STREAMING_DECODER 2688 ) 2689 messages.append(message) 2690 2691 # Run a second pass through the messages, this time defining all of the 2692 # methods which were previously only declared. 2693 for message in messages: 2694 define_not_in_class_methods( 2695 message, package, output, ClassType.STREAMING_ENCODER 2696 ) 2697 define_not_in_class_methods( 2698 message, package, output, ClassType.MEMORY_ENCODER 2699 ) 2700 define_not_in_class_methods( 2701 message, package, output, ClassType.STREAMING_DECODER 2702 ) 2703 2704 if package.cpp_namespace(): 2705 output.write_line(f'\n}} // namespace {package.cpp_namespace()}') 2706 2707 # Aliasing namespaces aren't needed if `package.cpp_namespace()` is 2708 # empty (since everyone can see the global namespace). It shouldn't 2709 # ever be empty, though. 2710 2711 if not suppress_legacy_namespace: 2712 output.write_line() 2713 output.write_line( 2714 '// Aliases for legacy pwpb codegen interface. ' 2715 'Please use the' 2716 ) 2717 output.write_line('// `::pwpb`-suffixed names in new code.') 2718 legacy_namespace = package.cpp_namespace(codegen_subnamespace=None) 2719 output.write_line(f'namespace {legacy_namespace} {{') 2720 output.write_line(f'using namespace ::{package.cpp_namespace()};') 2721 output.write_line(f'}} // namespace {legacy_namespace}') 2722 2723 # TODO(b/250945489) Remove this if possible 2724 output.write_line() 2725 output.write_line( 2726 '// Codegen implementation detail; do not use this namespace!' 2727 ) 2728 2729 external_lookup_namespace = "{}::{}".format( 2730 EXTERNAL_SYMBOL_WORKAROUND_NAMESPACE, 2731 package.cpp_namespace(codegen_subnamespace=None), 2732 ) 2733 2734 output.write_line(f'namespace {external_lookup_namespace} {{') 2735 output.write_line(f'using namespace ::{package.cpp_namespace()};') 2736 output.write_line(f'}} // namespace {external_lookup_namespace}') 2737 2738 if messages: 2739 proto_namespace = PROTOBUF_NAMESPACE.lstrip(':') 2740 output.write_line() 2741 output.write_line(f'namespace {proto_namespace} {{') 2742 2743 for message in messages: 2744 generate_is_trivially_comparable_specialization( 2745 message, package, output 2746 ) 2747 2748 output.write_line(f'}} // namespace {proto_namespace}') 2749 2750 2751def process_proto_file( 2752 proto_file, 2753 proto_options, 2754 suppress_legacy_namespace: bool, 2755 exclude_legacy_snake_case_field_name_enums: bool, 2756) -> Iterable[OutputFile]: 2757 """Generates code for a single .proto file.""" 2758 2759 # Two passes are made through the file. The first builds the tree of all 2760 # message/enum nodes, then the second creates the fields in each. This is 2761 # done as non-primitive fields need pointers to their types, which requires 2762 # the entire tree to have been parsed into memory. 2763 _, package_root = build_node_tree(proto_file, proto_options=proto_options) 2764 2765 output_filename = _proto_filename_to_generated_header(proto_file.name) 2766 output_file = OutputFile(output_filename) 2767 generate_code_for_package( 2768 proto_file, 2769 package_root, 2770 output_file, 2771 suppress_legacy_namespace, 2772 exclude_legacy_snake_case_field_name_enums, 2773 ) 2774 2775 return [output_file] 2776