• 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 #pragma once
15 
16 #include <cstdint>
17 
18 #include "pw_function/function.h"
19 #include "pw_preprocessor/compiler.h"
20 #include "pw_protobuf/wire_format.h"
21 #include "pw_span/span.h"
22 #include "pw_status/status.h"
23 
24 // TODO: b/259746255 - Remove this manual application of -Wconversion when all
25 // of
26 //     Pigweed builds with it.
27 PW_MODIFY_DIAGNOSTICS_PUSH();
28 PW_MODIFY_DIAGNOSTIC(error, "-Wconversion");
29 
30 namespace pw::protobuf {
31 namespace internal {
32 
33 // Varints can be encoded as an unsigned type, a signed type with normal
34 // encoding, or a signed type with zigzag encoding.
35 enum class VarintType {
36   kUnsigned = 0,
37   kNormal = 1,
38   kZigZag = 2,
39 };
40 
41 // Represents a field in a code generated message struct that can be the target
42 // for decoding or source of encoding.
43 //
44 // An instance of this class exists for every field in every protobuf in the
45 // binary, thus it is size critical to ensure efficiency while retaining enough
46 // information to describe the layout of the generated message struct.
47 //
48 // Limitations imposed:
49 //  - Element size of a repeated fields must be no larger than 15 bytes.
50 //    (8 byte int64/fixed64/double is the largest supported element).
51 //  - Individual field size (including repeated and nested messages) must be no
52 //    larger than 64 KB. (This is already the maximum size of pw::Vector).
53 //
54 // A complete codegen struct is represented by a span<MessageField>,
55 // holding a pointer to the MessageField members themselves, and the number of
56 // fields in the struct. These spans are global data, one span per protobuf
57 // message (including the size), and one MessageField per field in the message.
58 //
59 // Nested messages are handled with a pointer from the MessageField in the
60 // parent to a pointer to the (global data) span. Since the size of the nested
61 // message is stored as part of the global span, the cost of a nested message
62 // is only the size of a pointer to that span.
63 class MessageField {
64  public:
65   static constexpr unsigned int kMaxFieldSize = (1u << 16) - 1;
66 
MessageField(uint32_t field_number,WireType wire_type,size_t elem_size,VarintType varint_type,bool is_string,bool is_fixed_size,bool is_repeated,bool is_optional,bool use_callback,size_t field_offset,size_t field_size,const span<const MessageField> * nested_message_fields)67   constexpr MessageField(uint32_t field_number,
68                          WireType wire_type,
69                          size_t elem_size,
70                          VarintType varint_type,
71                          bool is_string,
72                          bool is_fixed_size,
73                          bool is_repeated,
74                          bool is_optional,
75                          bool use_callback,
76                          size_t field_offset,
77                          size_t field_size,
78                          const span<const MessageField>* nested_message_fields)
79       : field_number_(field_number),
80         field_info_(static_cast<uint32_t>(wire_type) << kWireTypeShift |
81                     static_cast<uint32_t>(elem_size) << kElemSizeShift |
82                     static_cast<uint32_t>(varint_type) << kVarintTypeShift |
83                     static_cast<uint32_t>(is_string) << kIsStringShift |
84                     static_cast<uint32_t>(is_fixed_size) << kIsFixedSizeShift |
85                     static_cast<uint32_t>(is_repeated) << kIsRepeatedShift |
86                     static_cast<uint32_t>(is_optional) << kIsOptionalShift |
87                     static_cast<uint32_t>(use_callback) << kUseCallbackShift |
88                     static_cast<uint32_t>(field_size) << kFieldSizeShift),
89         field_offset_(field_offset),
90         nested_message_fields_(nested_message_fields) {}
91 
field_number()92   constexpr uint32_t field_number() const { return field_number_; }
wire_type()93   constexpr WireType wire_type() const {
94     return static_cast<WireType>((field_info_ >> kWireTypeShift) &
95                                  kWireTypeMask);
96   }
elem_size()97   constexpr size_t elem_size() const {
98     return (field_info_ >> kElemSizeShift) & kElemSizeMask;
99   }
varint_type()100   constexpr VarintType varint_type() const {
101     return static_cast<VarintType>((field_info_ >> kVarintTypeShift) &
102                                    kVarintTypeMask);
103   }
is_string()104   constexpr bool is_string() const {
105     return (field_info_ >> kIsStringShift) & 1;
106   }
is_fixed_size()107   constexpr bool is_fixed_size() const {
108     return (field_info_ >> kIsFixedSizeShift) & 1;
109   }
is_repeated()110   constexpr bool is_repeated() const {
111     return (field_info_ >> kIsRepeatedShift) & 1;
112   }
is_optional()113   constexpr bool is_optional() const {
114     return (field_info_ >> kIsOptionalShift) & 1;
115   }
use_callback()116   constexpr bool use_callback() const {
117     return (field_info_ >> kUseCallbackShift) & 1;
118   }
field_offset()119   constexpr size_t field_offset() const { return field_offset_; }
field_size()120   constexpr size_t field_size() const {
121     return (field_info_ >> kFieldSizeShift) & kFieldSizeMask;
122   }
nested_message_fields()123   constexpr const span<const MessageField>* nested_message_fields() const {
124     return nested_message_fields_;
125   }
126 
127   constexpr bool operator==(uint32_t field_number) const {
128     return field_number == field_number_;
129   }
130 
131  private:
132   // field_info_ packs multiple fields into a single word as follows:
133   //
134   //   wire_type      : 3
135   //   varint_type    : 2
136   //   is_string      : 1
137   //   is_fixed_size  : 1
138   //   is_repeated    : 1
139   //   use_callback   : 1
140   //   -
141   //   elem_size      : 4
142   //   is_optional    : 1
143   //   [unused space] : 2
144   //   -
145   //   field_size     : 16
146   //
147   // The protobuf field type is spread among a few fields (wire_type,
148   // varint_type, is_string, elem_size). The exact field type (e.g. int32, bool,
149   // message, etc.), from which all of that information can be derived, can be
150   // represented in 4 bits. If more bits are needed in the future, these could
151   // be consolidated into a single field type enum.
152   static constexpr unsigned int kWireTypeShift = 29u;
153   static constexpr unsigned int kWireTypeMask = (1u << 3) - 1;
154   static constexpr unsigned int kVarintTypeShift = 27u;
155   static constexpr unsigned int kVarintTypeMask = (1u << 2) - 1;
156   static constexpr unsigned int kIsStringShift = 26u;
157   static constexpr unsigned int kIsFixedSizeShift = 25u;
158   static constexpr unsigned int kIsRepeatedShift = 24u;
159   static constexpr unsigned int kUseCallbackShift = 23u;
160   static constexpr unsigned int kElemSizeShift = 19u;
161   static constexpr unsigned int kElemSizeMask = (1u << 4) - 1;
162   static constexpr unsigned int kIsOptionalShift = 16u;
163   static constexpr unsigned int kFieldSizeShift = 0u;
164   static constexpr unsigned int kFieldSizeMask = kMaxFieldSize;
165 
166   uint32_t field_number_;
167   uint32_t field_info_;
168   size_t field_offset_;
169   // TODO: b/234875722 - Could be replaced by a class MessageDescriptor*
170   const span<const MessageField>* nested_message_fields_;
171 };
172 static_assert(sizeof(MessageField) <= sizeof(size_t) * 4,
173               "MessageField should be four words or less");
174 
175 template <typename...>
176 constexpr std::false_type kInvalidMessageStruct{};
177 
178 }  // namespace internal
179 
180 // Callback for a structure member that cannot be represented by a data type.
181 // Holds either a callback for encoding a field, or a callback for decoding
182 // a field.
183 template <typename StreamEncoder, typename StreamDecoder>
184 union Callback {
Callback()185   constexpr Callback() : encode_() {}
~Callback()186   ~Callback() { encode_ = nullptr; }
187 
188   // Set the encoder callback.
SetEncoder(Function<Status (StreamEncoder & encoder)> && encode)189   void SetEncoder(Function<Status(StreamEncoder& encoder)>&& encode) {
190     encode_ = std::move(encode);
191   }
192 
193   // Set the decoder callback.
SetDecoder(Function<Status (StreamDecoder & decoder)> && decode)194   void SetDecoder(Function<Status(StreamDecoder& decoder)>&& decode) {
195     decode_ = std::move(decode);
196   }
197 
198   // Allow moving of callbacks by moving the member.
199   constexpr Callback(Callback&& other) = default;
200   constexpr Callback& operator=(Callback&& other) = default;
201 
202   // Copying a callback does not copy the functions.
Callback(const Callback &)203   constexpr Callback(const Callback&) : encode_() {}
204   constexpr Callback& operator=(const Callback&) {
205     encode_ = nullptr;
206     return *this;
207   }
208 
209   // Evaluate to true if the encoder or decoder callback is set.
210   explicit operator bool() const { return encode_ || decode_; }
211 
212  private:
213   friend StreamDecoder;
214   friend StreamEncoder;
215 
216   // Called by StreamEncoder to encode the structure member.
217   // Returns OkStatus() if this has not been set by the caller, the default
218   // behavior of a field without an encoder is the same as default-initialized
219   // field.
Encode(StreamEncoder & encoder)220   Status Encode(StreamEncoder& encoder) const {
221     if (encode_) {
222       return encode_(encoder);
223     }
224     return OkStatus();
225   }
226 
227   // Called by StreamDecoder to decode the structure member when the field
228   // is present. Returns DataLoss() if this has not been set by the caller.
Decode(StreamDecoder & decoder)229   Status Decode(StreamDecoder& decoder) const {
230     if (decode_) {
231       return decode_(decoder);
232     }
233     return Status::DataLoss();
234   }
235 
236   Function<Status(StreamEncoder& encoder)> encode_;
237   Function<Status(StreamDecoder& decoder)> decode_;
238 };
239 
240 template <typename T>
IsTriviallyComparable()241 constexpr bool IsTriviallyComparable() {
242   static_assert(internal::kInvalidMessageStruct<T>,
243                 "Not a generated message struct");
244   return false;
245 }
246 
247 }  // namespace pw::protobuf
248 
249 PW_MODIFY_DIAGNOSTICS_POP();
250