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