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