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