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