1# Copyright 2023 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 Iterable, Type 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 FindMethod(ReadMethod): 350 def name(self) -> str: 351 return 'Find{}'.format(self._field.name()) 352 353 def params(self) -> list[tuple[str, str]]: 354 return [('::pw::ConstByteSpan', 'message')] 355 356 def body(self) -> list[str]: 357 lines: list[str] = [] 358 lines += [ 359 f'return {PROTOBUF_NAMESPACE}::{self._find_fn()}' 360 f'(message, {self.field_cast()});' 361 ] 362 return lines 363 364 def _find_fn(self) -> str: 365 """The find function to call. 366 367 Defined in subclasses. 368 369 e.g. 'FindUint32', 'FindBytes', etc. 370 """ 371 raise NotImplementedError() 372 373 374class FindStreamMethod(FindMethod): 375 def name(self) -> str: 376 return 'Find{}'.format(self._field.name()) 377 378 def params(self) -> list[tuple[str, str]]: 379 return [('::pw::stream::Reader&', 'message_stream')] 380 381 def body(self) -> list[str]: 382 lines: list[str] = [] 383 lines += [ 384 f'return {PROTOBUF_NAMESPACE}::{self._find_fn()}' 385 f'(message_stream, {self.field_cast()});' 386 ] 387 return lines 388 389 390class MessageProperty(ProtoMember): 391 """Base class for a C++ property for a field in a protobuf message.""" 392 393 def name(self) -> str: 394 return self._field.field_name() 395 396 def should_appear(self) -> bool: 397 return True 398 399 @abc.abstractmethod 400 def type_name(self, from_root: bool = False) -> str: 401 """Returns the type of the property, e.g. uint32_t.""" 402 403 @abc.abstractmethod 404 def wire_type(self) -> str: 405 """Returns the wire type of the property, e.g. kVarint.""" 406 407 def varint_decode_type(self) -> str: 408 """Returns the varint decoding type of the property, e.g. kZigZag. 409 410 Defined in subclasses that return kVarint for wire_type(). 411 """ 412 raise NotImplementedError() 413 414 def is_string(self) -> bool: # pylint: disable=no-self-use 415 """True if this field is a string field (as opposed to bytes).""" 416 return False 417 418 @staticmethod 419 def repeated_field_container(type_name: str, max_size: str) -> str: 420 """Returns the container type used for repeated fields. 421 422 Defaults to ::pw::Vector<type, max_size>. String fields use 423 ::pw::InlineString<max_size> instead. 424 """ 425 return f'::pw::Vector<{type_name}, {max_size}>' 426 427 def use_callback(self) -> bool: # pylint: disable=no-self-use 428 """Returns whether the decoder should use a callback.""" 429 options = self._field.options() 430 assert options is not None 431 return options.use_callback or ( 432 self._field.is_repeated() and self.max_size() == 0 433 ) 434 435 def is_optional(self) -> bool: 436 """Returns whether the decoder should use std::optional.""" 437 return ( 438 self._field.is_optional() 439 and self.max_size() == 0 440 and self.wire_type() != 'kDelimited' 441 ) 442 443 def is_repeated(self) -> bool: 444 return self._field.is_repeated() 445 446 def max_size(self) -> int: 447 """Returns the maximum size of the field.""" 448 if self._field.is_repeated(): 449 options = self._field.options() 450 assert options is not None 451 return options.max_count 452 453 return 0 454 455 def is_fixed_size(self) -> bool: 456 """Returns whether the decoder should use a fixed sized field.""" 457 if self._field.is_repeated(): 458 options = self._field.options() 459 assert options is not None 460 return options.fixed_count 461 462 return False 463 464 def sub_table(self) -> str: # pylint: disable=no-self-use 465 return '{}' 466 467 def struct_member_type(self, from_root: bool = False) -> str: 468 """Returns the structure member type.""" 469 if self.use_callback(): 470 return ( 471 f'{PROTOBUF_NAMESPACE}::Callback<StreamEncoder, StreamDecoder>' 472 ) 473 474 # Optional fields are wrapped in std::optional 475 if self.is_optional(): 476 return 'std::optional<{}>'.format(self.type_name(from_root)) 477 478 # Non-repeated fields have a member of just the type name. 479 max_size = self.max_size() 480 if max_size == 0: 481 return self.type_name(from_root) 482 483 # Fixed size fields use std::array. 484 if self.is_fixed_size(): 485 return 'std::array<{}, {}>'.format( 486 self.type_name(from_root), self.max_size_constant_name() 487 ) 488 489 # Otherwise prefer pw::Vector for repeated fields. 490 return self.repeated_field_container( 491 self.type_name(from_root), self.max_size_constant_name() 492 ) 493 494 def max_size_constant_name(self) -> str: 495 return f'k{self._field.name()}MaxSize' 496 497 def _varint_type_table_entry(self) -> str: 498 if self.wire_type() == 'kVarint': 499 return '{}::VarintType::{}'.format( 500 _INTERNAL_NAMESPACE, self.varint_decode_type() 501 ) 502 503 return f'static_cast<{_INTERNAL_NAMESPACE}::VarintType>(0)' 504 505 def _wire_type_table_entry(self) -> str: 506 return '{}::WireType::{}'.format(PROTOBUF_NAMESPACE, self.wire_type()) 507 508 def _elem_size_table_entry(self) -> str: 509 return 'sizeof({})'.format(self.type_name()) 510 511 def _bool_attr(self, attr: str) -> str: 512 """C++ string for a bool argument that includes the argument name.""" 513 return f'/*{attr}=*/{bool(getattr(self, attr)())}'.lower() 514 515 def table_entry(self) -> list[str]: 516 """Table entry.""" 517 return [ 518 self.field_cast(), 519 self._wire_type_table_entry(), 520 self._elem_size_table_entry(), 521 self._varint_type_table_entry(), 522 self._bool_attr('is_string'), 523 self._bool_attr('is_fixed_size'), 524 self._bool_attr('is_repeated'), 525 self._bool_attr('is_optional'), 526 self._bool_attr('use_callback'), 527 'offsetof(Message, {})'.format(self.name()), 528 'sizeof(Message::{})'.format(self.name()), 529 self.sub_table(), 530 ] 531 532 @abc.abstractmethod 533 def _size_fn(self) -> str: 534 """Returns the name of the field size function.""" 535 536 def _size_length(self) -> str | None: # pylint: disable=no-self-use 537 """Returns the length to add to the maximum encoded size.""" 538 return None 539 540 def max_encoded_size(self) -> str: 541 """Returns a constant expression for field's maximum encoded size.""" 542 size_call = '{}::{}({})'.format( 543 PROTOBUF_NAMESPACE, self._size_fn(), self.field_cast() 544 ) 545 546 size_length: str | None = self._size_length() 547 if size_length is None: 548 return size_call 549 550 return f'{size_call} + {size_length}' 551 552 def include_in_scratch_size(self) -> bool: # pylint: disable=no-self-use 553 """Returns whether the field contributes to the scratch buffer size.""" 554 return False 555 556 557# 558# The following code defines write and read methods for each of the 559# complex protobuf types. 560# 561 562 563class SubMessageEncoderMethod(ProtoMethod): 564 """Method which returns a sub-message encoder.""" 565 566 def name(self) -> str: 567 return 'Get{}Encoder'.format(self._field.name()) 568 569 def return_type(self, from_root: bool = False) -> str: 570 return '{}::StreamEncoder'.format( 571 self._relative_type_namespace(from_root) 572 ) 573 574 def params(self) -> list[tuple[str, str]]: 575 return [] 576 577 def body(self) -> list[str]: 578 line = 'return {}::StreamEncoder({}::GetNestedEncoder({}));'.format( 579 self._relative_type_namespace(), self._base_class, self.field_cast() 580 ) 581 return [line] 582 583 # Submessage methods are not defined within the class itself because the 584 # submessage class may not yet have been defined. 585 def in_class_definition(self) -> bool: 586 return False 587 588 589class SubMessageDecoderMethod(ReadMethod): 590 """Method which returns a sub-message decoder.""" 591 592 def name(self) -> str: 593 return 'Get{}Decoder'.format(self._field.name()) 594 595 def return_type(self, from_root: bool = False) -> str: 596 return '{}::StreamDecoder'.format( 597 self._relative_type_namespace(from_root) 598 ) 599 600 def _decoder_body(self) -> list[str]: 601 line = 'return {}::StreamDecoder(GetNestedDecoder());'.format( 602 self._relative_type_namespace() 603 ) 604 return [line] 605 606 # Submessage methods are not defined within the class itself because the 607 # submessage class may not yet have been defined. 608 def in_class_definition(self) -> bool: 609 return False 610 611 612class SubMessageFindMethod(FindMethod): 613 """Method which reads a proto submessage.""" 614 615 def _result_type(self) -> str: 616 return '::pw::ConstByteSpan' 617 618 def _find_fn(self) -> str: 619 return 'FindBytes' 620 621 622class SubMessageProperty(MessageProperty): 623 """Property which contains a sub-message.""" 624 625 def _dependency_removed(self) -> bool: 626 """Returns true if the message dependency was removed to break a cycle. 627 628 Proto allows cycles between messages, but C++ doesn't allow cycles 629 between class references. So when we're forced to break one, the 630 struct member is replaced with a callback. 631 """ 632 type_node = self._field.type_node() 633 assert type_node is not None 634 return type_node in cast(ProtoMessage, self._scope).dependency_cycles() 635 636 def _elem_size_table_entry(self) -> str: 637 # Since messages can't be repeated (as we couldn't set callbacks), 638 # only field size is used. Set elem_size to 0 so space can be saved by 639 # not using more than 4 bits for it. 640 return '0' 641 642 def type_name(self, from_root: bool = False) -> str: 643 return '{}::Message'.format(self._relative_type_namespace(from_root)) 644 645 def use_callback(self) -> bool: 646 # Always use a callback for a message dependency removed to break a 647 # cycle, and for repeated fields, since in both cases there's no way 648 # to handle the size of nested field. 649 options = self._field.options() 650 assert options is not None 651 return ( 652 options.use_callback 653 or self._dependency_removed() 654 or self._field.is_repeated() 655 ) 656 657 def wire_type(self) -> str: 658 return 'kDelimited' 659 660 def sub_table(self) -> str: 661 if self.use_callback(): 662 return 'nullptr' 663 664 return '&{}::kMessageFields'.format(self._relative_type_namespace()) 665 666 def _size_fn(self) -> str: 667 # This uses the WithoutValue method to ensure that the maximum length 668 # of the delimited field size varint is used. This is because the nested 669 # message might include callbacks and be longer than we expect, and to 670 # account for scratch overhead when used with MemoryEncoder. 671 return 'SizeOfDelimitedFieldWithoutValue' 672 673 def _size_length(self) -> str | None: 674 if self.use_callback(): 675 return None 676 677 return '{}::kMaxEncodedSizeBytes'.format( 678 self._relative_type_namespace() 679 ) 680 681 def include_in_scratch_size(self) -> bool: 682 return True 683 684 685class BytesReaderMethod(ReadMethod): 686 """Method which returns a bytes reader.""" 687 688 def name(self) -> str: 689 return 'Get{}Reader'.format(self._field.name()) 690 691 def return_type(self, from_root: bool = False) -> str: 692 return f'{PROTOBUF_NAMESPACE}::StreamDecoder::BytesReader' 693 694 def _decoder_fn(self) -> str: 695 return 'GetBytesReader' 696 697 698# 699# The following code defines write and read methods for each of the 700# primitive protobuf types. 701# 702 703 704class DoubleWriteMethod(WriteMethod): 705 """Method which writes a proto double value.""" 706 707 def params(self) -> list[tuple[str, str]]: 708 return [('double', 'value')] 709 710 def _encoder_fn(self) -> str: 711 return 'WriteDouble' 712 713 714class PackedDoubleWriteMethod(PackedWriteMethod): 715 """Method which writes a packed list of doubles.""" 716 717 def params(self) -> list[tuple[str, str]]: 718 return [('pw::span<const double>', 'values')] 719 720 def _encoder_fn(self) -> str: 721 return 'WritePackedDouble' 722 723 724class PackedDoubleWriteVectorMethod(PackedWriteMethod): 725 """Method which writes a packed vector of doubles.""" 726 727 def params(self) -> list[tuple[str, str]]: 728 return [('const ::pw::Vector<double>&', 'values')] 729 730 def _encoder_fn(self) -> str: 731 return 'WriteRepeatedDouble' 732 733 734class DoubleReadMethod(ReadMethod): 735 """Method which reads a proto double value.""" 736 737 def _result_type(self) -> str: 738 return 'double' 739 740 def _decoder_fn(self) -> str: 741 return 'ReadDouble' 742 743 744class PackedDoubleReadMethod(PackedReadMethod): 745 """Method which reads packed double values.""" 746 747 def _result_type(self) -> str: 748 return 'double' 749 750 def _decoder_fn(self) -> str: 751 return 'ReadPackedDouble' 752 753 754class PackedDoubleReadVectorMethod(PackedReadVectorMethod): 755 """Method which reads packed double values.""" 756 757 def _result_type(self) -> str: 758 return 'double' 759 760 def _decoder_fn(self) -> str: 761 return 'ReadRepeatedDouble' 762 763 764class DoubleFindMethod(FindMethod): 765 """Method which reads a proto double value.""" 766 767 def _result_type(self) -> str: 768 return 'double' 769 770 def _find_fn(self) -> str: 771 return 'FindDouble' 772 773 774class DoubleFindStreamMethod(FindStreamMethod): 775 """Method which reads a proto double value.""" 776 777 def _result_type(self) -> str: 778 return 'double' 779 780 def _find_fn(self) -> str: 781 return 'FindDouble' 782 783 784class DoubleProperty(MessageProperty): 785 """Property which holds a proto double value.""" 786 787 def type_name(self, from_root: bool = False) -> str: 788 return 'double' 789 790 def wire_type(self) -> str: 791 return 'kFixed64' 792 793 def _size_fn(self) -> str: 794 return 'SizeOfFieldDouble' 795 796 797class FloatWriteMethod(WriteMethod): 798 """Method which writes a proto float value.""" 799 800 def params(self) -> list[tuple[str, str]]: 801 return [('float', 'value')] 802 803 def _encoder_fn(self) -> str: 804 return 'WriteFloat' 805 806 807class PackedFloatWriteMethod(PackedWriteMethod): 808 """Method which writes a packed list of floats.""" 809 810 def params(self) -> list[tuple[str, str]]: 811 return [('pw::span<const float>', 'values')] 812 813 def _encoder_fn(self) -> str: 814 return 'WritePackedFloat' 815 816 817class PackedFloatWriteVectorMethod(PackedWriteMethod): 818 """Method which writes a packed vector of floats.""" 819 820 def params(self) -> list[tuple[str, str]]: 821 return [('const ::pw::Vector<float>&', 'values')] 822 823 def _encoder_fn(self) -> str: 824 return 'WriteRepeatedFloat' 825 826 827class FloatReadMethod(ReadMethod): 828 """Method which reads a proto float value.""" 829 830 def _result_type(self) -> str: 831 return 'float' 832 833 def _decoder_fn(self) -> str: 834 return 'ReadFloat' 835 836 837class PackedFloatReadMethod(PackedReadMethod): 838 """Method which reads packed float values.""" 839 840 def _result_type(self) -> str: 841 return 'float' 842 843 def _decoder_fn(self) -> str: 844 return 'ReadPackedFloat' 845 846 847class PackedFloatReadVectorMethod(PackedReadVectorMethod): 848 """Method which reads packed float values.""" 849 850 def _result_type(self) -> str: 851 return 'float' 852 853 def _decoder_fn(self) -> str: 854 return 'ReadRepeatedFloat' 855 856 857class FloatFindMethod(FindMethod): 858 """Method which reads a proto float value.""" 859 860 def _result_type(self) -> str: 861 return 'float' 862 863 def _find_fn(self) -> str: 864 return 'FindFloat' 865 866 867class FloatFindStreamMethod(FindStreamMethod): 868 """Method which reads a proto float value.""" 869 870 def _result_type(self) -> str: 871 return 'float' 872 873 def _find_fn(self) -> str: 874 return 'FindFloat' 875 876 877class FloatProperty(MessageProperty): 878 """Property which holds a proto float value.""" 879 880 def type_name(self, from_root: bool = False) -> str: 881 return 'float' 882 883 def wire_type(self) -> str: 884 return 'kFixed32' 885 886 def _size_fn(self) -> str: 887 return 'SizeOfFieldFloat' 888 889 890class Int32WriteMethod(WriteMethod): 891 """Method which writes a proto int32 value.""" 892 893 def params(self) -> list[tuple[str, str]]: 894 return [('int32_t', 'value')] 895 896 def _encoder_fn(self) -> str: 897 return 'WriteInt32' 898 899 900class PackedInt32WriteMethod(PackedWriteMethod): 901 """Method which writes a packed list of int32.""" 902 903 def params(self) -> list[tuple[str, str]]: 904 return [('pw::span<const int32_t>', 'values')] 905 906 def _encoder_fn(self) -> str: 907 return 'WritePackedInt32' 908 909 910class PackedInt32WriteVectorMethod(PackedWriteMethod): 911 """Method which writes a packed vector of int32.""" 912 913 def params(self) -> list[tuple[str, str]]: 914 return [('const ::pw::Vector<int32_t>&', 'values')] 915 916 def _encoder_fn(self) -> str: 917 return 'WriteRepeatedInt32' 918 919 920class Int32ReadMethod(ReadMethod): 921 """Method which reads a proto int32 value.""" 922 923 def _result_type(self) -> str: 924 return 'int32_t' 925 926 def _decoder_fn(self) -> str: 927 return 'ReadInt32' 928 929 930class PackedInt32ReadMethod(PackedReadMethod): 931 """Method which reads packed int32 values.""" 932 933 def _result_type(self) -> str: 934 return 'int32_t' 935 936 def _decoder_fn(self) -> str: 937 return 'ReadPackedInt32' 938 939 940class PackedInt32ReadVectorMethod(PackedReadVectorMethod): 941 """Method which reads packed int32 values.""" 942 943 def _result_type(self) -> str: 944 return 'int32_t' 945 946 def _decoder_fn(self) -> str: 947 return 'ReadRepeatedInt32' 948 949 950class Int32FindMethod(FindMethod): 951 """Method which reads a proto int32 value.""" 952 953 def _result_type(self) -> str: 954 return 'int32_t' 955 956 def _find_fn(self) -> str: 957 return 'FindInt32' 958 959 960class Int32FindStreamMethod(FindStreamMethod): 961 """Method which reads a proto int32 value.""" 962 963 def _result_type(self) -> str: 964 return 'int32_t' 965 966 def _find_fn(self) -> str: 967 return 'FindInt32' 968 969 970class Int32Property(MessageProperty): 971 """Property which holds a proto int32 value.""" 972 973 def type_name(self, from_root: bool = False) -> str: 974 return 'int32_t' 975 976 def wire_type(self) -> str: 977 return 'kVarint' 978 979 def varint_decode_type(self) -> str: 980 return 'kNormal' 981 982 def _size_fn(self) -> str: 983 return 'SizeOfFieldInt32' 984 985 986class Sint32WriteMethod(WriteMethod): 987 """Method which writes a proto sint32 value.""" 988 989 def params(self) -> list[tuple[str, str]]: 990 return [('int32_t', 'value')] 991 992 def _encoder_fn(self) -> str: 993 return 'WriteSint32' 994 995 996class PackedSint32WriteMethod(PackedWriteMethod): 997 """Method which writes a packed list of sint32.""" 998 999 def params(self) -> list[tuple[str, str]]: 1000 return [('pw::span<const int32_t>', 'values')] 1001 1002 def _encoder_fn(self) -> str: 1003 return 'WritePackedSint32' 1004 1005 1006class PackedSint32WriteVectorMethod(PackedWriteMethod): 1007 """Method which writes a packed vector of sint32.""" 1008 1009 def params(self) -> list[tuple[str, str]]: 1010 return [('const ::pw::Vector<int32_t>&', 'values')] 1011 1012 def _encoder_fn(self) -> str: 1013 return 'WriteRepeatedSint32' 1014 1015 1016class Sint32ReadMethod(ReadMethod): 1017 """Method which reads a proto sint32 value.""" 1018 1019 def _result_type(self) -> str: 1020 return 'int32_t' 1021 1022 def _decoder_fn(self) -> str: 1023 return 'ReadSint32' 1024 1025 1026class PackedSint32ReadMethod(PackedReadMethod): 1027 """Method which reads packed sint32 values.""" 1028 1029 def _result_type(self) -> str: 1030 return 'int32_t' 1031 1032 def _decoder_fn(self) -> str: 1033 return 'ReadPackedSint32' 1034 1035 1036class PackedSint32ReadVectorMethod(PackedReadVectorMethod): 1037 """Method which reads packed sint32 values.""" 1038 1039 def _result_type(self) -> str: 1040 return 'int32_t' 1041 1042 def _decoder_fn(self) -> str: 1043 return 'ReadRepeatedSint32' 1044 1045 1046class Sint32FindMethod(FindMethod): 1047 """Method which reads a proto sint32 value.""" 1048 1049 def _result_type(self) -> str: 1050 return 'int32_t' 1051 1052 def _find_fn(self) -> str: 1053 return 'FindSint32' 1054 1055 1056class Sint32FindStreamMethod(FindStreamMethod): 1057 """Method which reads a proto sint32 value.""" 1058 1059 def _result_type(self) -> str: 1060 return 'int32_t' 1061 1062 def _find_fn(self) -> str: 1063 return 'FindSint32' 1064 1065 1066class Sint32Property(MessageProperty): 1067 """Property which holds a proto sint32 value.""" 1068 1069 def type_name(self, from_root: bool = False) -> str: 1070 return 'int32_t' 1071 1072 def wire_type(self) -> str: 1073 return 'kVarint' 1074 1075 def varint_decode_type(self) -> str: 1076 return 'kZigZag' 1077 1078 def _size_fn(self) -> str: 1079 return 'SizeOfFieldSint32' 1080 1081 1082class Sfixed32WriteMethod(WriteMethod): 1083 """Method which writes a proto sfixed32 value.""" 1084 1085 def params(self) -> list[tuple[str, str]]: 1086 return [('int32_t', 'value')] 1087 1088 def _encoder_fn(self) -> str: 1089 return 'WriteSfixed32' 1090 1091 1092class PackedSfixed32WriteMethod(PackedWriteMethod): 1093 """Method which writes a packed list of sfixed32.""" 1094 1095 def params(self) -> list[tuple[str, str]]: 1096 return [('pw::span<const int32_t>', 'values')] 1097 1098 def _encoder_fn(self) -> str: 1099 return 'WritePackedSfixed32' 1100 1101 1102class PackedSfixed32WriteVectorMethod(PackedWriteMethod): 1103 """Method which writes a packed vector of sfixed32.""" 1104 1105 def params(self) -> list[tuple[str, str]]: 1106 return [('const ::pw::Vector<int32_t>&', 'values')] 1107 1108 def _encoder_fn(self) -> str: 1109 return 'WriteRepeatedSfixed32' 1110 1111 1112class Sfixed32ReadMethod(ReadMethod): 1113 """Method which reads a proto sfixed32 value.""" 1114 1115 def _result_type(self) -> str: 1116 return 'int32_t' 1117 1118 def _decoder_fn(self) -> str: 1119 return 'ReadSfixed32' 1120 1121 1122class PackedSfixed32ReadMethod(PackedReadMethod): 1123 """Method which reads packed sfixed32 values.""" 1124 1125 def _result_type(self) -> str: 1126 return 'int32_t' 1127 1128 def _decoder_fn(self) -> str: 1129 return 'ReadPackedSfixed32' 1130 1131 1132class PackedSfixed32ReadVectorMethod(PackedReadVectorMethod): 1133 """Method which reads packed sfixed32 values.""" 1134 1135 def _result_type(self) -> str: 1136 return 'int32_t' 1137 1138 def _decoder_fn(self) -> str: 1139 return 'ReadRepeatedSfixed32' 1140 1141 1142class Sfixed32FindMethod(FindMethod): 1143 """Method which reads a proto sfixed32 value.""" 1144 1145 def _result_type(self) -> str: 1146 return 'int32_t' 1147 1148 def _find_fn(self) -> str: 1149 return 'FindSfixed32' 1150 1151 1152class Sfixed32FindStreamMethod(FindStreamMethod): 1153 """Method which reads a proto sfixed32 value.""" 1154 1155 def _result_type(self) -> str: 1156 return 'int32_t' 1157 1158 def _find_fn(self) -> str: 1159 return 'FindSfixed32' 1160 1161 1162class Sfixed32Property(MessageProperty): 1163 """Property which holds a proto sfixed32 value.""" 1164 1165 def type_name(self, from_root: bool = False) -> str: 1166 return 'int32_t' 1167 1168 def wire_type(self) -> str: 1169 return 'kFixed32' 1170 1171 def _size_fn(self) -> str: 1172 return 'SizeOfFieldSfixed32' 1173 1174 1175class Int64WriteMethod(WriteMethod): 1176 """Method which writes a proto int64 value.""" 1177 1178 def params(self) -> list[tuple[str, str]]: 1179 return [('int64_t', 'value')] 1180 1181 def _encoder_fn(self) -> str: 1182 return 'WriteInt64' 1183 1184 1185class PackedInt64WriteMethod(PackedWriteMethod): 1186 """Method which writes a packed list of int64.""" 1187 1188 def params(self) -> list[tuple[str, str]]: 1189 return [('pw::span<const int64_t>', 'values')] 1190 1191 def _encoder_fn(self) -> str: 1192 return 'WritePackedInt64' 1193 1194 1195class PackedInt64WriteVectorMethod(PackedWriteMethod): 1196 """Method which writes a packed vector of int64.""" 1197 1198 def params(self) -> list[tuple[str, str]]: 1199 return [('const ::pw::Vector<int64_t>&', 'values')] 1200 1201 def _encoder_fn(self) -> str: 1202 return 'WriteRepeatedInt64' 1203 1204 1205class Int64ReadMethod(ReadMethod): 1206 """Method which reads a proto int64 value.""" 1207 1208 def _result_type(self) -> str: 1209 return 'int64_t' 1210 1211 def _decoder_fn(self) -> str: 1212 return 'ReadInt64' 1213 1214 1215class PackedInt64ReadMethod(PackedReadMethod): 1216 """Method which reads packed int64 values.""" 1217 1218 def _result_type(self) -> str: 1219 return 'int64_t' 1220 1221 def _decoder_fn(self) -> str: 1222 return 'ReadPackedInt64' 1223 1224 1225class PackedInt64ReadVectorMethod(PackedReadVectorMethod): 1226 """Method which reads packed int64 values.""" 1227 1228 def _result_type(self) -> str: 1229 return 'int64_t' 1230 1231 def _decoder_fn(self) -> str: 1232 return 'ReadRepeatedInt64' 1233 1234 1235class Int64FindMethod(FindMethod): 1236 """Method which reads a proto int64 value.""" 1237 1238 def _result_type(self) -> str: 1239 return 'int64_t' 1240 1241 def _find_fn(self) -> str: 1242 return 'FindInt64' 1243 1244 1245class Int64FindStreamMethod(FindStreamMethod): 1246 """Method which reads a proto int64 value.""" 1247 1248 def _result_type(self) -> str: 1249 return 'int64_t' 1250 1251 def _find_fn(self) -> str: 1252 return 'FindInt64' 1253 1254 1255class Int64Property(MessageProperty): 1256 """Property which holds a proto int64 value.""" 1257 1258 def type_name(self, from_root: bool = False) -> str: 1259 return 'int64_t' 1260 1261 def wire_type(self) -> str: 1262 return 'kVarint' 1263 1264 def varint_decode_type(self) -> str: 1265 return 'kNormal' 1266 1267 def _size_fn(self) -> str: 1268 return 'SizeOfFieldInt64' 1269 1270 1271class Sint64WriteMethod(WriteMethod): 1272 """Method which writes a proto sint64 value.""" 1273 1274 def params(self) -> list[tuple[str, str]]: 1275 return [('int64_t', 'value')] 1276 1277 def _encoder_fn(self) -> str: 1278 return 'WriteSint64' 1279 1280 1281class PackedSint64WriteMethod(PackedWriteMethod): 1282 """Method which writes a packst list of sint64.""" 1283 1284 def params(self) -> list[tuple[str, str]]: 1285 return [('pw::span<const int64_t>', 'values')] 1286 1287 def _encoder_fn(self) -> str: 1288 return 'WritePackedSint64' 1289 1290 1291class PackedSint64WriteVectorMethod(PackedWriteMethod): 1292 """Method which writes a packed vector of sint64.""" 1293 1294 def params(self) -> list[tuple[str, str]]: 1295 return [('const ::pw::Vector<int64_t>&', 'values')] 1296 1297 def _encoder_fn(self) -> str: 1298 return 'WriteRepeatedSint64' 1299 1300 1301class Sint64ReadMethod(ReadMethod): 1302 """Method which reads a proto sint64 value.""" 1303 1304 def _result_type(self) -> str: 1305 return 'int64_t' 1306 1307 def _decoder_fn(self) -> str: 1308 return 'ReadSint64' 1309 1310 1311class PackedSint64ReadMethod(PackedReadMethod): 1312 """Method which reads packed sint64 values.""" 1313 1314 def _result_type(self) -> str: 1315 return 'int64_t' 1316 1317 def _decoder_fn(self) -> str: 1318 return 'ReadPackedSint64' 1319 1320 1321class PackedSint64ReadVectorMethod(PackedReadVectorMethod): 1322 """Method which reads packed sint64 values.""" 1323 1324 def _result_type(self) -> str: 1325 return 'int64_t' 1326 1327 def _decoder_fn(self) -> str: 1328 return 'ReadRepeatedSint64' 1329 1330 1331class Sint64FindMethod(FindMethod): 1332 """Method which reads a proto sint64 value.""" 1333 1334 def _result_type(self) -> str: 1335 return 'int64_t' 1336 1337 def _find_fn(self) -> str: 1338 return 'FindSint64' 1339 1340 1341class Sint64FindStreamMethod(FindStreamMethod): 1342 """Method which reads a proto sint64 value.""" 1343 1344 def _result_type(self) -> str: 1345 return 'int64_t' 1346 1347 def _find_fn(self) -> str: 1348 return 'FindSint64' 1349 1350 1351class Sint64Property(MessageProperty): 1352 """Property which holds a proto sint64 value.""" 1353 1354 def type_name(self, from_root: bool = False) -> str: 1355 return 'int64_t' 1356 1357 def wire_type(self) -> str: 1358 return 'kVarint' 1359 1360 def varint_decode_type(self) -> str: 1361 return 'kZigZag' 1362 1363 def _size_fn(self) -> str: 1364 return 'SizeOfFieldSint64' 1365 1366 1367class Sfixed64WriteMethod(WriteMethod): 1368 """Method which writes a proto sfixed64 value.""" 1369 1370 def params(self) -> list[tuple[str, str]]: 1371 return [('int64_t', 'value')] 1372 1373 def _encoder_fn(self) -> str: 1374 return 'WriteSfixed64' 1375 1376 1377class PackedSfixed64WriteMethod(PackedWriteMethod): 1378 """Method which writes a packed list of sfixed64.""" 1379 1380 def params(self) -> list[tuple[str, str]]: 1381 return [('pw::span<const int64_t>', 'values')] 1382 1383 def _encoder_fn(self) -> str: 1384 return 'WritePackedSfixed4' 1385 1386 1387class PackedSfixed64WriteVectorMethod(PackedWriteMethod): 1388 """Method which writes a packed vector of sfixed64.""" 1389 1390 def params(self) -> list[tuple[str, str]]: 1391 return [('const ::pw::Vector<int64_t>&', 'values')] 1392 1393 def _encoder_fn(self) -> str: 1394 return 'WriteRepeatedSfixed4' 1395 1396 1397class Sfixed64ReadMethod(ReadMethod): 1398 """Method which reads a proto sfixed64 value.""" 1399 1400 def _result_type(self) -> str: 1401 return 'int64_t' 1402 1403 def _decoder_fn(self) -> str: 1404 return 'ReadSfixed64' 1405 1406 1407class PackedSfixed64ReadMethod(PackedReadMethod): 1408 """Method which reads packed sfixed64 values.""" 1409 1410 def _result_type(self) -> str: 1411 return 'int64_t' 1412 1413 def _decoder_fn(self) -> str: 1414 return 'ReadPackedSfixed64' 1415 1416 1417class PackedSfixed64ReadVectorMethod(PackedReadVectorMethod): 1418 """Method which reads packed sfixed64 values.""" 1419 1420 def _result_type(self) -> str: 1421 return 'int64_t' 1422 1423 def _decoder_fn(self) -> str: 1424 return 'ReadRepeatedSfixed64' 1425 1426 1427class Sfixed64FindMethod(FindMethod): 1428 """Method which reads a proto sfixed64 value.""" 1429 1430 def _result_type(self) -> str: 1431 return 'int64_t' 1432 1433 def _find_fn(self) -> str: 1434 return 'FindSfixed64' 1435 1436 1437class Sfixed64FindStreamMethod(FindStreamMethod): 1438 """Method which reads a proto sfixed64 value.""" 1439 1440 def _result_type(self) -> str: 1441 return 'int64_t' 1442 1443 def _find_fn(self) -> str: 1444 return 'FindSfixed64' 1445 1446 1447class Sfixed64Property(MessageProperty): 1448 """Property which holds a proto sfixed64 value.""" 1449 1450 def type_name(self, from_root: bool = False) -> str: 1451 return 'int64_t' 1452 1453 def wire_type(self) -> str: 1454 return 'kFixed64' 1455 1456 def _size_fn(self) -> str: 1457 return 'SizeOfFieldSfixed64' 1458 1459 1460class Uint32WriteMethod(WriteMethod): 1461 """Method which writes a proto uint32 value.""" 1462 1463 def params(self) -> list[tuple[str, str]]: 1464 return [('uint32_t', 'value')] 1465 1466 def _encoder_fn(self) -> str: 1467 return 'WriteUint32' 1468 1469 1470class PackedUint32WriteMethod(PackedWriteMethod): 1471 """Method which writes a packed list of uint32.""" 1472 1473 def params(self) -> list[tuple[str, str]]: 1474 return [('pw::span<const uint32_t>', 'values')] 1475 1476 def _encoder_fn(self) -> str: 1477 return 'WritePackedUint32' 1478 1479 1480class PackedUint32WriteVectorMethod(PackedWriteMethod): 1481 """Method which writes a packed vector of uint32.""" 1482 1483 def params(self) -> list[tuple[str, str]]: 1484 return [('const ::pw::Vector<uint32_t>&', 'values')] 1485 1486 def _encoder_fn(self) -> str: 1487 return 'WriteRepeatedUint32' 1488 1489 1490class Uint32ReadMethod(ReadMethod): 1491 """Method which reads a proto uint32 value.""" 1492 1493 def _result_type(self) -> str: 1494 return 'uint32_t' 1495 1496 def _decoder_fn(self) -> str: 1497 return 'ReadUint32' 1498 1499 1500class PackedUint32ReadMethod(PackedReadMethod): 1501 """Method which reads packed uint32 values.""" 1502 1503 def _result_type(self) -> str: 1504 return 'uint32_t' 1505 1506 def _decoder_fn(self) -> str: 1507 return 'ReadPackedUint32' 1508 1509 1510class PackedUint32ReadVectorMethod(PackedReadVectorMethod): 1511 """Method which reads packed uint32 values.""" 1512 1513 def _result_type(self) -> str: 1514 return 'uint32_t' 1515 1516 def _decoder_fn(self) -> str: 1517 return 'ReadRepeatedUint32' 1518 1519 1520class Uint32FindMethod(FindMethod): 1521 """Method which finds a proto uint32 value.""" 1522 1523 def _result_type(self) -> str: 1524 return 'uint32_t' 1525 1526 def _find_fn(self) -> str: 1527 return 'FindUint32' 1528 1529 1530class Uint32FindStreamMethod(FindStreamMethod): 1531 """Method which finds a proto uint32 value.""" 1532 1533 def _result_type(self) -> str: 1534 return 'uint32_t' 1535 1536 def _find_fn(self) -> str: 1537 return 'FindUint32' 1538 1539 1540class Uint32Property(MessageProperty): 1541 """Property which holds a proto uint32 value.""" 1542 1543 def type_name(self, from_root: bool = False) -> str: 1544 return 'uint32_t' 1545 1546 def wire_type(self) -> str: 1547 return 'kVarint' 1548 1549 def varint_decode_type(self) -> str: 1550 return 'kUnsigned' 1551 1552 def _size_fn(self) -> str: 1553 return 'SizeOfFieldUint32' 1554 1555 1556class Fixed32WriteMethod(WriteMethod): 1557 """Method which writes a proto fixed32 value.""" 1558 1559 def params(self) -> list[tuple[str, str]]: 1560 return [('uint32_t', 'value')] 1561 1562 def _encoder_fn(self) -> str: 1563 return 'WriteFixed32' 1564 1565 1566class PackedFixed32WriteMethod(PackedWriteMethod): 1567 """Method which writes a packed list of fixed32.""" 1568 1569 def params(self) -> list[tuple[str, str]]: 1570 return [('pw::span<const uint32_t>', 'values')] 1571 1572 def _encoder_fn(self) -> str: 1573 return 'WritePackedFixed32' 1574 1575 1576class PackedFixed32WriteVectorMethod(PackedWriteMethod): 1577 """Method which writes a packed vector of fixed32.""" 1578 1579 def params(self) -> list[tuple[str, str]]: 1580 return [('const ::pw::Vector<uint32_t>&', 'values')] 1581 1582 def _encoder_fn(self) -> str: 1583 return 'WriteRepeatedFixed32' 1584 1585 1586class Fixed32ReadMethod(ReadMethod): 1587 """Method which reads a proto fixed32 value.""" 1588 1589 def _result_type(self) -> str: 1590 return 'uint32_t' 1591 1592 def _decoder_fn(self) -> str: 1593 return 'ReadFixed32' 1594 1595 1596class PackedFixed32ReadMethod(PackedReadMethod): 1597 """Method which reads packed fixed32 values.""" 1598 1599 def _result_type(self) -> str: 1600 return 'uint32_t' 1601 1602 def _decoder_fn(self) -> str: 1603 return 'ReadPackedFixed32' 1604 1605 1606class PackedFixed32ReadVectorMethod(PackedReadVectorMethod): 1607 """Method which reads packed fixed32 values.""" 1608 1609 def _result_type(self) -> str: 1610 return 'uint32_t' 1611 1612 def _decoder_fn(self) -> str: 1613 return 'ReadRepeatedFixed32' 1614 1615 1616class Fixed32FindMethod(FindMethod): 1617 """Method which finds a proto fixed32 value.""" 1618 1619 def _result_type(self) -> str: 1620 return 'uint32_t' 1621 1622 def _find_fn(self) -> str: 1623 return 'FindFixed32' 1624 1625 1626class Fixed32FindStreamMethod(FindStreamMethod): 1627 """Method which finds a proto fixed32 value.""" 1628 1629 def _result_type(self) -> str: 1630 return 'uint32_t' 1631 1632 def _find_fn(self) -> str: 1633 return 'FindFixed32' 1634 1635 1636class Fixed32Property(MessageProperty): 1637 """Property which holds a proto fixed32 value.""" 1638 1639 def type_name(self, from_root: bool = False) -> str: 1640 return 'uint32_t' 1641 1642 def wire_type(self) -> str: 1643 return 'kFixed32' 1644 1645 def _size_fn(self) -> str: 1646 return 'SizeOfFieldFixed32' 1647 1648 1649class Uint64WriteMethod(WriteMethod): 1650 """Method which writes a proto uint64 value.""" 1651 1652 def params(self) -> list[tuple[str, str]]: 1653 return [('uint64_t', 'value')] 1654 1655 def _encoder_fn(self) -> str: 1656 return 'WriteUint64' 1657 1658 1659class PackedUint64WriteMethod(PackedWriteMethod): 1660 """Method which writes a packed list of uint64.""" 1661 1662 def params(self) -> list[tuple[str, str]]: 1663 return [('pw::span<const uint64_t>', 'values')] 1664 1665 def _encoder_fn(self) -> str: 1666 return 'WritePackedUint64' 1667 1668 1669class PackedUint64WriteVectorMethod(PackedWriteMethod): 1670 """Method which writes a packed vector of uint64.""" 1671 1672 def params(self) -> list[tuple[str, str]]: 1673 return [('const ::pw::Vector<uint64_t>&', 'values')] 1674 1675 def _encoder_fn(self) -> str: 1676 return 'WriteRepeatedUint64' 1677 1678 1679class Uint64ReadMethod(ReadMethod): 1680 """Method which reads a proto uint64 value.""" 1681 1682 def _result_type(self) -> str: 1683 return 'uint64_t' 1684 1685 def _decoder_fn(self) -> str: 1686 return 'ReadUint64' 1687 1688 1689class PackedUint64ReadMethod(PackedReadMethod): 1690 """Method which reads packed uint64 values.""" 1691 1692 def _result_type(self) -> str: 1693 return 'uint64_t' 1694 1695 def _decoder_fn(self) -> str: 1696 return 'ReadPackedUint64' 1697 1698 1699class PackedUint64ReadVectorMethod(PackedReadVectorMethod): 1700 """Method which reads packed uint64 values.""" 1701 1702 def _result_type(self) -> str: 1703 return 'uint64_t' 1704 1705 def _decoder_fn(self) -> str: 1706 return 'ReadRepeatedUint64' 1707 1708 1709class Uint64FindMethod(FindMethod): 1710 """Method which finds a proto uint64 value.""" 1711 1712 def _result_type(self) -> str: 1713 return 'uint64_t' 1714 1715 def _find_fn(self) -> str: 1716 return 'FindUint64' 1717 1718 1719class Uint64FindStreamMethod(FindStreamMethod): 1720 """Method which finds a proto uint64 value.""" 1721 1722 def _result_type(self) -> str: 1723 return 'uint64_t' 1724 1725 def _find_fn(self) -> str: 1726 return 'FindUint64' 1727 1728 1729class Uint64Property(MessageProperty): 1730 """Property which holds a proto uint64 value.""" 1731 1732 def type_name(self, from_root: bool = False) -> str: 1733 return 'uint64_t' 1734 1735 def wire_type(self) -> str: 1736 return 'kVarint' 1737 1738 def varint_decode_type(self) -> str: 1739 return 'kUnsigned' 1740 1741 def _size_fn(self) -> str: 1742 return 'SizeOfFieldUint64' 1743 1744 1745class Fixed64WriteMethod(WriteMethod): 1746 """Method which writes a proto fixed64 value.""" 1747 1748 def params(self) -> list[tuple[str, str]]: 1749 return [('uint64_t', 'value')] 1750 1751 def _encoder_fn(self) -> str: 1752 return 'WriteFixed64' 1753 1754 1755class PackedFixed64WriteMethod(PackedWriteMethod): 1756 """Method which writes a packed list of fixed64.""" 1757 1758 def params(self) -> list[tuple[str, str]]: 1759 return [('pw::span<const uint64_t>', 'values')] 1760 1761 def _encoder_fn(self) -> str: 1762 return 'WritePackedFixed64' 1763 1764 1765class PackedFixed64WriteVectorMethod(PackedWriteMethod): 1766 """Method which writes a packed list of fixed64.""" 1767 1768 def params(self) -> list[tuple[str, str]]: 1769 return [('const ::pw::Vector<uint64_t>&', 'values')] 1770 1771 def _encoder_fn(self) -> str: 1772 return 'WriteRepeatedFixed64' 1773 1774 1775class Fixed64ReadMethod(ReadMethod): 1776 """Method which reads a proto fixed64 value.""" 1777 1778 def _result_type(self) -> str: 1779 return 'uint64_t' 1780 1781 def _decoder_fn(self) -> str: 1782 return 'ReadFixed64' 1783 1784 1785class PackedFixed64ReadMethod(PackedReadMethod): 1786 """Method which reads packed fixed64 values.""" 1787 1788 def _result_type(self) -> str: 1789 return 'uint64_t' 1790 1791 def _decoder_fn(self) -> str: 1792 return 'ReadPackedFixed64' 1793 1794 1795class PackedFixed64ReadVectorMethod(PackedReadVectorMethod): 1796 """Method which reads packed fixed64 values.""" 1797 1798 def _result_type(self) -> str: 1799 return 'uint64_t' 1800 1801 def _decoder_fn(self) -> str: 1802 return 'ReadRepeatedFixed64' 1803 1804 1805class Fixed64FindMethod(FindMethod): 1806 """Method which finds a proto fixed64 value.""" 1807 1808 def _result_type(self) -> str: 1809 return 'uint64_t' 1810 1811 def _find_fn(self) -> str: 1812 return 'FindFixed64' 1813 1814 1815class Fixed64FindStreamMethod(FindStreamMethod): 1816 """Method which finds a proto fixed64 value.""" 1817 1818 def _result_type(self) -> str: 1819 return 'uint64_t' 1820 1821 def _find_fn(self) -> str: 1822 return 'FindFixed64' 1823 1824 1825class Fixed64Property(MessageProperty): 1826 """Property which holds a proto fixed64 value.""" 1827 1828 def type_name(self, from_root: bool = False) -> str: 1829 return 'uint64_t' 1830 1831 def wire_type(self) -> str: 1832 return 'kFixed64' 1833 1834 def _size_fn(self) -> str: 1835 return 'SizeOfFieldFixed64' 1836 1837 1838class BoolWriteMethod(WriteMethod): 1839 """Method which writes a proto bool value.""" 1840 1841 def params(self) -> list[tuple[str, str]]: 1842 return [('bool', 'value')] 1843 1844 def _encoder_fn(self) -> str: 1845 return 'WriteBool' 1846 1847 1848class PackedBoolWriteMethod(PackedWriteMethod): 1849 """Method which writes a packed list of bools.""" 1850 1851 def params(self) -> list[tuple[str, str]]: 1852 return [('pw::span<const bool>', 'values')] 1853 1854 def _encoder_fn(self) -> str: 1855 return 'WritePackedBool' 1856 1857 1858class PackedBoolWriteVectorMethod(PackedWriteMethod): 1859 """Method which writes a packed vector of bools.""" 1860 1861 def params(self) -> list[tuple[str, str]]: 1862 return [('const ::pw::Vector<bool>&', 'values')] 1863 1864 def _encoder_fn(self) -> str: 1865 return 'WriteRepeatedBool' 1866 1867 1868class BoolReadMethod(ReadMethod): 1869 """Method which reads a proto bool value.""" 1870 1871 def _result_type(self) -> str: 1872 return 'bool' 1873 1874 def _decoder_fn(self) -> str: 1875 return 'ReadBool' 1876 1877 1878class PackedBoolReadMethod(PackedReadMethod): 1879 """Method which reads packed bool values.""" 1880 1881 def _result_type(self) -> str: 1882 return 'bool' 1883 1884 def _decoder_fn(self) -> str: 1885 return 'ReadPackedBool' 1886 1887 1888class BoolFindMethod(FindMethod): 1889 """Method which finds a proto bool value.""" 1890 1891 def _result_type(self) -> str: 1892 return 'bool' 1893 1894 def _find_fn(self) -> str: 1895 return 'FindBool' 1896 1897 1898class BoolFindStreamMethod(FindStreamMethod): 1899 """Method which finds a proto bool value.""" 1900 1901 def _result_type(self) -> str: 1902 return 'bool' 1903 1904 def _find_fn(self) -> str: 1905 return 'FindBool' 1906 1907 1908class BoolProperty(MessageProperty): 1909 """Property which holds a proto bool value.""" 1910 1911 def type_name(self, from_root: bool = False) -> str: 1912 return 'bool' 1913 1914 def wire_type(self) -> str: 1915 return 'kVarint' 1916 1917 def varint_decode_type(self) -> str: 1918 return 'kUnsigned' 1919 1920 def _size_fn(self) -> str: 1921 return 'SizeOfFieldBool' 1922 1923 1924class BytesWriteMethod(WriteMethod): 1925 """Method which writes a proto bytes value.""" 1926 1927 def params(self) -> list[tuple[str, str]]: 1928 return [('pw::span<const std::byte>', 'value')] 1929 1930 def _encoder_fn(self) -> str: 1931 return 'WriteBytes' 1932 1933 1934class BytesReadMethod(ReadMethod): 1935 """Method which reads a proto bytes value.""" 1936 1937 def return_type(self, from_root: bool = False) -> str: 1938 return '::pw::StatusWithSize' 1939 1940 def params(self) -> list[tuple[str, str]]: 1941 return [('pw::span<std::byte>', 'out')] 1942 1943 def _decoder_fn(self) -> str: 1944 return 'ReadBytes' 1945 1946 1947class BytesFindMethod(FindMethod): 1948 """Method which reads a proto bytes value.""" 1949 1950 def _result_type(self) -> str: 1951 return '::pw::ConstByteSpan' 1952 1953 def _find_fn(self) -> str: 1954 return 'FindBytes' 1955 1956 1957class BytesFindStreamMethod(FindStreamMethod): 1958 """Method which reads a proto bytes value.""" 1959 1960 def return_type(self, from_root: bool = False) -> str: 1961 return '::pw::StatusWithSize' 1962 1963 def params(self) -> list[tuple[str, str]]: 1964 return [ 1965 ('::pw::stream::Reader&', 'message_stream'), 1966 ('::pw::ByteSpan', 'out'), 1967 ] 1968 1969 def body(self) -> list[str]: 1970 lines: list[str] = [] 1971 lines += [ 1972 f'return {PROTOBUF_NAMESPACE}::{self._find_fn()}' 1973 f'(message_stream, {self.field_cast()}, out);' 1974 ] 1975 return lines 1976 1977 def _find_fn(self) -> str: 1978 return 'FindBytes' 1979 1980 1981class BytesProperty(MessageProperty): 1982 """Property which holds a proto bytes value.""" 1983 1984 def type_name(self, from_root: bool = False) -> str: 1985 return 'std::byte' 1986 1987 def use_callback(self) -> bool: 1988 return self.max_size() == 0 1989 1990 def max_size(self) -> int: 1991 if not self._field.is_repeated(): 1992 options = self._field.options() 1993 assert options is not None 1994 return options.max_size 1995 1996 return 0 1997 1998 def is_fixed_size(self) -> bool: 1999 if not self._field.is_repeated(): 2000 options = self._field.options() 2001 assert options is not None 2002 return options.fixed_size 2003 2004 return False 2005 2006 def wire_type(self) -> str: 2007 return 'kDelimited' 2008 2009 def _size_fn(self) -> str: 2010 # This uses the WithoutValue method to ensure that the maximum length 2011 # of the delimited field size varint is used. This accounts for scratch 2012 # overhead when used with MemoryEncoder. 2013 return 'SizeOfDelimitedFieldWithoutValue' 2014 2015 def _size_length(self) -> str | None: 2016 if self.use_callback(): 2017 return None 2018 return self.max_size_constant_name() 2019 2020 2021class StringLenWriteMethod(WriteMethod): 2022 """Method which writes a proto string value with length.""" 2023 2024 def params(self) -> list[tuple[str, str]]: 2025 return [('const char*', 'value'), ('size_t', 'len')] 2026 2027 def _encoder_fn(self) -> str: 2028 return 'WriteString' 2029 2030 2031class StringWriteMethod(WriteMethod): 2032 """Method which writes a proto string value.""" 2033 2034 def params(self) -> list[tuple[str, str]]: 2035 return [('std::string_view', 'value')] 2036 2037 def _encoder_fn(self) -> str: 2038 return 'WriteString' 2039 2040 2041class StringReadMethod(ReadMethod): 2042 """Method which reads a proto string value.""" 2043 2044 def return_type(self, from_root: bool = False) -> str: 2045 return '::pw::StatusWithSize' 2046 2047 def params(self) -> list[tuple[str, str]]: 2048 return [('pw::span<char>', 'out')] 2049 2050 def _decoder_fn(self) -> str: 2051 return 'ReadString' 2052 2053 2054class StringFindMethod(FindMethod): 2055 """Method which reads a proto string value.""" 2056 2057 def _result_type(self) -> str: 2058 return 'std::string_view' 2059 2060 def _find_fn(self) -> str: 2061 return 'FindString' 2062 2063 2064class StringFindStreamMethod(FindStreamMethod): 2065 """Method which reads a proto string value.""" 2066 2067 def return_type(self, from_root: bool = False) -> str: 2068 return '::pw::StatusWithSize' 2069 2070 def params(self) -> list[tuple[str, str]]: 2071 return [ 2072 ('::pw::stream::Reader&', 'message_stream'), 2073 ('::pw::span<char>', 'out'), 2074 ] 2075 2076 def body(self) -> list[str]: 2077 lines: list[str] = [] 2078 lines += [ 2079 f'return {PROTOBUF_NAMESPACE}::{self._find_fn()}' 2080 f'(message_stream, {self.field_cast()}, out);' 2081 ] 2082 return lines 2083 2084 def _find_fn(self) -> str: 2085 return 'FindString' 2086 2087 2088class StringFindStreamMethodInlineString(FindStreamMethod): 2089 """Method which reads a proto string value to an InlineString.""" 2090 2091 def return_type(self, from_root: bool = False) -> str: 2092 return '::pw::StatusWithSize' 2093 2094 def params(self) -> list[tuple[str, str]]: 2095 return [ 2096 ('::pw::stream::Reader&', 'message_stream'), 2097 ('::pw::InlineString<>&', 'out'), 2098 ] 2099 2100 def body(self) -> list[str]: 2101 lines: list[str] = [] 2102 lines += [ 2103 f'return {PROTOBUF_NAMESPACE}::{self._find_fn()}' 2104 f'(message_stream, {self.field_cast()}, out);' 2105 ] 2106 return lines 2107 2108 def _find_fn(self) -> str: 2109 return 'FindString' 2110 2111 2112class StringProperty(MessageProperty): 2113 """Property which holds a proto string value.""" 2114 2115 def type_name(self, from_root: bool = False) -> str: 2116 return 'char' 2117 2118 def use_callback(self) -> bool: 2119 return self.max_size() == 0 2120 2121 def max_size(self) -> int: 2122 if not self._field.is_repeated(): 2123 options = self._field.options() 2124 assert options is not None 2125 return options.max_size 2126 2127 return 0 2128 2129 def is_fixed_size(self) -> bool: 2130 return False 2131 2132 def wire_type(self) -> str: 2133 return 'kDelimited' 2134 2135 def is_string(self) -> bool: 2136 return True 2137 2138 @staticmethod 2139 def repeated_field_container(type_name: str, max_size: str) -> str: 2140 return f'::pw::InlineBasicString<{type_name}, {max_size}>' 2141 2142 def _size_fn(self) -> str: 2143 # This uses the WithoutValue method to ensure that the maximum length 2144 # of the delimited field size varint is used. This accounts for scratch 2145 # overhead when used with MemoryEncoder. 2146 return 'SizeOfDelimitedFieldWithoutValue' 2147 2148 def _size_length(self) -> str | None: 2149 if self.use_callback(): 2150 return None 2151 return self.max_size_constant_name() 2152 2153 2154class EnumWriteMethod(WriteMethod): 2155 """Method which writes a proto enum value.""" 2156 2157 def params(self) -> list[tuple[str, str]]: 2158 return [(self._relative_type_namespace(), 'value')] 2159 2160 def body(self) -> list[str]: 2161 line = ( 2162 'return {}::WriteUint32({}, ' 2163 'static_cast<uint32_t>(value));'.format( 2164 self._base_class, self.field_cast() 2165 ) 2166 ) 2167 return [line] 2168 2169 def in_class_definition(self) -> bool: 2170 return True 2171 2172 def _encoder_fn(self) -> str: 2173 raise NotImplementedError() 2174 2175 2176class PackedEnumWriteMethod(PackedWriteMethod): 2177 """Method which writes a packed list of enum.""" 2178 2179 def params(self) -> list[tuple[str, str]]: 2180 return [ 2181 ( 2182 'pw::span<const {}>'.format(self._relative_type_namespace()), 2183 'values', 2184 ) 2185 ] 2186 2187 def body(self) -> list[str]: 2188 value_param = self.params()[0][1] 2189 line = ( 2190 f'return {self._base_class}::WritePackedUint32(' 2191 f'{self.field_cast()}, pw::span(reinterpret_cast<const uint32_t*>(' 2192 f'{value_param}.data()), {value_param}.size()));' 2193 ) 2194 return [line] 2195 2196 def in_class_definition(self) -> bool: 2197 return True 2198 2199 def _encoder_fn(self) -> str: 2200 raise NotImplementedError() 2201 2202 2203class PackedEnumWriteVectorMethod(PackedEnumWriteMethod): 2204 """Method which writes a packed vector of enum.""" 2205 2206 def params(self) -> list[tuple[str, str]]: 2207 return [ 2208 ( 2209 'const ::pw::Vector<{}>&'.format( 2210 self._relative_type_namespace() 2211 ), 2212 'values', 2213 ) 2214 ] 2215 2216 2217class EnumReadMethod(ReadMethod): 2218 """Method which reads a proto enum value.""" 2219 2220 def _result_type(self): 2221 return self._relative_type_namespace() 2222 2223 def _decoder_body(self) -> list[str]: 2224 lines: list[str] = [] 2225 lines += ['::pw::Result<uint32_t> value = ReadUint32();'] 2226 lines += ['if (!value.ok()) {'] 2227 lines += [' return value.status();'] 2228 lines += ['}'] 2229 2230 lines += [f'return static_cast<{self._result_type()}>(value.value());'] 2231 return lines 2232 2233 2234class PackedEnumReadMethod(PackedReadMethod): 2235 """Method which reads packed enum values.""" 2236 2237 def _result_type(self): 2238 return self._relative_type_namespace() 2239 2240 def _decoder_body(self) -> list[str]: 2241 value_param = self.params()[0][1] 2242 return [ 2243 f'return ReadPackedUint32(' 2244 f'pw::span(reinterpret_cast<uint32_t*>({value_param}.data()), ' 2245 f'{value_param}.size()));' 2246 ] 2247 2248 2249class PackedEnumReadVectorMethod(PackedReadVectorMethod): 2250 """Method which reads packed enum values.""" 2251 2252 def _result_type(self): 2253 return self._relative_type_namespace() 2254 2255 def _decoder_body(self) -> list[str]: 2256 value_param = self.params()[0][1] 2257 return [ 2258 f'return ReadRepeatedUint32(' 2259 f'*reinterpret_cast<pw::Vector<uint32_t>*>(&{value_param}));' 2260 ] 2261 2262 2263class EnumFindMethod(FindMethod): 2264 """Method which finds a proto enum value.""" 2265 2266 def _result_type(self) -> str: 2267 return self._relative_type_namespace() 2268 2269 def body(self) -> list[str]: 2270 lines: list[str] = [] 2271 lines += [ 2272 '::pw::Result<uint32_t> result = ' 2273 f'{PROTOBUF_NAMESPACE}::{self._find_fn()}' 2274 f'(message, {self.field_cast()});', 2275 'if (!result.ok()) {', 2276 ' return result.status();', 2277 '}', 2278 f'return static_cast<{self._result_type()}>(result.value());', 2279 ] 2280 return lines 2281 2282 def _find_fn(self) -> str: 2283 return 'FindUint32' 2284 2285 2286class EnumFindStreamMethod(FindStreamMethod): 2287 """Method which finds a proto enum value.""" 2288 2289 def _result_type(self) -> str: 2290 return self._relative_type_namespace() 2291 2292 def body(self) -> list[str]: 2293 lines: list[str] = [] 2294 lines += [ 2295 '::pw::Result<uint32_t> result = ' 2296 f'{PROTOBUF_NAMESPACE}::{self._find_fn()}' 2297 f'(message_stream, {self.field_cast()});', 2298 'if (!result.ok()) {', 2299 ' return result.status();', 2300 '}', 2301 f'return static_cast<{self._result_type()}>(result.value());', 2302 ] 2303 return lines 2304 2305 def _find_fn(self) -> str: 2306 return 'FindUint32' 2307 2308 2309class EnumProperty(MessageProperty): 2310 """Property which holds a proto enum value.""" 2311 2312 def type_name(self, from_root: bool = False) -> str: 2313 return self._relative_type_namespace(from_root=from_root) 2314 2315 def wire_type(self) -> str: 2316 return 'kVarint' 2317 2318 def varint_decode_type(self) -> str: 2319 return 'kUnsigned' 2320 2321 def _size_fn(self) -> str: 2322 return 'SizeOfFieldEnum' 2323 2324 2325# Mapping of protobuf field types to their method definitions. 2326PROTO_FIELD_WRITE_METHODS: dict[int, list] = { 2327 descriptor_pb2.FieldDescriptorProto.TYPE_DOUBLE: [ 2328 DoubleWriteMethod, 2329 PackedDoubleWriteMethod, 2330 PackedDoubleWriteVectorMethod, 2331 ], 2332 descriptor_pb2.FieldDescriptorProto.TYPE_FLOAT: [ 2333 FloatWriteMethod, 2334 PackedFloatWriteMethod, 2335 PackedFloatWriteVectorMethod, 2336 ], 2337 descriptor_pb2.FieldDescriptorProto.TYPE_INT32: [ 2338 Int32WriteMethod, 2339 PackedInt32WriteMethod, 2340 PackedInt32WriteVectorMethod, 2341 ], 2342 descriptor_pb2.FieldDescriptorProto.TYPE_SINT32: [ 2343 Sint32WriteMethod, 2344 PackedSint32WriteMethod, 2345 PackedSint32WriteVectorMethod, 2346 ], 2347 descriptor_pb2.FieldDescriptorProto.TYPE_SFIXED32: [ 2348 Sfixed32WriteMethod, 2349 PackedSfixed32WriteMethod, 2350 PackedSfixed32WriteVectorMethod, 2351 ], 2352 descriptor_pb2.FieldDescriptorProto.TYPE_INT64: [ 2353 Int64WriteMethod, 2354 PackedInt64WriteMethod, 2355 PackedInt64WriteVectorMethod, 2356 ], 2357 descriptor_pb2.FieldDescriptorProto.TYPE_SINT64: [ 2358 Sint64WriteMethod, 2359 PackedSint64WriteMethod, 2360 PackedSint64WriteVectorMethod, 2361 ], 2362 descriptor_pb2.FieldDescriptorProto.TYPE_SFIXED64: [ 2363 Sfixed64WriteMethod, 2364 PackedSfixed64WriteMethod, 2365 PackedSfixed64WriteVectorMethod, 2366 ], 2367 descriptor_pb2.FieldDescriptorProto.TYPE_UINT32: [ 2368 Uint32WriteMethod, 2369 PackedUint32WriteMethod, 2370 PackedUint32WriteVectorMethod, 2371 ], 2372 descriptor_pb2.FieldDescriptorProto.TYPE_FIXED32: [ 2373 Fixed32WriteMethod, 2374 PackedFixed32WriteMethod, 2375 PackedFixed32WriteVectorMethod, 2376 ], 2377 descriptor_pb2.FieldDescriptorProto.TYPE_UINT64: [ 2378 Uint64WriteMethod, 2379 PackedUint64WriteMethod, 2380 PackedUint64WriteVectorMethod, 2381 ], 2382 descriptor_pb2.FieldDescriptorProto.TYPE_FIXED64: [ 2383 Fixed64WriteMethod, 2384 PackedFixed64WriteMethod, 2385 PackedFixed64WriteVectorMethod, 2386 ], 2387 descriptor_pb2.FieldDescriptorProto.TYPE_BOOL: [ 2388 BoolWriteMethod, 2389 PackedBoolWriteMethod, 2390 PackedBoolWriteVectorMethod, 2391 ], 2392 descriptor_pb2.FieldDescriptorProto.TYPE_BYTES: [BytesWriteMethod], 2393 descriptor_pb2.FieldDescriptorProto.TYPE_STRING: [ 2394 StringLenWriteMethod, 2395 StringWriteMethod, 2396 ], 2397 descriptor_pb2.FieldDescriptorProto.TYPE_MESSAGE: [SubMessageEncoderMethod], 2398 descriptor_pb2.FieldDescriptorProto.TYPE_ENUM: [ 2399 EnumWriteMethod, 2400 PackedEnumWriteMethod, 2401 PackedEnumWriteVectorMethod, 2402 ], 2403} 2404 2405PROTO_FIELD_READ_METHODS: dict[int, list] = { 2406 descriptor_pb2.FieldDescriptorProto.TYPE_DOUBLE: [ 2407 DoubleReadMethod, 2408 PackedDoubleReadMethod, 2409 PackedDoubleReadVectorMethod, 2410 ], 2411 descriptor_pb2.FieldDescriptorProto.TYPE_FLOAT: [ 2412 FloatReadMethod, 2413 PackedFloatReadMethod, 2414 PackedFloatReadVectorMethod, 2415 ], 2416 descriptor_pb2.FieldDescriptorProto.TYPE_INT32: [ 2417 Int32ReadMethod, 2418 PackedInt32ReadMethod, 2419 PackedInt32ReadVectorMethod, 2420 ], 2421 descriptor_pb2.FieldDescriptorProto.TYPE_SINT32: [ 2422 Sint32ReadMethod, 2423 PackedSint32ReadMethod, 2424 PackedSint32ReadVectorMethod, 2425 ], 2426 descriptor_pb2.FieldDescriptorProto.TYPE_SFIXED32: [ 2427 Sfixed32ReadMethod, 2428 PackedSfixed32ReadMethod, 2429 PackedSfixed32ReadVectorMethod, 2430 ], 2431 descriptor_pb2.FieldDescriptorProto.TYPE_INT64: [ 2432 Int64ReadMethod, 2433 PackedInt64ReadMethod, 2434 PackedInt64ReadVectorMethod, 2435 ], 2436 descriptor_pb2.FieldDescriptorProto.TYPE_SINT64: [ 2437 Sint64ReadMethod, 2438 PackedSint64ReadMethod, 2439 PackedSint64ReadVectorMethod, 2440 ], 2441 descriptor_pb2.FieldDescriptorProto.TYPE_SFIXED64: [ 2442 Sfixed64ReadMethod, 2443 PackedSfixed64ReadMethod, 2444 PackedSfixed64ReadVectorMethod, 2445 ], 2446 descriptor_pb2.FieldDescriptorProto.TYPE_UINT32: [ 2447 Uint32ReadMethod, 2448 PackedUint32ReadMethod, 2449 PackedUint32ReadVectorMethod, 2450 ], 2451 descriptor_pb2.FieldDescriptorProto.TYPE_FIXED32: [ 2452 Fixed32ReadMethod, 2453 PackedFixed32ReadMethod, 2454 PackedFixed32ReadVectorMethod, 2455 ], 2456 descriptor_pb2.FieldDescriptorProto.TYPE_UINT64: [ 2457 Uint64ReadMethod, 2458 PackedUint64ReadMethod, 2459 PackedUint64ReadVectorMethod, 2460 ], 2461 descriptor_pb2.FieldDescriptorProto.TYPE_FIXED64: [ 2462 Fixed64ReadMethod, 2463 PackedFixed64ReadMethod, 2464 PackedFixed64ReadVectorMethod, 2465 ], 2466 descriptor_pb2.FieldDescriptorProto.TYPE_BOOL: [ 2467 BoolReadMethod, 2468 PackedBoolReadMethod, 2469 ], 2470 descriptor_pb2.FieldDescriptorProto.TYPE_BYTES: [ 2471 BytesReadMethod, 2472 BytesReaderMethod, 2473 ], 2474 descriptor_pb2.FieldDescriptorProto.TYPE_STRING: [ 2475 StringReadMethod, 2476 BytesReaderMethod, 2477 ], 2478 descriptor_pb2.FieldDescriptorProto.TYPE_MESSAGE: [SubMessageDecoderMethod], 2479 descriptor_pb2.FieldDescriptorProto.TYPE_ENUM: [ 2480 EnumReadMethod, 2481 PackedEnumReadMethod, 2482 PackedEnumReadVectorMethod, 2483 ], 2484} 2485 2486PROTO_FIELD_FIND_METHODS: dict[int, list] = { 2487 descriptor_pb2.FieldDescriptorProto.TYPE_DOUBLE: [ 2488 DoubleFindMethod, 2489 DoubleFindStreamMethod, 2490 ], 2491 descriptor_pb2.FieldDescriptorProto.TYPE_FLOAT: [ 2492 FloatFindMethod, 2493 FloatFindStreamMethod, 2494 ], 2495 descriptor_pb2.FieldDescriptorProto.TYPE_INT32: [ 2496 Int32FindMethod, 2497 Int32FindStreamMethod, 2498 ], 2499 descriptor_pb2.FieldDescriptorProto.TYPE_SINT32: [ 2500 Sint32FindMethod, 2501 Sint32FindStreamMethod, 2502 ], 2503 descriptor_pb2.FieldDescriptorProto.TYPE_SFIXED32: [ 2504 Sfixed32FindMethod, 2505 Sfixed32FindStreamMethod, 2506 ], 2507 descriptor_pb2.FieldDescriptorProto.TYPE_INT64: [ 2508 Int64FindMethod, 2509 Int64FindStreamMethod, 2510 ], 2511 descriptor_pb2.FieldDescriptorProto.TYPE_SINT64: [ 2512 Sint64FindMethod, 2513 Sint64FindStreamMethod, 2514 ], 2515 descriptor_pb2.FieldDescriptorProto.TYPE_SFIXED64: [ 2516 Sfixed64FindMethod, 2517 Sfixed64FindStreamMethod, 2518 ], 2519 descriptor_pb2.FieldDescriptorProto.TYPE_UINT32: [ 2520 Uint32FindMethod, 2521 Uint32FindStreamMethod, 2522 ], 2523 descriptor_pb2.FieldDescriptorProto.TYPE_FIXED32: [ 2524 Fixed32FindMethod, 2525 Fixed32FindStreamMethod, 2526 ], 2527 descriptor_pb2.FieldDescriptorProto.TYPE_UINT64: [ 2528 Uint64FindMethod, 2529 Uint64FindStreamMethod, 2530 ], 2531 descriptor_pb2.FieldDescriptorProto.TYPE_FIXED64: [ 2532 Fixed64FindMethod, 2533 Fixed64FindStreamMethod, 2534 ], 2535 descriptor_pb2.FieldDescriptorProto.TYPE_BOOL: [ 2536 BoolFindMethod, 2537 BoolFindStreamMethod, 2538 ], 2539 descriptor_pb2.FieldDescriptorProto.TYPE_BYTES: [ 2540 BytesFindMethod, 2541 BytesFindStreamMethod, 2542 ], 2543 descriptor_pb2.FieldDescriptorProto.TYPE_STRING: [ 2544 StringFindMethod, 2545 StringFindStreamMethod, 2546 StringFindStreamMethodInlineString, 2547 ], 2548 descriptor_pb2.FieldDescriptorProto.TYPE_MESSAGE: [ 2549 SubMessageFindMethod, 2550 ], 2551 descriptor_pb2.FieldDescriptorProto.TYPE_ENUM: [ 2552 EnumFindMethod, 2553 EnumFindStreamMethod, 2554 ], 2555} 2556 2557PROTO_FIELD_PROPERTIES: dict[int, Type[MessageProperty]] = { 2558 descriptor_pb2.FieldDescriptorProto.TYPE_DOUBLE: DoubleProperty, 2559 descriptor_pb2.FieldDescriptorProto.TYPE_FLOAT: FloatProperty, 2560 descriptor_pb2.FieldDescriptorProto.TYPE_INT32: Int32Property, 2561 descriptor_pb2.FieldDescriptorProto.TYPE_SINT32: Sint32Property, 2562 descriptor_pb2.FieldDescriptorProto.TYPE_SFIXED32: Sfixed32Property, 2563 descriptor_pb2.FieldDescriptorProto.TYPE_INT64: Int64Property, 2564 descriptor_pb2.FieldDescriptorProto.TYPE_SINT64: Sint64Property, 2565 descriptor_pb2.FieldDescriptorProto.TYPE_SFIXED64: Sfixed32Property, 2566 descriptor_pb2.FieldDescriptorProto.TYPE_UINT32: Uint32Property, 2567 descriptor_pb2.FieldDescriptorProto.TYPE_FIXED32: Fixed32Property, 2568 descriptor_pb2.FieldDescriptorProto.TYPE_UINT64: Uint64Property, 2569 descriptor_pb2.FieldDescriptorProto.TYPE_FIXED64: Fixed64Property, 2570 descriptor_pb2.FieldDescriptorProto.TYPE_BOOL: BoolProperty, 2571 descriptor_pb2.FieldDescriptorProto.TYPE_BYTES: BytesProperty, 2572 descriptor_pb2.FieldDescriptorProto.TYPE_STRING: StringProperty, 2573 descriptor_pb2.FieldDescriptorProto.TYPE_MESSAGE: SubMessageProperty, 2574 descriptor_pb2.FieldDescriptorProto.TYPE_ENUM: EnumProperty, 2575} 2576 2577 2578def proto_message_field_props( 2579 message: ProtoMessage, 2580 root: ProtoNode, 2581) -> Iterable[MessageProperty]: 2582 """Yields a MessageProperty for each field in a ProtoMessage. 2583 2584 Only properties which should_appear() is True are returned. 2585 2586 Args: 2587 message: The ProtoMessage whose fields are iterated. 2588 root: The root ProtoNode of the tree. 2589 2590 Yields: 2591 An appropriately-typed MessageProperty object for each field 2592 in the message, to which the property refers. 2593 """ 2594 for field in message.fields(): 2595 property_class = PROTO_FIELD_PROPERTIES[field.type()] 2596 prop = property_class(field, message, root) 2597 if prop.should_appear(): 2598 yield prop 2599 2600 2601def proto_field_methods(class_type: ClassType, field_type: int) -> list: 2602 return ( 2603 PROTO_FIELD_WRITE_METHODS[field_type] 2604 if class_type.is_encoder() 2605 else PROTO_FIELD_READ_METHODS[field_type] 2606 ) 2607 2608 2609def generate_class_for_message( 2610 message: ProtoMessage, 2611 root: ProtoNode, 2612 output: OutputFile, 2613 class_type: ClassType, 2614) -> None: 2615 """Creates a C++ class to encode or decoder a protobuf message.""" 2616 assert message.type() == ProtoNode.Type.MESSAGE 2617 2618 base_class_name = class_type.base_class_name() 2619 class_name = class_type.codegen_class_name() 2620 2621 # Message classes inherit from the base proto message class in codegen.h 2622 # and use its constructor. 2623 base_class = f'{PROTOBUF_NAMESPACE}::{base_class_name}' 2624 output.write_line( 2625 f'class {message.cpp_namespace(root=root)}::{class_name} ' 2626 f': public {base_class} {{' 2627 ) 2628 output.write_line(' public:') 2629 2630 with output.indent(): 2631 # Inherit the constructors from the base class. 2632 output.write_line(f'using {base_class}::{base_class_name};') 2633 2634 # Declare a move constructor that takes a base class. 2635 output.write_line( 2636 f'constexpr {class_name}({base_class}&& parent) ' 2637 f': {base_class}(std::move(parent)) {{}}' 2638 ) 2639 2640 # Allow MemoryEncoder& to be converted to StreamEncoder&. 2641 if class_type == ClassType.MEMORY_ENCODER: 2642 stream_type = ( 2643 f'::{message.cpp_namespace()}::' 2644 f'{ClassType.STREAMING_ENCODER.codegen_class_name()}' 2645 ) 2646 output.write_line( 2647 f'operator {stream_type}&() ' 2648 f' {{ return static_cast<{stream_type}&>(' 2649 f'*static_cast<{PROTOBUF_NAMESPACE}::StreamEncoder*>(this));}}' 2650 ) 2651 2652 # Add a typed Field() member to StreamDecoder 2653 if class_type == ClassType.STREAMING_DECODER: 2654 output.write_line() 2655 output.write_line('::pw::Result<Fields> Field() {') 2656 with output.indent(): 2657 output.write_line( 2658 '::pw::Result<uint32_t> result ' '= FieldNumber();' 2659 ) 2660 output.write_line('if (!result.ok()) {') 2661 with output.indent(): 2662 output.write_line('return result.status();') 2663 output.write_line('}') 2664 output.write_line('return static_cast<Fields>(result.value());') 2665 output.write_line('}') 2666 2667 # Generate entry for message table read or write methods. 2668 if class_type == ClassType.STREAMING_DECODER: 2669 output.write_line() 2670 output.write_line('::pw::Status Read(Message& message) {') 2671 with output.indent(): 2672 output.write_line( 2673 f'return {base_class}::Read(' 2674 'pw::as_writable_bytes(pw::span(&message, 1)), ' 2675 'kMessageFields);' 2676 ) 2677 output.write_line('}') 2678 elif class_type in ( 2679 ClassType.STREAMING_ENCODER, 2680 ClassType.MEMORY_ENCODER, 2681 ): 2682 output.write_line() 2683 output.write_line('::pw::Status Write(const Message& message) {') 2684 with output.indent(): 2685 output.write_line( 2686 f'return {base_class}::Write(' 2687 'pw::as_bytes(pw::span(&message, 1)), kMessageFields);' 2688 ) 2689 output.write_line('}') 2690 2691 # Generate methods for each of the message's fields. 2692 for field in message.fields(): 2693 for method_class in proto_field_methods(class_type, field.type()): 2694 method = method_class(field, message, root, base_class) 2695 if not method.should_appear(): 2696 continue 2697 2698 output.write_line() 2699 method_signature = ( 2700 f'{method.return_type()} ' 2701 f'{method.name()}({method.param_string()})' 2702 ) 2703 2704 if not method.in_class_definition(): 2705 # Method will be defined outside of the class at the end of 2706 # the file. 2707 output.write_line(f'{method_signature};') 2708 continue 2709 2710 output.write_line(f'{method_signature} {{') 2711 with output.indent(): 2712 for line in method.body(): 2713 output.write_line(line) 2714 output.write_line('}') 2715 2716 output.write_line('};') 2717 2718 2719def define_not_in_class_methods( 2720 message: ProtoMessage, 2721 root: ProtoNode, 2722 output: OutputFile, 2723 class_type: ClassType, 2724) -> None: 2725 """Defines methods for a message class that were previously declared.""" 2726 assert message.type() == ProtoNode.Type.MESSAGE 2727 2728 base_class_name = class_type.base_class_name() 2729 base_class = f'{PROTOBUF_NAMESPACE}::{base_class_name}' 2730 2731 for field in message.fields(): 2732 for method_class in proto_field_methods(class_type, field.type()): 2733 method = method_class(field, message, root, base_class) 2734 if not method.should_appear() or method.in_class_definition(): 2735 continue 2736 2737 output.write_line() 2738 class_name = ( 2739 f'{message.cpp_namespace(root=root)}::' 2740 f'{class_type.codegen_class_name()}' 2741 ) 2742 method_signature = ( 2743 f'inline {method.return_type(from_root=True)} ' 2744 f'{class_name}::{method.name()}({method.param_string()})' 2745 ) 2746 output.write_line(f'{method_signature} {{') 2747 with output.indent(): 2748 for line in method.body(): 2749 output.write_line(line) 2750 output.write_line('}') 2751 2752 2753def _common_value_prefix(proto_enum: ProtoEnum) -> str: 2754 """Calculate the common prefix of all enum values. 2755 2756 Given an enumeration: 2757 enum Thing { 2758 THING_ONE = 1; 2759 THING_TWO = 2; 2760 THING_THREE = 3; 2761 } 2762 2763 If will return 'THING_', resulting in generated "style" aliases of 2764 'kOne', 'kTwo', and 'kThree'. 2765 2766 The prefix is walked back to the last _, so that the enumeration: 2767 enum Activity { 2768 ACTIVITY_RUN = 1; 2769 ACTIVITY_ROW = 2; 2770 } 2771 2772 Returns 'ACTIVITY_' and not 'ACTIVITY_R'. 2773 """ 2774 if len(proto_enum.values()) <= 1: 2775 return '' 2776 2777 common_prefix = "".join( 2778 ch[0] 2779 for ch in takewhile( 2780 lambda ch: all(ch[0] == c for c in ch), 2781 zip(*[name for name, _ in proto_enum.values()]), 2782 ) 2783 ) 2784 (left, under, _) = common_prefix.rpartition('_') 2785 return left + under 2786 2787 2788def generate_code_for_enum( 2789 proto_enum: ProtoEnum, root: ProtoNode, output: OutputFile 2790) -> None: 2791 """Creates a C++ enum for a proto enum.""" 2792 assert proto_enum.type() == ProtoNode.Type.ENUM 2793 2794 common_prefix = _common_value_prefix(proto_enum) 2795 output.write_line( 2796 f'enum class {proto_enum.cpp_namespace(root=root)} ' f': uint32_t {{' 2797 ) 2798 with output.indent(): 2799 for name, number in proto_enum.values(): 2800 output.write_line(f'{name} = {number},') 2801 2802 style_name = 'k' + ProtoMessageField.upper_camel_case( 2803 name[len(common_prefix) :] 2804 ) 2805 if style_name != name: 2806 output.write_line(f'{style_name} = {name},') 2807 2808 output.write_line('};') 2809 2810 2811def generate_function_for_enum( 2812 proto_enum: ProtoEnum, root: ProtoNode, output: OutputFile 2813) -> None: 2814 """Creates a C++ validation function for a proto enum.""" 2815 assert proto_enum.type() == ProtoNode.Type.ENUM 2816 2817 enum_name = proto_enum.cpp_namespace(root=root) 2818 output.write_line( 2819 f'constexpr bool IsValid{enum_name}({enum_name} value) {{' 2820 ) 2821 with output.indent(): 2822 output.write_line('switch (value) {') 2823 with output.indent(): 2824 for name, _ in proto_enum.values(): 2825 output.write_line(f'case {enum_name}::{name}: return true;') 2826 output.write_line('default: return false;') 2827 output.write_line('}') 2828 output.write_line('}') 2829 2830 2831def generate_to_string_for_enum( 2832 proto_enum: ProtoEnum, root: ProtoNode, output: OutputFile 2833) -> None: 2834 """Creates a C++ to string function for a proto enum.""" 2835 assert proto_enum.type() == ProtoNode.Type.ENUM 2836 2837 enum_name = proto_enum.cpp_namespace(root=root) 2838 output.write_line( 2839 f'// Returns string names for {enum_name}; ' 2840 'returns "" for invalid enum values.' 2841 ) 2842 output.write_line( 2843 f'constexpr const char* {enum_name}ToString({enum_name} value) {{' 2844 ) 2845 with output.indent(): 2846 output.write_line('switch (value) {') 2847 with output.indent(): 2848 for name, _ in proto_enum.values(): 2849 output.write_line(f'case {enum_name}::{name}: return "{name}";') 2850 output.write_line('default: return "";') 2851 output.write_line('}') 2852 output.write_line('}') 2853 2854 2855def forward_declare( 2856 message: ProtoMessage, 2857 root: ProtoNode, 2858 output: OutputFile, 2859 exclude_legacy_snake_case_field_name_enums: bool, 2860) -> None: 2861 """Generates code forward-declaring entities in a message's namespace.""" 2862 namespace = message.cpp_namespace(root=root) 2863 output.write_line() 2864 output.write_line(f'namespace {namespace} {{') 2865 2866 # Define an enum defining each of the message's fields and their numbers. 2867 output.write_line('enum class Fields : uint32_t {') 2868 with output.indent(): 2869 for field in message.fields(): 2870 output.write_line(f'{field.enum_name()} = {field.number()},') 2871 2872 # Migration support from SNAKE_CASE to kConstantCase. 2873 if not exclude_legacy_snake_case_field_name_enums: 2874 for field in message.fields(): 2875 output.write_line( 2876 f'{field.legacy_enum_name()} = {field.number()},' 2877 ) 2878 2879 output.write_line('};') 2880 2881 # Define constants for fixed-size fields. 2882 output.write_line() 2883 for prop in proto_message_field_props(message, root): 2884 max_size = prop.max_size() 2885 if max_size: 2886 output.write_line( 2887 f'static constexpr size_t {prop.max_size_constant_name()} ' 2888 f'= {max_size};' 2889 ) 2890 2891 # Declare the message's message struct. 2892 output.write_line() 2893 output.write_line('struct Message;') 2894 2895 # Declare the message's encoder classes. 2896 output.write_line() 2897 output.write_line('class StreamEncoder;') 2898 output.write_line('class MemoryEncoder;') 2899 2900 # Declare the message's decoder classes. 2901 output.write_line() 2902 output.write_line('class StreamDecoder;') 2903 2904 # Declare the message's enums. 2905 for child in message.children(): 2906 if child.type() == ProtoNode.Type.ENUM: 2907 output.write_line() 2908 generate_code_for_enum(cast(ProtoEnum, child), message, output) 2909 output.write_line() 2910 generate_function_for_enum(cast(ProtoEnum, child), message, output) 2911 output.write_line() 2912 generate_to_string_for_enum(cast(ProtoEnum, child), message, output) 2913 2914 output.write_line(f'}} // namespace {namespace}') 2915 2916 2917def generate_struct_for_message( 2918 message: ProtoMessage, root: ProtoNode, output: OutputFile 2919) -> None: 2920 """Creates a C++ struct to hold a protobuf message values.""" 2921 assert message.type() == ProtoNode.Type.MESSAGE 2922 2923 output.write_line(f'struct {message.cpp_namespace(root=root)}::Message {{') 2924 2925 # Generate members for each of the message's fields. 2926 with output.indent(): 2927 cmp: list[str] = [] 2928 for prop in proto_message_field_props(message, root): 2929 type_name = prop.struct_member_type() 2930 name = prop.name() 2931 output.write_line(f'{type_name} {name};') 2932 2933 if not prop.use_callback(): 2934 cmp.append(f'this->{name} == other.{name}') 2935 2936 # Equality operator 2937 output.write_line() 2938 output.write_line('bool operator==(const Message& other) const {') 2939 with output.indent(): 2940 if len(cmp) > 0: 2941 output.write_line(f'return {" && ".join(cmp)};') 2942 else: 2943 output.write_line('static_cast<void>(other);') 2944 output.write_line('return true;') 2945 output.write_line('}') 2946 output.write_line( 2947 'bool operator!=(const Message& other) const ' 2948 '{ return !(*this == other); }' 2949 ) 2950 2951 output.write_line('};') 2952 2953 2954def generate_table_for_message( 2955 message: ProtoMessage, root: ProtoNode, output: OutputFile 2956) -> None: 2957 """Creates a C++ array to hold a protobuf message description.""" 2958 assert message.type() == ProtoNode.Type.MESSAGE 2959 2960 namespace = message.cpp_namespace(root=root) 2961 output.write_line(f'namespace {namespace} {{') 2962 2963 properties = list(proto_message_field_props(message, root)) 2964 2965 output.write_line('PW_MODIFY_DIAGNOSTICS_PUSH();') 2966 output.write_line('PW_MODIFY_DIAGNOSTIC(ignored, "-Winvalid-offsetof");') 2967 2968 # Generate static_asserts to fail at compile-time if the structure cannot 2969 # be converted into a table. 2970 for idx, prop in enumerate(properties): 2971 if idx > 0: 2972 output.write_line( 2973 'static_assert(offsetof(Message, {}) > 0);'.format(prop.name()) 2974 ) 2975 output.write_line( 2976 'static_assert(sizeof(Message::{}) <= ' 2977 '{}::MessageField::kMaxFieldSize);'.format( 2978 prop.name(), _INTERNAL_NAMESPACE 2979 ) 2980 ) 2981 2982 # Zero-length C arrays are not permitted by the C++ standard, so only 2983 # generate the message fields array if it is non-empty. Zero-length 2984 # std::arrays are valid, but older toolchains may not support constexpr 2985 # std::arrays, even with -std=c++17. 2986 # 2987 # The kMessageFields span is generated whether the message has fields or 2988 # not. Only the span is referenced elsewhere. 2989 if properties: 2990 output.write_line( 2991 f'inline constexpr {_INTERNAL_NAMESPACE}::MessageField ' 2992 ' _kMessageFields[] = {' 2993 ) 2994 2995 # Generate members for each of the message's fields. 2996 with output.indent(): 2997 for prop in properties: 2998 table = ', '.join(prop.table_entry()) 2999 output.write_line(f'{{{table}}},') 3000 3001 output.write_line('};') 3002 output.write_line('PW_MODIFY_DIAGNOSTICS_POP();') 3003 3004 output.write_line( 3005 f'inline constexpr pw::span<const {_INTERNAL_NAMESPACE}::' 3006 'MessageField> kMessageFields = _kMessageFields;' 3007 ) 3008 3009 member_list = ', '.join( 3010 [f'message.{prop.name()}' for prop in properties] 3011 ) 3012 3013 # Generate std::tuple for Message fields. 3014 output.write_line( 3015 'inline constexpr auto ToTuple(const Message &message) {' 3016 ) 3017 output.write_line(f' return std::tie({member_list});') 3018 output.write_line('}') 3019 3020 # Generate mutable std::tuple for Message fields. 3021 output.write_line( 3022 'inline constexpr auto ToMutableTuple(Message &message) {' 3023 ) 3024 output.write_line(f' return std::tie({member_list});') 3025 output.write_line('}') 3026 else: 3027 output.write_line( 3028 f'inline constexpr pw::span<const {_INTERNAL_NAMESPACE}::' 3029 'MessageField> kMessageFields;' 3030 ) 3031 3032 output.write_line(f'}} // namespace {namespace}') 3033 3034 3035def generate_sizes_for_message( 3036 message: ProtoMessage, root: ProtoNode, output: OutputFile 3037) -> None: 3038 """Creates C++ constants for the encoded sizes of a protobuf message.""" 3039 assert message.type() == ProtoNode.Type.MESSAGE 3040 3041 namespace = message.cpp_namespace(root=root) 3042 output.write_line(f'namespace {namespace} {{') 3043 3044 property_sizes: list[str] = [] 3045 scratch_sizes: list[str] = [] 3046 for prop in proto_message_field_props(message, root): 3047 property_sizes.append(prop.max_encoded_size()) 3048 if prop.include_in_scratch_size(): 3049 scratch_sizes.append(prop.max_encoded_size()) 3050 3051 output.write_line('inline constexpr size_t kMaxEncodedSizeBytes =') 3052 with output.indent(): 3053 if len(property_sizes) == 0: 3054 output.write_line('0;') 3055 while len(property_sizes) > 0: 3056 property_size = property_sizes.pop(0) 3057 if len(property_sizes) > 0: 3058 output.write_line(f'{property_size} +') 3059 else: 3060 output.write_line(f'{property_size};') 3061 3062 output.write_line() 3063 output.write_line( 3064 'inline constexpr size_t kScratchBufferSizeBytes = ' 3065 + ('std::max({' if len(scratch_sizes) > 0 else '0;') 3066 ) 3067 with output.indent(): 3068 for scratch_size in scratch_sizes: 3069 output.write_line(f'{scratch_size},') 3070 if len(scratch_sizes) > 0: 3071 output.write_line('});') 3072 3073 output.write_line(f'}} // namespace {namespace}') 3074 3075 3076def generate_find_functions_for_message( 3077 message: ProtoMessage, root: ProtoNode, output: OutputFile 3078) -> None: 3079 """Creates C++ constants for the encoded sizes of a protobuf message.""" 3080 assert message.type() == ProtoNode.Type.MESSAGE 3081 3082 namespace = message.cpp_namespace(root=root) 3083 output.write_line(f'namespace {namespace} {{') 3084 3085 for field in message.fields(): 3086 if field.is_repeated(): 3087 # Find methods don't account for repeated field semantics, so 3088 # ignore them to avoid confusion. 3089 continue 3090 3091 try: 3092 methods = PROTO_FIELD_FIND_METHODS[field.type()] 3093 except KeyError: 3094 continue 3095 3096 for cls in methods: 3097 method = cls(field, message, root, '') 3098 method_signature = ( 3099 f'inline {method.return_type()} ' 3100 f'{method.name()}({method.param_string()})' 3101 ) 3102 3103 output.write_line() 3104 output.write_line(f'{method_signature} {{') 3105 3106 with output.indent(): 3107 for line in method.body(): 3108 output.write_line(line) 3109 3110 output.write_line('}') 3111 3112 output.write_line(f'}} // namespace {namespace}') 3113 3114 3115def generate_is_trivially_comparable_specialization( 3116 message: ProtoMessage, root: ProtoNode, output: OutputFile 3117) -> None: 3118 is_trivially_comparable = True 3119 for prop in proto_message_field_props(message, root): 3120 if prop.use_callback(): 3121 is_trivially_comparable = False 3122 break 3123 3124 qualified_message = f'::{message.cpp_namespace()}::Message' 3125 3126 output.write_line('template <>') 3127 output.write_line( 3128 'constexpr bool IsTriviallyComparable' f'<{qualified_message}>() {{' 3129 ) 3130 output.write_line(f' return {str(is_trivially_comparable).lower()};') 3131 output.write_line('}') 3132 3133 3134def _proto_filename_to_generated_header(proto_file: str) -> str: 3135 """Returns the generated C++ header name for a .proto file.""" 3136 return os.path.splitext(proto_file)[0] + PROTO_H_EXTENSION 3137 3138 3139def dependency_sorted_messages(package: ProtoNode): 3140 """Yields the messages in the package sorted after their dependencies.""" 3141 3142 # Build the graph of dependencies between messages. 3143 graph: dict[ProtoMessage, list[ProtoMessage]] = {} 3144 for node in package: 3145 if node.type() == ProtoNode.Type.MESSAGE: 3146 message = cast(ProtoMessage, node) 3147 graph[message] = message.dependencies() 3148 3149 # Repeatedly prepare a topological sort of the dependency graph, removing 3150 # a dependency each time a cycle is a detected, until we're left with a 3151 # fully directed graph. 3152 tsort: TopologicalSorter 3153 while True: 3154 tsort = TopologicalSorter(graph) 3155 try: 3156 tsort.prepare() 3157 break 3158 except CycleError as err: 3159 dependency, message = err.args[1][0], err.args[1][1] 3160 message.remove_dependency_cycle(dependency) 3161 graph[message] = message.dependencies() 3162 3163 # Yield the messages from the sorted graph. 3164 while tsort.is_active(): 3165 messages = tsort.get_ready() 3166 yield from messages 3167 tsort.done(*messages) 3168 3169 3170def generate_code_for_package( 3171 file_descriptor_proto, 3172 package: ProtoNode, 3173 output: OutputFile, 3174 suppress_legacy_namespace: bool, 3175 exclude_legacy_snake_case_field_name_enums: bool, 3176) -> None: 3177 """Generates code for a single .pb.h file corresponding to a .proto file.""" 3178 3179 assert package.type() == ProtoNode.Type.PACKAGE 3180 3181 output.write_line( 3182 f'// {os.path.basename(output.name())} automatically ' 3183 f'generated by {PLUGIN_NAME} {PLUGIN_VERSION}' 3184 ) 3185 output.write_line('#pragma once\n') 3186 output.write_line('#include <algorithm>') 3187 output.write_line('#include <array>') 3188 output.write_line('#include <cstddef>') 3189 output.write_line('#include <cstdint>') 3190 output.write_line('#include <optional>') 3191 output.write_line('#include <string_view>\n') 3192 output.write_line('#include "pw_assert/assert.h"') 3193 output.write_line('#include "pw_containers/vector.h"') 3194 output.write_line('#include "pw_preprocessor/compiler.h"') 3195 output.write_line('#include "pw_protobuf/encoder.h"') 3196 output.write_line('#include "pw_protobuf/find.h"') 3197 output.write_line('#include "pw_protobuf/internal/codegen.h"') 3198 output.write_line('#include "pw_protobuf/serialized_size.h"') 3199 output.write_line('#include "pw_protobuf/stream_decoder.h"') 3200 output.write_line('#include "pw_result/result.h"') 3201 output.write_line('#include "pw_span/span.h"') 3202 output.write_line('#include "pw_status/status.h"') 3203 output.write_line('#include "pw_status/status_with_size.h"') 3204 output.write_line('#include "pw_string/string.h"') 3205 3206 for imported_file in file_descriptor_proto.dependency: 3207 generated_header = _proto_filename_to_generated_header(imported_file) 3208 output.write_line(f'#include "{generated_header}"') 3209 3210 if package.cpp_namespace(): 3211 file_namespace = package.cpp_namespace() 3212 if file_namespace.startswith('::'): 3213 file_namespace = file_namespace[2:] 3214 3215 output.write_line(f'\nnamespace {file_namespace} {{') 3216 3217 for node in package: 3218 if node.type() == ProtoNode.Type.MESSAGE: 3219 forward_declare( 3220 cast(ProtoMessage, node), 3221 package, 3222 output, 3223 exclude_legacy_snake_case_field_name_enums, 3224 ) 3225 3226 # Define all top-level enums. 3227 for node in package.children(): 3228 if node.type() == ProtoNode.Type.ENUM: 3229 output.write_line() 3230 generate_code_for_enum(cast(ProtoEnum, node), package, output) 3231 output.write_line() 3232 generate_function_for_enum(cast(ProtoEnum, node), package, output) 3233 output.write_line() 3234 generate_to_string_for_enum(cast(ProtoEnum, node), package, output) 3235 3236 # Run through all messages, generating structs and classes for each. 3237 messages = [] 3238 for message in dependency_sorted_messages(package): 3239 output.write_line() 3240 generate_struct_for_message(message, package, output) 3241 output.write_line() 3242 generate_table_for_message(message, package, output) 3243 output.write_line() 3244 generate_sizes_for_message(message, package, output) 3245 output.write_line() 3246 generate_find_functions_for_message(message, package, output) 3247 output.write_line() 3248 generate_class_for_message( 3249 message, package, output, ClassType.STREAMING_ENCODER 3250 ) 3251 output.write_line() 3252 generate_class_for_message( 3253 message, package, output, ClassType.MEMORY_ENCODER 3254 ) 3255 output.write_line() 3256 generate_class_for_message( 3257 message, package, output, ClassType.STREAMING_DECODER 3258 ) 3259 messages.append(message) 3260 3261 # Run a second pass through the messages, this time defining all of the 3262 # methods which were previously only declared. 3263 for message in messages: 3264 define_not_in_class_methods( 3265 message, package, output, ClassType.STREAMING_ENCODER 3266 ) 3267 define_not_in_class_methods( 3268 message, package, output, ClassType.MEMORY_ENCODER 3269 ) 3270 define_not_in_class_methods( 3271 message, package, output, ClassType.STREAMING_DECODER 3272 ) 3273 3274 if package.cpp_namespace(): 3275 output.write_line(f'\n}} // namespace {package.cpp_namespace()}') 3276 3277 # Aliasing namespaces aren't needed if `package.cpp_namespace()` is 3278 # empty (since everyone can see the global namespace). It shouldn't 3279 # ever be empty, though. 3280 3281 if not suppress_legacy_namespace: 3282 output.write_line() 3283 output.write_line( 3284 '// Aliases for legacy pwpb codegen interface. ' 3285 'Please use the' 3286 ) 3287 output.write_line('// `::pwpb`-suffixed names in new code.') 3288 legacy_namespace = package.cpp_namespace(codegen_subnamespace=None) 3289 output.write_line(f'namespace {legacy_namespace} {{') 3290 output.write_line(f'using namespace ::{package.cpp_namespace()};') 3291 output.write_line(f'}} // namespace {legacy_namespace}') 3292 3293 # TODO: b/250945489 - Remove this if possible 3294 output.write_line() 3295 output.write_line( 3296 '// Codegen implementation detail; do not use this namespace!' 3297 ) 3298 3299 external_lookup_namespace = "{}::{}".format( 3300 EXTERNAL_SYMBOL_WORKAROUND_NAMESPACE, 3301 package.cpp_namespace(codegen_subnamespace=None), 3302 ) 3303 3304 output.write_line(f'namespace {external_lookup_namespace} {{') 3305 output.write_line(f'using namespace ::{package.cpp_namespace()};') 3306 output.write_line(f'}} // namespace {external_lookup_namespace}') 3307 3308 if messages: 3309 proto_namespace = PROTOBUF_NAMESPACE.lstrip(':') 3310 output.write_line() 3311 output.write_line(f'namespace {proto_namespace} {{') 3312 3313 for message in messages: 3314 generate_is_trivially_comparable_specialization( 3315 message, package, output 3316 ) 3317 3318 output.write_line(f'}} // namespace {proto_namespace}') 3319 3320 3321def process_proto_file( 3322 proto_file, 3323 proto_options, 3324 suppress_legacy_namespace: bool, 3325 exclude_legacy_snake_case_field_name_enums: bool, 3326) -> Iterable[OutputFile]: 3327 """Generates code for a single .proto file.""" 3328 3329 # Two passes are made through the file. The first builds the tree of all 3330 # message/enum nodes, then the second creates the fields in each. This is 3331 # done as non-primitive fields need pointers to their types, which requires 3332 # the entire tree to have been parsed into memory. 3333 _, package_root = build_node_tree(proto_file, proto_options=proto_options) 3334 3335 output_filename = _proto_filename_to_generated_header(proto_file.name) 3336 output_file = OutputFile(output_filename) 3337 generate_code_for_package( 3338 proto_file, 3339 package_root, 3340 output_file, 3341 suppress_legacy_namespace, 3342 exclude_legacy_snake_case_field_name_enums, 3343 ) 3344 3345 return [output_file] 3346