1 // Copyright 2021 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 <algorithm>
17 #include <array>
18 #include <bit>
19 #include <cstddef>
20 #include <cstring>
21 #include <span>
22 #include <string_view>
23
24 #include "pw_assert/assert.h"
25 #include "pw_bytes/endian.h"
26 #include "pw_bytes/span.h"
27 #include "pw_containers/vector.h"
28 #include "pw_protobuf/config.h"
29 #include "pw_protobuf/wire_format.h"
30 #include "pw_status/status.h"
31 #include "pw_status/try.h"
32 #include "pw_stream/memory_stream.h"
33 #include "pw_stream/stream.h"
34 #include "pw_varint/varint.h"
35
36 namespace pw::protobuf {
37
38 // Provides a size estimate to help with sizing buffers passed to
39 // StreamEncoder and MemoryEncoder objects.
40 //
41 // Args:
42 // max_message_size: For MemoryEncoder objects, this is the max expected size
43 // of the final proto. For StreamEncoder objects, this should be the max
44 // size of any nested proto submessage that will be built with this encoder
45 // (recursively accumulating the size from the root submessage). If your
46 // proto will encode many large submessages, this value should just be the
47 // size of the largest one.
48 // max_nested_depth: The max number of nested submessage encoders that are
49 // expected to be open simultaneously to encode this proto message.
MaxScratchBufferSize(size_t max_message_size,size_t max_nested_depth)50 constexpr size_t MaxScratchBufferSize(size_t max_message_size,
51 size_t max_nested_depth) {
52 return max_message_size + max_nested_depth * config::kMaxVarintSize;
53 }
54
55 // Write a varint value to the writer.
56 //
57 // Args:
58 // value: The value of the varint to write
59 // writer: The writer for writing to output.
60 //
61 // Returns:
62 // OK - varint is written successfully
63 //
64 // Errors encountered by the `writer` will be returned as it is.
WriteVarint(uint64_t value,stream::Writer & writer)65 inline Status WriteVarint(uint64_t value, stream::Writer& writer) {
66 std::array<std::byte, varint::kMaxVarint64SizeBytes> varint_encode_buffer;
67 const size_t varint_size =
68 pw::varint::EncodeLittleEndianBase128(value, varint_encode_buffer);
69 return writer.Write(std::span(varint_encode_buffer).first(varint_size));
70 }
71
72 // Write the field key and length prefix for a length-delimited field. It is
73 // up to the caller to ensure that this will be followed by an exact number
74 // of bytes written for the field in order to form a valid proto message.
75 //
76 // Args:
77 // field_number: The field number for the field.
78 // payload_size: The size of the payload.
79 // writer: The output writer to write to
80 //
81 //
82 // Returns:
83 // OK - Field key is written successfully
84 //
85 // Errors encountered by the `writer` will be returned as it is.
86 //
87 // Precondition: The field_number must be a ValidFieldNumber.
88 // Precondition: `data_size_bytes` must be smaller than
89 // std::numeric_limits<uint32_t>::max()
WriteLengthDelimitedKeyAndLengthPrefix(uint32_t field_number,size_t payload_size,stream::Writer & writer)90 inline Status WriteLengthDelimitedKeyAndLengthPrefix(uint32_t field_number,
91 size_t payload_size,
92 stream::Writer& writer) {
93 PW_TRY(WriteVarint(FieldKey(field_number, WireType::kDelimited), writer));
94 return WriteVarint(payload_size, writer);
95 }
96
97 // Forward declaration. StreamEncoder and MemoryEncoder are very tightly
98 // coupled.
99 class MemoryEncoder;
100
101 // A protobuf encoder that encodes serialized proto data to a
102 // pw::stream::Writer.
103 class StreamEncoder {
104 public:
105 // The StreamEncoder will serialize proto data to the pw::stream::Writer
106 // provided through the constructor. The scratch buffer provided is for
107 // internal use ONLY and should not be considered valid proto data.
108 //
109 // If a StreamEncoder object will be writing nested proto messages, it must
110 // provide a scratch buffer large enough to hold the largest submessage some
111 // additional overhead incurred by the encoder's implementation. It's a good
112 // idea to be generous when sizing this buffer. MaxScratchBufferSize() can be
113 // helpful in providing an estimated size for this buffer. The scratch buffer
114 // must exist for the lifetime of the StreamEncoder object.
115 //
116 // StreamEncoder objects that do not write nested proto messages can
117 // provide a zero-length scratch buffer.
StreamEncoder(stream::Writer & writer,ByteSpan scratch_buffer)118 constexpr StreamEncoder(stream::Writer& writer, ByteSpan scratch_buffer)
119 : status_(OkStatus()),
120 parent_(nullptr),
121 nested_field_number_(0),
122 memory_writer_(scratch_buffer),
123 writer_(writer) {}
124
125 // Precondition: Encoder has no active child encoder.
126 //
127 // Postcondition: If this encoder is a nested one, the parent encoder is
128 // unlocked and proto encoding may resume on the parent.
129 ~StreamEncoder();
130
131 // Disallow copy/assign to avoid confusion about who owns the buffer.
132 StreamEncoder& operator=(const StreamEncoder& other) = delete;
133 StreamEncoder(const StreamEncoder& other) = delete;
134
135 // It's not safe to move an encoder as it could cause another encoder's
136 // parent_ pointer to become invalid.
137 StreamEncoder& operator=(StreamEncoder&& other) = delete;
138
139 // Forwards the conservative write limit of the underlying
140 // pw::stream::Writer.
141 //
142 // Precondition: Encoder has no active child encoder.
ConservativeWriteLimit()143 size_t ConservativeWriteLimit() const {
144 PW_ASSERT(!nested_encoder_open());
145 return writer_.ConservativeWriteLimit();
146 }
147
148 // Creates a nested encoder with the provided field number. Once this is
149 // called, the parent encoder is locked and not available for use until the
150 // nested encoder is finalized (either explicitly or through destruction).
151 //
152 // Precondition: Encoder has no active child encoder.
153 //
154 // Postcondition: Until the nested child encoder has been destroyed, this
155 // encoder cannot be used.
156 StreamEncoder GetNestedEncoder(uint32_t field_number);
157
158 // Returns the current encoder's status.
159 //
160 // Precondition: Encoder has no active child encoder.
status()161 Status status() const {
162 PW_ASSERT(!nested_encoder_open());
163 return status_;
164 }
165
166 // Writes a proto uint32 key-value pair.
167 //
168 // Precondition: Encoder has no active child encoder.
WriteUint32(uint32_t field_number,uint32_t value)169 Status WriteUint32(uint32_t field_number, uint32_t value) {
170 return WriteUint64(field_number, value);
171 }
172
173 // Writes a repeated uint32 using packed encoding.
174 //
175 // Precondition: Encoder has no active child encoder.
WritePackedUint32(uint32_t field_number,std::span<const uint32_t> values)176 Status WritePackedUint32(uint32_t field_number,
177 std::span<const uint32_t> values) {
178 return WritePackedVarints(field_number, values, VarintEncodeType::kNormal);
179 }
180
181 // Writes a repeated uint32 using packed encoding.
182 //
183 // Precondition: Encoder has no active child encoder.
WriteRepeatedUint32(uint32_t field_number,const pw::Vector<uint32_t> & values)184 Status WriteRepeatedUint32(uint32_t field_number,
185 const pw::Vector<uint32_t>& values) {
186 return WritePackedVarints(field_number,
187 std::span(values.data(), values.size()),
188 VarintEncodeType::kNormal);
189 }
190
191 // Writes a proto uint64 key-value pair.
192 //
193 // Precondition: Encoder has no active child encoder.
WriteUint64(uint32_t field_number,uint64_t value)194 Status WriteUint64(uint32_t field_number, uint64_t value) {
195 return WriteVarintField(field_number, value);
196 }
197
198 // Writes a repeated uint64 using packed encoding.
199 //
200 // Precondition: Encoder has no active child encoder.
WritePackedUint64(uint64_t field_number,std::span<const uint64_t> values)201 Status WritePackedUint64(uint64_t field_number,
202 std::span<const uint64_t> values) {
203 return WritePackedVarints(field_number, values, VarintEncodeType::kNormal);
204 }
205
206 // Writes a repeated uint64 using packed encoding.
207 //
208 // Precondition: Encoder has no active child encoder.
WriteRepeatedUint64(uint32_t field_number,const pw::Vector<uint64_t> & values)209 Status WriteRepeatedUint64(uint32_t field_number,
210 const pw::Vector<uint64_t>& values) {
211 return WritePackedVarints(field_number,
212 std::span(values.data(), values.size()),
213 VarintEncodeType::kNormal);
214 }
215
216 // Writes a proto int32 key-value pair.
217 //
218 // Precondition: Encoder has no active child encoder.
WriteInt32(uint32_t field_number,int32_t value)219 Status WriteInt32(uint32_t field_number, int32_t value) {
220 return WriteUint64(field_number, value);
221 }
222
223 // Writes a repeated int32 using packed encoding.
224 //
225 // Precondition: Encoder has no active child encoder.
WritePackedInt32(uint32_t field_number,std::span<const int32_t> values)226 Status WritePackedInt32(uint32_t field_number,
227 std::span<const int32_t> values) {
228 return WritePackedVarints(
229 field_number,
230 std::span(reinterpret_cast<const uint32_t*>(values.data()),
231 values.size()),
232 VarintEncodeType::kNormal);
233 }
234
235 // Writes a repeated int32 using packed encoding.
236 //
237 // Precondition: Encoder has no active child encoder.
WriteRepeatedInt32(uint32_t field_number,const pw::Vector<int32_t> & values)238 Status WriteRepeatedInt32(uint32_t field_number,
239 const pw::Vector<int32_t>& values) {
240 return WritePackedVarints(
241 field_number,
242 std::span(reinterpret_cast<const uint32_t*>(values.data()),
243 values.size()),
244 VarintEncodeType::kNormal);
245 }
246
247 // Writes a proto int64 key-value pair.
248 //
249 // Precondition: Encoder has no active child encoder.
WriteInt64(uint32_t field_number,int64_t value)250 Status WriteInt64(uint32_t field_number, int64_t value) {
251 return WriteUint64(field_number, value);
252 }
253
254 // Writes a repeated int64 using packed encoding.
255 //
256 // Precondition: Encoder has no active child encoder.
WritePackedInt64(uint32_t field_number,std::span<const int64_t> values)257 Status WritePackedInt64(uint32_t field_number,
258 std::span<const int64_t> values) {
259 return WritePackedVarints(
260 field_number,
261 std::span(reinterpret_cast<const uint64_t*>(values.data()),
262 values.size()),
263 VarintEncodeType::kNormal);
264 }
265
266 // Writes a repeated int64 using packed encoding.
267 //
268 // Precondition: Encoder has no active child encoder.
WriteRepeatedInt64(uint32_t field_number,const pw::Vector<int64_t> & values)269 Status WriteRepeatedInt64(uint32_t field_number,
270 const pw::Vector<int64_t>& values) {
271 return WritePackedVarints(
272 field_number,
273 std::span(reinterpret_cast<const uint64_t*>(values.data()),
274 values.size()),
275 VarintEncodeType::kNormal);
276 }
277
278 // Writes a proto sint32 key-value pair.
279 //
280 // Precondition: Encoder has no active child encoder.
WriteSint32(uint32_t field_number,int32_t value)281 Status WriteSint32(uint32_t field_number, int32_t value) {
282 return WriteUint64(field_number, varint::ZigZagEncode(value));
283 }
284
285 // Writes a repeated sint32 using packed encoding.
286 //
287 // Precondition: Encoder has no active child encoder.
WritePackedSint32(uint32_t field_number,std::span<const int32_t> values)288 Status WritePackedSint32(uint32_t field_number,
289 std::span<const int32_t> values) {
290 return WritePackedVarints(
291 field_number,
292 std::span(reinterpret_cast<const uint32_t*>(values.data()),
293 values.size()),
294 VarintEncodeType::kZigZag);
295 }
296
297 // Writes a repeated sint32 using packed encoding.
298 //
299 // Precondition: Encoder has no active child encoder.
WriteRepeatedSint32(uint32_t field_number,const pw::Vector<int32_t> & values)300 Status WriteRepeatedSint32(uint32_t field_number,
301 const pw::Vector<int32_t>& values) {
302 return WritePackedVarints(
303 field_number,
304 std::span(reinterpret_cast<const uint32_t*>(values.data()),
305 values.size()),
306 VarintEncodeType::kZigZag);
307 }
308
309 // Writes a proto sint64 key-value pair.
310 //
311 // Precondition: Encoder has no active child encoder.
WriteSint64(uint32_t field_number,int64_t value)312 Status WriteSint64(uint32_t field_number, int64_t value) {
313 return WriteUint64(field_number, varint::ZigZagEncode(value));
314 }
315
316 // Writes a repeated sint64 using packed encoding.
317 //
318 // Precondition: Encoder has no active child encoder.
WritePackedSint64(uint32_t field_number,std::span<const int64_t> values)319 Status WritePackedSint64(uint32_t field_number,
320 std::span<const int64_t> values) {
321 return WritePackedVarints(
322 field_number,
323 std::span(reinterpret_cast<const uint64_t*>(values.data()),
324 values.size()),
325 VarintEncodeType::kZigZag);
326 }
327
328 // Writes a repeated sint64 using packed encoding.
329 //
330 // Precondition: Encoder has no active child encoder.
WriteRepeatedSint64(uint32_t field_number,const pw::Vector<int64_t> & values)331 Status WriteRepeatedSint64(uint32_t field_number,
332 const pw::Vector<int64_t>& values) {
333 return WritePackedVarints(
334 field_number,
335 std::span(reinterpret_cast<const uint64_t*>(values.data()),
336 values.size()),
337 VarintEncodeType::kZigZag);
338 }
339
340 // Writes a proto bool key-value pair.
341 //
342 // Precondition: Encoder has no active child encoder.
WriteBool(uint32_t field_number,bool value)343 Status WriteBool(uint32_t field_number, bool value) {
344 return WriteUint32(field_number, static_cast<uint32_t>(value));
345 }
346
347 // Writes a repeated bool using packed encoding.
348 //
349 // Precondition: Encoder has no active child encoder.
WritePackedBool(uint32_t field_number,std::span<const bool> values)350 Status WritePackedBool(uint32_t field_number, std::span<const bool> values) {
351 static_assert(sizeof(bool) == sizeof(uint8_t),
352 "bool must be same size as uint8_t");
353 return WritePackedVarints(
354 field_number,
355 std::span(reinterpret_cast<const uint8_t*>(values.data()),
356 values.size()),
357 VarintEncodeType::kNormal);
358 }
359
360 // Writes a repeated bool using packed encoding.
361 //
362 // Precondition: Encoder has no active child encoder.
WriteRepeatedBool(uint32_t field_number,const pw::Vector<bool> & values)363 Status WriteRepeatedBool(uint32_t field_number,
364 const pw::Vector<bool>& values) {
365 static_assert(sizeof(bool) == sizeof(uint8_t),
366 "bool must be same size as uint8_t");
367
368 return WritePackedVarints(
369 field_number,
370 std::span(reinterpret_cast<const uint8_t*>(values.data()),
371 values.size()),
372 VarintEncodeType::kNormal);
373 }
374
375 // Writes a proto fixed32 key-value pair.
376 //
377 // Precondition: Encoder has no active child encoder.
WriteFixed32(uint32_t field_number,uint32_t value)378 Status WriteFixed32(uint32_t field_number, uint32_t value) {
379 std::array<std::byte, sizeof(value)> data =
380 bytes::CopyInOrder(std::endian::little, value);
381 return WriteFixed(field_number, data);
382 }
383
384 // Writes a repeated fixed32 field using packed encoding.
385 //
386 // Precondition: Encoder has no active child encoder.
WritePackedFixed32(uint32_t field_number,std::span<const uint32_t> values)387 Status WritePackedFixed32(uint32_t field_number,
388 std::span<const uint32_t> values) {
389 return WritePackedFixed(
390 field_number, std::as_bytes(values), sizeof(uint32_t));
391 }
392
393 // Writes a repeated fixed32 field using packed encoding.
394 //
395 // Precondition: Encoder has no active child encoder.
WriteRepeatedFixed32(uint32_t field_number,const pw::Vector<uint32_t> & values)396 Status WriteRepeatedFixed32(uint32_t field_number,
397 const pw::Vector<uint32_t>& values) {
398 return WritePackedFixed(
399 field_number,
400 std::as_bytes(std::span(values.data(), values.size())),
401 sizeof(uint32_t));
402 }
403
404 // Writes a proto fixed64 key-value pair.
405 //
406 // Precondition: Encoder has no active child encoder.
WriteFixed64(uint32_t field_number,uint64_t value)407 Status WriteFixed64(uint32_t field_number, uint64_t value) {
408 std::array<std::byte, sizeof(value)> data =
409 bytes::CopyInOrder(std::endian::little, value);
410 return WriteFixed(field_number, data);
411 }
412
413 // Writes a repeated fixed64 field using packed encoding.
414 //
415 // Precondition: Encoder has no active child encoder.
WritePackedFixed64(uint32_t field_number,std::span<const uint64_t> values)416 Status WritePackedFixed64(uint32_t field_number,
417 std::span<const uint64_t> values) {
418 return WritePackedFixed(
419 field_number, std::as_bytes(values), sizeof(uint64_t));
420 }
421
422 // Writes a repeated fixed64 field using packed encoding.
423 //
424 // Precondition: Encoder has no active child encoder.
WriteRepeatedFixed64(uint32_t field_number,const pw::Vector<uint64_t> & values)425 Status WriteRepeatedFixed64(uint32_t field_number,
426 const pw::Vector<uint64_t>& values) {
427 return WritePackedFixed(
428 field_number,
429 std::as_bytes(std::span(values.data(), values.size())),
430 sizeof(uint64_t));
431 }
432
433 // Writes a proto sfixed32 key-value pair.
434 //
435 // Precondition: Encoder has no active child encoder.
WriteSfixed32(uint32_t field_number,int32_t value)436 Status WriteSfixed32(uint32_t field_number, int32_t value) {
437 return WriteFixed32(field_number, static_cast<uint32_t>(value));
438 }
439
440 // Writes a repeated sfixed32 field using packed encoding.
441 //
442 // Precondition: Encoder has no active child encoder.
WritePackedSfixed32(uint32_t field_number,std::span<const int32_t> values)443 Status WritePackedSfixed32(uint32_t field_number,
444 std::span<const int32_t> values) {
445 return WritePackedFixed(
446 field_number, std::as_bytes(values), sizeof(int32_t));
447 }
448
449 // Writes a repeated fixed32 field using packed encoding.
450 //
451 // Precondition: Encoder has no active child encoder.
WriteRepeatedSfixed32(uint32_t field_number,const pw::Vector<int32_t> & values)452 Status WriteRepeatedSfixed32(uint32_t field_number,
453 const pw::Vector<int32_t>& values) {
454 return WritePackedFixed(
455 field_number,
456 std::as_bytes(std::span(values.data(), values.size())),
457 sizeof(int32_t));
458 }
459
460 // Writes a proto sfixed64 key-value pair.
461 //
462 // Precondition: Encoder has no active child encoder.
WriteSfixed64(uint32_t field_number,int64_t value)463 Status WriteSfixed64(uint32_t field_number, int64_t value) {
464 return WriteFixed64(field_number, static_cast<uint64_t>(value));
465 }
466
467 // Writes a repeated sfixed64 field using packed encoding.
468 //
469 // Precondition: Encoder has no active child encoder.
WritePackedSfixed64(uint32_t field_number,std::span<const int64_t> values)470 Status WritePackedSfixed64(uint32_t field_number,
471 std::span<const int64_t> values) {
472 return WritePackedFixed(
473 field_number, std::as_bytes(values), sizeof(int64_t));
474 }
475
476 // Writes a repeated fixed64 field using packed encoding.
477 //
478 // Precondition: Encoder has no active child encoder.
WriteRepeatedFixed64(uint32_t field_number,const pw::Vector<int64_t> & values)479 Status WriteRepeatedFixed64(uint32_t field_number,
480 const pw::Vector<int64_t>& values) {
481 return WritePackedFixed(
482 field_number,
483 std::as_bytes(std::span(values.data(), values.size())),
484 sizeof(int64_t));
485 }
486
487 // Writes a proto float key-value pair.
488 //
489 // Precondition: Encoder has no active child encoder.
WriteFloat(uint32_t field_number,float value)490 Status WriteFloat(uint32_t field_number, float value) {
491 static_assert(sizeof(float) == sizeof(uint32_t),
492 "Float and uint32_t are not the same size");
493 uint32_t integral_value;
494 std::memcpy(&integral_value, &value, sizeof(value));
495 std::array<std::byte, sizeof(value)> data =
496 bytes::CopyInOrder(std::endian::little, integral_value);
497 return WriteFixed(field_number, data);
498 }
499
500 // Writes a repeated float field using packed encoding.
501 //
502 // Precondition: Encoder has no active child encoder.
WritePackedFloat(uint32_t field_number,std::span<const float> values)503 Status WritePackedFloat(uint32_t field_number,
504 std::span<const float> values) {
505 return WritePackedFixed(field_number, std::as_bytes(values), sizeof(float));
506 }
507
508 // Writes a repeated float field using packed encoding.
509 //
510 // Precondition: Encoder has no active child encoder.
WriteRepeatedFloat(uint32_t field_number,const pw::Vector<float> & values)511 Status WriteRepeatedFloat(uint32_t field_number,
512 const pw::Vector<float>& values) {
513 return WritePackedFixed(
514 field_number,
515 std::as_bytes(std::span(values.data(), values.size())),
516 sizeof(float));
517 }
518
519 // Writes a proto double key-value pair.
520 //
521 // Precondition: Encoder has no active child encoder.
WriteDouble(uint32_t field_number,double value)522 Status WriteDouble(uint32_t field_number, double value) {
523 static_assert(sizeof(double) == sizeof(uint64_t),
524 "Double and uint64_t are not the same size");
525 uint64_t integral_value;
526 std::memcpy(&integral_value, &value, sizeof(value));
527 std::array<std::byte, sizeof(value)> data =
528 bytes::CopyInOrder(std::endian::little, integral_value);
529 return WriteFixed(field_number, data);
530 }
531
532 // Writes a repeated double field using packed encoding.
533 //
534 // Precondition: Encoder has no active child encoder.
WritePackedDouble(uint32_t field_number,std::span<const double> values)535 Status WritePackedDouble(uint32_t field_number,
536 std::span<const double> values) {
537 return WritePackedFixed(
538 field_number, std::as_bytes(values), sizeof(double));
539 }
540
541 // Writes a repeated double field using packed encoding.
542 //
543 // Precondition: Encoder has no active child encoder.
WriteRepeatedDouble(uint32_t field_number,const pw::Vector<double> & values)544 Status WriteRepeatedDouble(uint32_t field_number,
545 const pw::Vector<double>& values) {
546 return WritePackedFixed(
547 field_number,
548 std::as_bytes(std::span(values.data(), values.size())),
549 sizeof(double));
550 }
551
552 // Writes a proto `bytes` field as a key-value pair. This can also be used to
553 // write a pre-encoded nested submessage directly without using a nested
554 // encoder.
555 //
556 // Precondition: Encoder has no active child encoder.
WriteBytes(uint32_t field_number,ConstByteSpan value)557 Status WriteBytes(uint32_t field_number, ConstByteSpan value) {
558 return WriteLengthDelimitedField(field_number, value);
559 }
560
561 // Writes a proto 'bytes' field from the stream bytes_reader.
562 //
563 // The payload for the value is provided through the stream::Reader
564 // `bytes_reader`. The method reads a chunk of the data from the reader using
565 // the `stream_pipe_buffer` and writes it to the encoder.
566 //
567 // Precondition: The stream_pipe_buffer.byte_size() >= 1
568 // Precondition: Encoder has no active child encoder.
569 //
570 // Returns:
571 // OK - Bytes field is written successfully.
572 // RESOURCE_EXHAUSTED - Exceeds write limits.
573 // OUT_OF_RANGE - `bytes_reader` is exhausted before `num_bytes` of
574 // bytes is read.
575 //
576 // Other errors encountered by the writer will be returned as it is.
WriteBytesFromStream(uint32_t field_number,stream::Reader & bytes_reader,size_t num_bytes,ByteSpan stream_pipe_buffer)577 Status WriteBytesFromStream(uint32_t field_number,
578 stream::Reader& bytes_reader,
579 size_t num_bytes,
580 ByteSpan stream_pipe_buffer) {
581 return WriteLengthDelimitedFieldFromStream(
582 field_number, bytes_reader, num_bytes, stream_pipe_buffer);
583 }
584
585 // Writes a proto string key-value pair.
586 //
587 // Precondition: Encoder has no active child encoder.
WriteString(uint32_t field_number,std::string_view value)588 Status WriteString(uint32_t field_number, std::string_view value) {
589 return WriteBytes(field_number, std::as_bytes(std::span(value)));
590 }
591
592 // Writes a proto string key-value pair.
593 //
594 // Precondition: Encoder has no active child encoder.
WriteString(uint32_t field_number,const char * value,size_t len)595 Status WriteString(uint32_t field_number, const char* value, size_t len) {
596 return WriteBytes(field_number, std::as_bytes(std::span(value, len)));
597 }
598
599 // Writes a proto 'string' field from the stream bytes_reader.
600 //
601 // The payload for the value is provided through the stream::Reader
602 // `bytes_reader`. The method reads a chunk of the data from the reader using
603 // the `stream_pipe_buffer` and writes it to the encoder.
604 //
605 // Precondition: The stream_pipe_buffer.byte_size() >= 1
606 // Precondition: Encoder has no active child encoder.
607 //
608 // Returns:
609 // OK - String field is written successfully.
610 // RESOURCE_EXHAUSTED - Exceeds write limits.
611 // OUT_OF_RANGE - `bytes_reader` is exhausted before `num_bytes` of
612 // bytes is read.
613 //
614 // Other errors encountered by the writer will be returned as it is.
WriteStringFromStream(uint32_t field_number,stream::Reader & bytes_reader,size_t num_bytes,ByteSpan stream_pipe_buffer)615 Status WriteStringFromStream(uint32_t field_number,
616 stream::Reader& bytes_reader,
617 size_t num_bytes,
618 ByteSpan stream_pipe_buffer) {
619 return WriteBytesFromStream(
620 field_number, bytes_reader, num_bytes, stream_pipe_buffer);
621 }
622
623 protected:
624 // Specialized move constructor used only for codegen.
625 //
626 // Postcondition: The other encoder is invalidated and cannot be used as it
627 // acts like a parent encoder with an active child encoder.
StreamEncoder(StreamEncoder && other)628 constexpr StreamEncoder(StreamEncoder&& other)
629 : status_(other.status_),
630 parent_(other.parent_),
631 nested_field_number_(other.nested_field_number_),
632 memory_writer_(std::move(other.memory_writer_)),
633 writer_(&other.writer_ == &other.memory_writer_ ? memory_writer_
634 : other.writer_) {
635 PW_ASSERT(nested_field_number_ == 0);
636 // Make the nested encoder look like it has an open child to block writes
637 // for the remainder of the object's life.
638 other.nested_field_number_ = kFirstReservedNumber;
639 other.parent_ = nullptr;
640 }
641
642 private:
643 friend class MemoryEncoder;
644
645 enum class VarintEncodeType {
646 kNormal,
647 kZigZag,
648 };
649
StreamEncoder(StreamEncoder & parent,ByteSpan scratch_buffer)650 constexpr StreamEncoder(StreamEncoder& parent, ByteSpan scratch_buffer)
651 : status_(scratch_buffer.empty() ? Status::ResourceExhausted()
652 : OkStatus()),
653 parent_(&parent),
654 nested_field_number_(0),
655 memory_writer_(scratch_buffer),
656 writer_(memory_writer_) {}
657
nested_encoder_open()658 bool nested_encoder_open() const { return nested_field_number_ != 0; }
659
660 // CloseNestedMessage() is called on the parent encoder as part of the nested
661 // encoder destructor.
662 void CloseNestedMessage(StreamEncoder& nested);
663
664 // Implementation for encoding all varint field types.
665 Status WriteVarintField(uint32_t field_number, uint64_t value);
666
667 // Implementation for encoding all length-delimited field types.
668 Status WriteLengthDelimitedField(uint32_t field_number, ConstByteSpan data);
669
670 // Encoding of length-delimited field where payload comes from `bytes_reader`.
671 Status WriteLengthDelimitedFieldFromStream(uint32_t field_number,
672 stream::Reader& bytes_reader,
673 size_t num_bytes,
674 ByteSpan stream_pipe_buffer);
675
676 // Implementation for encoding all fixed-length integer types.
677 Status WriteFixed(uint32_t field_number, ConstByteSpan data);
678
679 // Encodes a base-128 varint to the buffer. This function assumes the caller
680 // has already checked UpdateStatusForWrite() to ensure the writer's
681 // conservative write limit indicates the Writer has sufficient buffer space.
WriteVarint(uint64_t value)682 Status WriteVarint(uint64_t value) {
683 PW_TRY(status_);
684 status_.Update(::pw::protobuf::WriteVarint(value, writer_));
685 return status_;
686 }
687
WriteZigzagVarint(int64_t value)688 Status WriteZigzagVarint(int64_t value) {
689 return WriteVarint(varint::ZigZagEncode(value));
690 }
691
692 // Writes a list of varints to the buffer in length-delimited packed encoding.
693 template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
WritePackedVarints(uint32_t field_number,std::span<T> values,VarintEncodeType encode_type)694 Status WritePackedVarints(uint32_t field_number,
695 std::span<T> values,
696 VarintEncodeType encode_type) {
697 static_assert(std::is_same<T, const uint8_t>::value ||
698 std::is_same<T, const uint32_t>::value ||
699 std::is_same<T, const int32_t>::value ||
700 std::is_same<T, const uint64_t>::value ||
701 std::is_same<T, const int64_t>::value,
702 "Packed varints must be of type bool, uint32_t, int32_t, "
703 "uint64_t, or int64_t");
704
705 size_t payload_size = 0;
706 for (T val : values) {
707 if (encode_type == VarintEncodeType::kZigZag) {
708 int64_t integer =
709 static_cast<int64_t>(static_cast<std::make_signed_t<T>>(val));
710 payload_size += varint::EncodedSize(varint::ZigZagEncode(integer));
711 } else {
712 uint64_t integer = static_cast<uint64_t>(val);
713 payload_size += varint::EncodedSize(integer);
714 }
715 }
716
717 if (!UpdateStatusForWrite(field_number, WireType::kDelimited, payload_size)
718 .ok()) {
719 return status_;
720 }
721
722 WriteVarint(FieldKey(field_number, WireType::kDelimited))
723 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
724 WriteVarint(payload_size)
725 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
726 for (T value : values) {
727 if (encode_type == VarintEncodeType::kZigZag) {
728 WriteZigzagVarint(static_cast<std::make_signed_t<T>>(value))
729 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
730 } else {
731 WriteVarint(value)
732 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
733 }
734 }
735
736 return status_;
737 }
738
739 // Writes a list of fixed-size types to the buffer in length-delimited
740 // packed encoding. Only float, double, uint32_t, int32_t, uint64_t, and
741 // int64_t are permitted
742 Status WritePackedFixed(uint32_t field_number,
743 std::span<const std::byte> values,
744 size_t elem_size);
745
746 // Checks if a write is invalid or will cause the encoder to enter an error
747 // state, and preemptively sets this encoder's status to that error to block
748 // the write. Only the first error encountered is tracked.
749 //
750 // Precondition: Encoder has no active child encoder.
751 //
752 // Returns:
753 // InvalidArgument: The field number provided was invalid.
754 // ResourceExhausted: The requested write would have exceeded the
755 // stream::Writer's conservative write limit.
756 // Other: If any Write() operations on the stream::Writer caused an error,
757 // that error will be repeated here.
758 Status UpdateStatusForWrite(uint32_t field_number,
759 WireType type,
760 size_t data_size);
761
762 // The current encoder status. This status is only updated to reflect the
763 // first error encountered. Any further write operations are blocked when the
764 // encoder enters an error state.
765 Status status_;
766
767 // If this is a nested encoder, this points to the encoder that created it.
768 // For user-created MemoryEncoders, parent_ points to this object as an
769 // optimization for the MemoryEncoder and nested encoders to use the same
770 // underlying buffer.
771 StreamEncoder* parent_;
772
773 // If an encoder has a child encoder open, this is the field number of that
774 // submessage. Otherwise, this is 0 to indicate no child encoder is open.
775 uint32_t nested_field_number_;
776
777 // This memory writer is used for staging proto submessages to the
778 // scratch_buffer.
779 stream::MemoryWriter memory_writer_;
780
781 // All proto encode operations are directly written to this writer.
782 stream::Writer& writer_;
783 };
784
785 // A protobuf encoder that writes directly to a provided buffer.
786 //
787 // Example:
788 //
789 // // Writes a proto response to the provided buffer, returning the encode
790 // // status and number of bytes written.
791 // StatusWithSize WriteProtoResponse(ByteSpan response) {
792 // // All proto writes are directly written to the `response` buffer.
793 // MemoryEncoder encoder(response);
794 // encoder.WriteUint32(kMagicNumberField, 0x1a1a2b2b);
795 // encoder.WriteString(kFavoriteFood, "cookies");
796 // return StatusWithSize(encoder.status(), encoder.size());
797 // }
798 //
799 // Note: Avoid using a MemoryEncoder reference as an argument for a function.
800 // The StreamEncoder is more generic.
801 class MemoryEncoder : public StreamEncoder {
802 public:
MemoryEncoder(ByteSpan dest)803 constexpr MemoryEncoder(ByteSpan dest) : StreamEncoder(*this, dest) {}
804
805 // Precondition: Encoder has no active child encoder.
806 //
807 // Postcondition: If this encoder is a nested one, the parent encoder is
808 // unlocked and proto encoding may resume on the parent.
809 ~MemoryEncoder() = default;
810
811 // Disallow copy/assign to avoid confusion about who owns the buffer.
812 MemoryEncoder(const MemoryEncoder& other) = delete;
813 MemoryEncoder& operator=(const MemoryEncoder& other) = delete;
814
815 // It's not safe to move an encoder as it could cause another encoder's
816 // parent_ pointer to become invalid.
817 MemoryEncoder& operator=(MemoryEncoder&& other) = delete;
818
data()819 const std::byte* data() const { return memory_writer_.data(); }
size()820 size_t size() const { return memory_writer_.bytes_written(); }
821
822 protected:
823 // This is needed by codegen.
824 MemoryEncoder(MemoryEncoder&& other) = default;
825 };
826
827 } // namespace pw::protobuf
828