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