Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / third_party / pigweed / repo / pw_protobuf / public / pw_protobuf / encoder.h
1 // Copyright 2019 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 <cstddef>
17 #include <cstring>
18 #include <span>
19
20 #include "pw_bytes/span.h"
21 #include "pw_protobuf/wire_format.h"
22 #include "pw_result/result.h"
23 #include "pw_status/status.h"
24 #include "pw_varint/varint.h"
25
26 namespace pw::protobuf {
27
28 // A streaming protobuf encoder which encodes to a user-specified buffer.
29 class Encoder {
30  public:
31   // TODO(frolv): Right now, only one intermediate size is supported. However,
32   // this can be wasteful, as it requires 4 or 8 bytes of space per nested
33   // message. This can be templated to minimize the overhead.
34   using SizeType = size_t;
35
36   constexpr Encoder(ByteSpan buffer,
37                     std::span<SizeType*> locations,
38                     std::span<SizeType*> stack)
39       : buffer_(buffer),
40         cursor_(buffer.data()),
41         blob_locations_(locations),
42         blob_count_(0),
43         blob_stack_(stack),
44         depth_(0),
45         encode_status_(OkStatus()) {}
46
47   // Disallow copy/assign to avoid confusion about who owns the buffer.
48   Encoder(const Encoder& other) = delete;
49   Encoder& operator=(const Encoder& other) = delete;
50
51   // Per the protobuf specification, valid field numbers range between 1 and
52   // 2**29 - 1, inclusive. The numbers 19000-19999 are reserved for internal
53   // use.
54   constexpr static uint32_t kMaxFieldNumber = (1u << 29) - 1;
55   constexpr static uint32_t kFirstReservedNumber = 19000;
56   constexpr static uint32_t kLastReservedNumber = 19999;
57
58   // Writes a proto uint32 key-value pair.
59   Status WriteUint32(uint32_t field_number, uint32_t value) {
60     return WriteUint64(field_number, value);
61   }
62
63   // Writes a repeated uint32 using packed encoding.
64   Status WritePackedUint32(uint32_t field_number,
65                            std::span<const uint32_t> values) {
66     return WritePackedVarints(field_number, values, /*zigzag=*/false);
67   }
68
69   // Writes a proto uint64 key-value pair.
70   Status WriteUint64(uint32_t field_number, uint64_t value);
71
72   // Writes a repeated uint64 using packed encoding.
73   Status WritePackedUint64(uint64_t field_number,
74                            std::span<const uint64_t> values) {
75     return WritePackedVarints(field_number, values, /*zigzag=*/false);
76   }
77
78   // Writes a proto int32 key-value pair.
79   Status WriteInt32(uint32_t field_number, int32_t value) {
80     return WriteUint64(field_number, value);
81   }
82
83   // Writes a repeated int32 using packed encoding.
84   Status WritePackedInt32(uint32_t field_number,
85                           std::span<const int32_t> values) {
86     return WritePackedVarints(
87         field_number,
88         std::span(reinterpret_cast<const uint32_t*>(values.data()),
89                   values.size()),
90         /*zigzag=*/false);
91   }
92
93   // Writes a proto int64 key-value pair.
94   Status WriteInt64(uint32_t field_number, int64_t value) {
95     return WriteUint64(field_number, value);
96   }
97
98   // Writes a repeated int64 using packed encoding.
99   Status WritePackedInt64(uint32_t field_number,
100                           std::span<const int64_t> values) {
101     return WritePackedVarints(
102         field_number,
103         std::span(reinterpret_cast<const uint64_t*>(values.data()),
104                   values.size()),
105         /*zigzag=*/false);
106   }
107
108   // Writes a proto sint32 key-value pair.
109   Status WriteSint32(uint32_t field_number, int32_t value) {
110     return WriteUint64(field_number, varint::ZigZagEncode(value));
111   }
112
113   // Writes a repeated sint32 using packed encoding.
114   Status WritePackedSint32(uint32_t field_number,
115                            std::span<const int32_t> values) {
116     return WritePackedVarints(
117         field_number,
118         std::span(reinterpret_cast<const uint32_t*>(values.data()),
119                   values.size()),
120         /*zigzag=*/true);
121   }
122
123   // Writes a proto sint64 key-value pair.
124   Status WriteSint64(uint32_t field_number, int64_t value) {
125     return WriteUint64(field_number, varint::ZigZagEncode(value));
126   }
127
128   // Writes a repeated sint64 using packed encoding.
129   Status WritePackedSint64(uint32_t field_number,
130                            std::span<const int64_t> values) {
131     return WritePackedVarints(
132         field_number,
133         std::span(reinterpret_cast<const uint64_t*>(values.data()),
134                   values.size()),
135         /*zigzag=*/true);
136   }
137
138   // Writes a proto bool key-value pair.
139   Status WriteBool(uint32_t field_number, bool value) {
140     return WriteUint32(field_number, static_cast<uint32_t>(value));
141   }
142
143   // Writes a proto fixed32 key-value pair.
144   Status WriteFixed32(uint32_t field_number, uint32_t value) {
145     std::byte* original_cursor = cursor_;
146     WriteFieldKey(field_number, WireType::kFixed32);
147     Status status = WriteRawBytes(value);
148     IncreaseParentSize(cursor_ - original_cursor);
149     return status;
150   }
151
152   // Writes a repeated fixed32 field using packed encoding.
153   Status WritePackedFixed32(uint32_t field_number,
154                             std::span<const uint32_t> values) {
155     return WriteBytes(field_number, std::as_bytes(values));
156   }
157
158   // Writes a proto fixed64 key-value pair.
159   Status WriteFixed64(uint32_t field_number, uint64_t value) {
160     std::byte* original_cursor = cursor_;
161     WriteFieldKey(field_number, WireType::kFixed64);
162     Status status = WriteRawBytes(value);
163     IncreaseParentSize(cursor_ - original_cursor);
164     return status;
165   }
166
167   // Writes a repeated fixed64 field using packed encoding.
168   Status WritePackedFixed64(uint32_t field_number,
169                             std::span<const uint64_t> values) {
170     return WriteBytes(field_number, std::as_bytes(values));
171   }
172
173   // Writes a proto sfixed32 key-value pair.
174   Status WriteSfixed32(uint32_t field_number, int32_t value) {
175     return WriteFixed32(field_number, static_cast<uint32_t>(value));
176   }
177
178   // Writes a repeated sfixed32 field using packed encoding.
179   Status WritePackedSfixed32(uint32_t field_number,
180                              std::span<const int32_t> values) {
181     return WriteBytes(field_number, std::as_bytes(values));
182   }
183
184   // Writes a proto sfixed64 key-value pair.
185   Status WriteSfixed64(uint32_t field_number, int64_t value) {
186     return WriteFixed64(field_number, static_cast<uint64_t>(value));
187   }
188
189   // Writes a repeated sfixed64 field using packed encoding.
190   Status WritePackedSfixed64(uint32_t field_number,
191                              std::span<const int64_t> values) {
192     return WriteBytes(field_number, std::as_bytes(values));
193   }
194
195   // Writes a proto float key-value pair.
196   Status WriteFloat(uint32_t field_number, float value) {
197     static_assert(sizeof(float) == sizeof(uint32_t),
198                   "Float and uint32_t are not the same size");
199     std::byte* original_cursor = cursor_;
200     WriteFieldKey(field_number, WireType::kFixed32);
201     Status status = WriteRawBytes(value);
202     IncreaseParentSize(cursor_ - original_cursor);
203     return status;
204   }
205
206   // Writes a repeated float field using packed encoding.
207   Status WritePackedFloat(uint32_t field_number,
208                           std::span<const float> values) {
209     return WriteBytes(field_number, std::as_bytes(values));
210   }
211
212   // Writes a proto double key-value pair.
213   Status WriteDouble(uint32_t field_number, double value) {
214     static_assert(sizeof(double) == sizeof(uint64_t),
215                   "Double and uint64_t are not the same size");
216     std::byte* original_cursor = cursor_;
217     WriteFieldKey(field_number, WireType::kFixed64);
218     Status status = WriteRawBytes(value);
219     IncreaseParentSize(cursor_ - original_cursor);
220     return status;
221   }
222
223   // Writes a repeated double field using packed encoding.
224   Status WritePackedDouble(uint32_t field_number,
225                            std::span<const double> values) {
226     return WriteBytes(field_number, std::as_bytes(values));
227   }
228
229   // Writes a proto bytes key-value pair.
230   Status WriteBytes(uint32_t field_number, ConstByteSpan value) {
231     std::byte* original_cursor = cursor_;
232     WriteFieldKey(field_number, WireType::kDelimited);
233     WriteVarint(value.size_bytes());
234     Status status = WriteRawBytes(value.data(), value.size_bytes());
235     IncreaseParentSize(cursor_ - original_cursor);
236     return status;
237   }
238
239   // Writes a proto string key-value pair.
240   Status WriteString(uint32_t field_number, const char* value, size_t size) {
241     return WriteBytes(field_number, std::as_bytes(std::span(value, size)));
242   }
243
244   Status WriteString(uint32_t field_number, const char* value) {
245     return WriteString(field_number, value, strlen(value));
246   }
247
248   // Begins writing a sub-message with a specified field number.
249   Status Push(uint32_t field_number);
250
251   // Finishes writing a sub-message.
252   Status Pop();
253
254   // Returns the total encoded size of the proto message.
255   size_t EncodedSize() const { return cursor_ - buffer_.data(); }
256
257   // Returns the number of bytes remaining in the buffer.
258   size_t RemainingSize() const { return buffer_.size() - EncodedSize(); }
259
260   // Resets write index to the start of the buffer. This invalidates any spans
261   // obtained from Encode().
262   void Clear() {
263     cursor_ = buffer_.data();
264     encode_status_ = OkStatus();
265     blob_count_ = 0;
266     depth_ = 0;
267   }
268
269   // Runs a final encoding pass over the intermediary data and returns the
270   // encoded protobuf message.
271   Result<ConstByteSpan> Encode();
272
273   // DEPRECATED. Use Encode() instead.
274   // TODO(frolv): Remove this after all references to it are updated.
275   Status Encode(ConstByteSpan* out) {
276     Result result = Encode();
277     if (!result.ok()) {
278       return result.status();
279     }
280     *out = result.value();
281     return OkStatus();
282   }
283
284  private:
285   constexpr bool ValidFieldNumber(uint32_t field_number) const {
286     return field_number != 0 && field_number <= kMaxFieldNumber &&
287            !(field_number >= kFirstReservedNumber &&
288              field_number <= kLastReservedNumber);
289   }
290
291   // Encodes the key for a proto field consisting of its number and wire type.
292   Status WriteFieldKey(uint32_t field_number, WireType wire_type) {
293     if (!ValidFieldNumber(field_number)) {
294       encode_status_ = Status::InvalidArgument();
295       return encode_status_;
296     }
297
298     return WriteVarint(MakeKey(field_number, wire_type));
299   }
300
301   Status WriteVarint(uint64_t value);
302
303   Status WriteZigzagVarint(int64_t value) {
304     return WriteVarint(varint::ZigZagEncode(value));
305   }
306
307   template <typename T>
308   Status WriteRawBytes(const T& value) {
309     return WriteRawBytes(reinterpret_cast<const std::byte*>(&value),
310                          sizeof(value));
311   }
312
313   Status WriteRawBytes(const std::byte* ptr, size_t size);
314
315   // Writes a list of varints to the buffer in length-delimited packed encoding.
316   // If zigzag is true, zig-zag encodes each of the varints.
317   template <typename T>
318   Status WritePackedVarints(uint32_t field_number,
319                             std::span<T> values,
320                             bool zigzag) {
321     if (Status status = Push(field_number); !status.ok()) {
322       return status;
323     }
324
325     std::byte* original_cursor = cursor_;
326     for (T value : values) {
327       if (zigzag) {
328         WriteZigzagVarint(static_cast<std::make_signed_t<T>>(value));
329       } else {
330         WriteVarint(value);
331       }
332     }
333     IncreaseParentSize(cursor_ - original_cursor);
334
335     return Pop();
336   }
337
338   // Adds to the parent proto's size field in the buffer.
339   void IncreaseParentSize(size_t bytes) {
340     if (depth_ > 0) {
341       *blob_stack_[depth_ - 1] += bytes;
342     }
343   }
344
345   // Returns the size of `n` encoded as a varint.
346   size_t VarintSizeBytes(uint64_t n) {
347     size_t size_bytes = 1;
348     while (n > 127) {
349       ++size_bytes;
350       n >>= 7;
351     }
352     return size_bytes;
353   }
354
355   // Do the actual (potentially partial) encoding. Also used in Pop().
356   Result<ConstByteSpan> EncodeFrom(size_t blob);
357
358   // The buffer into which the proto is encoded.
359   ByteSpan buffer_;
360   std::byte* cursor_;
361
362   // List of pointers to sub-messages' delimiting size fields.
363   std::span<SizeType*> blob_locations_;
364   size_t blob_count_;
365
366   // Stack of current nested message size locations. Push() operations add a new
367   // entry to this stack and Pop() operations remove one.
368   std::span<SizeType*> blob_stack_;
369   size_t depth_;
370
371   Status encode_status_;
372 };
373
374 // A proto encoder with its own blob stack.
375 template <size_t kMaxNestedDepth = 1, size_t kMaxBlobs = kMaxNestedDepth>
376 class NestedEncoder : public Encoder {
377  public:
378   NestedEncoder(ByteSpan buffer) : Encoder(buffer, blobs_, stack_) {}
379
380   // Disallow copy/assign to avoid confusion about who owns the buffer.
381   NestedEncoder(const NestedEncoder& other) = delete;
382   NestedEncoder& operator=(const NestedEncoder& other) = delete;
383
384  private:
385   std::array<size_t*, kMaxBlobs> blobs_;
386   std::array<size_t*, kMaxNestedDepth> stack_;
387 };
388
389 // Explicit template argument deduction to hide warnings.
390 NestedEncoder()->NestedEncoder<>;
391
392 }  // namespace pw::protobuf