C/C++ API Reference
Loading...
Searching...
No Matches
encoder.h
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 <cstddef>
19#include <cstring>
20#include <string_view>
21#include <type_traits>
22
23#include "pw_assert/assert.h"
24#include "pw_bytes/bit.h"
25#include "pw_bytes/endian.h"
26#include "pw_bytes/span.h"
27#include "pw_containers/vector.h"
28#include "pw_memory/internal/sibling_cast.h"
29#include "pw_protobuf/config.h"
30#include "pw_protobuf/internal/codegen.h"
31#include "pw_protobuf/wire_format.h"
32#include "pw_span/span.h"
33#include "pw_status/status.h"
34#include "pw_status/status_with_size.h"
35#include "pw_status/try.h"
36#include "pw_stream/memory_stream.h"
37#include "pw_stream/stream.h"
38#include "pw_varint/varint.h"
39
40namespace pw::protobuf {
41
43
44// Provides a size estimate to help with sizing buffers passed to
45// StreamEncoder and MemoryEncoder objects.
46//
47// Args:
48// max_message_size: For MemoryEncoder objects, this is the max expected size
49// of the final proto. For StreamEncoder objects, this should be the max
50// size of any nested proto submessage that will be built with this encoder
51// (recursively accumulating the size from the root submessage). If your
52// proto will encode many large submessages, this value should just be the
53// size of the largest one.
54// max_nested_depth: The max number of nested submessage encoders that are
55// expected to be open simultaneously to encode this proto message.
56constexpr size_t MaxScratchBufferSize(size_t max_message_size,
57 size_t max_nested_depth) {
58 return max_message_size + max_nested_depth * config::kMaxVarintSize;
59}
60
61// Write a varint value to the writer.
62//
63// Args:
64// value: The value of the varint to write
65// writer: The writer for writing to output.
66//
67// Returns:
68// OK - varint is written successfully
69//
70// Errors encountered by the `writer` will be returned as it is.
71inline Status WriteVarint(uint64_t value, stream::Writer& writer) {
72 std::array<std::byte, varint::kMaxVarint64SizeBytes> varint_encode_buffer;
73 const size_t varint_size =
74 pw::varint::EncodeLittleEndianBase128(value, varint_encode_buffer);
75 return writer.Write(span(varint_encode_buffer).first(varint_size));
76}
77
78// Write the field key and length prefix for a length-delimited field. It is
79// up to the caller to ensure that this will be followed by an exact number
80// of bytes written for the field in order to form a valid proto message.
81//
82// Args:
83// field_number: The field number for the field.
84// payload_size: The size of the payload.
85// writer: The output writer to write to
86//
87//
88// Returns:
89// OK - Field key is written successfully
90//
91// Errors encountered by the `writer` will be returned as it is.
92//
93// Precondition: The field_number must be a ValidFieldNumber.
94// Precondition: `data_size_bytes` must be smaller than
95// std::numeric_limits<uint32_t>::max()
96inline Status WriteLengthDelimitedKeyAndLengthPrefix(uint32_t field_number,
97 size_t payload_size,
98 stream::Writer& writer) {
99 PW_TRY(WriteVarint(FieldKey(field_number, WireType::kDelimited), writer));
100 return WriteVarint(payload_size, writer);
101}
102
103// Forward declaration. StreamEncoder and MemoryEncoder are very tightly
104// coupled.
105class MemoryEncoder;
106
107// A protobuf encoder that encodes serialized proto data to a
108// pw::stream::Writer.
110 public:
111 // The StreamEncoder will serialize proto data to the pw::stream::Writer
112 // provided through the constructor. The scratch buffer provided is for
113 // internal use ONLY and should not be considered valid proto data.
114 //
115 // If a StreamEncoder object will be writing nested proto messages, it must
116 // provide a scratch buffer large enough to hold the largest submessage some
117 // additional overhead incurred by the encoder's implementation. It's a good
118 // idea to be generous when sizing this buffer. MaxScratchBufferSize() can be
119 // helpful in providing an estimated size for this buffer. The scratch buffer
120 // must exist for the lifetime of the StreamEncoder object.
121 //
122 // StreamEncoder objects that do not write nested proto messages can
123 // provide a zero-length scratch buffer.
124 constexpr StreamEncoder(stream::Writer& writer, ByteSpan scratch_buffer)
125 : status_(OkStatus()),
126 write_when_empty_(true),
127 parent_(nullptr),
128 nested_field_number_(0),
129 memory_writer_(scratch_buffer),
130 writer_(writer) {}
131
132 // Precondition: Encoder has no active child encoder.
133 //
134 // Postcondition: If this encoder is a nested one, the parent encoder is
135 // unlocked and proto encoding may resume on the parent.
136 ~StreamEncoder() { CloseEncoder(); }
137
138 // Disallow copy/assign to avoid confusion about who owns the buffer.
139 StreamEncoder& operator=(const StreamEncoder& other) = delete;
140 StreamEncoder(const StreamEncoder& other) = delete;
141
142 // It's not safe to move an encoder as it could cause another encoder's
143 // parent_ pointer to become invalid.
144 StreamEncoder& operator=(StreamEncoder&& other) = delete;
145
146 // Closes this encoder, finalizing its output.
147 //
148 // This method is called automatically by `StreamEncoder`'s destructor, but
149 // may be invoked manually in order to close an encoder before the end of its
150 // lexical scope.
151 //
152 // Precondition: Encoder has no active child encoder.
153 //
154 // Postcondition: If this encoder is a nested one, the parent encoder is
155 // unlocked and proto encoding may resume on the parent. No more writes
156 // to this encoder may be performed.
157 void CloseEncoder();
158
159 // Forwards the conservative write limit of the underlying
160 // pw::stream::Writer.
161 //
162 // Precondition: Encoder has no active child encoder.
163 size_t ConservativeWriteLimit() const {
164 PW_ASSERT(!nested_encoder_open());
165 return writer_.ConservativeWriteLimit();
166 }
167
168 enum class EmptyEncoderBehavior { kWriteFieldNumber, kWriteNothing };
169
170 // Creates a nested encoder with the provided field number. Once this is
171 // called, the parent encoder is locked and not available for use until the
172 // nested encoder is finalized (either explicitly or through destruction).
173 //
174 // Precondition: Encoder has no active child encoder.
175 //
176 // Postcondition: Until the nested child encoder has been destroyed, this
177 // encoder cannot be used.
178 StreamEncoder GetNestedEncoder(uint32_t field_number,
179 EmptyEncoderBehavior empty_encoder_behavior =
180 EmptyEncoderBehavior::kWriteFieldNumber) {
181 return GetNestedEncoder(
182 field_number, /*write_when_empty=*/
183 empty_encoder_behavior == EmptyEncoderBehavior::kWriteFieldNumber);
184 }
185
186 // Invokes a given callback with an encoder to write a nested message field.
187 //
188 // This performs a multi-pass encoding and invokes the callback twice:
189 // Once to compute the total size of the nested message; and again to
190 // actually write the encoded data to the stream (after the nested message
191 // field prefix has been written).
192 //
193 // Args:
194 // field_number: The field number of the submessage to be written.
195 // write_message: A callable which is responsible for writing the
196 // submessage fields using the encoder passed to it.
197 //
198 // It must have the following signature: Status(StreamEncoder& encoder)
199 //
200 // It will be invoked twice and MUST perform the exact same set of writes
201 // on both invocations.
202 //
203 // empty_encoder_behavior: (Optional) Indicates the action to take when
204 // nothing is written to the nested message encoder.
205 //
206 // Returns:
207 // OK - The nested message was successfully written.
208 // OUT_OF_RANGE - The callback wrote fewer bytes on the second pass than on
209 // the first.
210 // RESOURCE_EXHAUSTED - The callback wrote more bytes on the second pass than
211 // on the first.
212 // Any other error from the underlying stream.
213 //
214 // Precondition: Encoder has no active child encoder.
215 //
216 // The type of the callable argument is intentionally templated (rather than
217 // using pw::Function) to allow for arbitrarily large objects (e.g. lambdas
218 // with any number of captures) to be passed. Internally, the method
219 // type-erases the callable to eliminate code bloat due to template
220 // instantations. This works because the encoder does not need to access the
221 // callable after encoding has finished and the method returns.
222 // TODO: b/432525176 - Drop this template when we have pw::FunctionRef.
223 template <typename WriteFunc>
224 Status WriteNestedMessage(uint32_t field_number,
225 WriteFunc write_message,
226 EmptyEncoderBehavior empty_encoder_behavior =
227 EmptyEncoderBehavior::kWriteFieldNumber);
228
229 // Returns the current encoder's status.
230 //
231 // Precondition: Encoder has no active child encoder.
232 Status status() const {
233 PW_ASSERT(!nested_encoder_open());
234 return status_;
235 }
236
237 // Writes a proto uint32 key-value pair.
238 //
239 // Precondition: Encoder has no active child encoder.
240 Status WriteUint32(uint32_t field_number, uint32_t value) {
241 return WriteUint64(field_number, value);
242 }
243
244 // Writes a repeated uint32 using packed encoding.
245 //
246 // Precondition: Encoder has no active child encoder.
247 Status WritePackedUint32(uint32_t field_number, span<const uint32_t> values) {
248 return WritePackedVarints(
249 field_number, values, internal::VarintType::kNormal);
250 }
251
252 // Writes a repeated uint32 using packed encoding.
253 //
254 // Precondition: Encoder has no active child encoder.
255 Status WriteRepeatedUint32(uint32_t field_number,
256 const pw::Vector<uint32_t>& values) {
257 return WritePackedVarints(field_number,
258 span(values.data(), values.size()),
259 internal::VarintType::kNormal);
260 }
261
262 // Writes a proto uint64 key-value pair.
263 //
264 // Precondition: Encoder has no active child encoder.
265 Status WriteUint64(uint32_t field_number, uint64_t value) {
266 return WriteVarintField(field_number, value);
267 }
268
269 // Writes a repeated uint64 using packed encoding.
270 //
271 // Precondition: Encoder has no active child encoder.
272 Status WritePackedUint64(uint32_t field_number, span<const uint64_t> values) {
273 return WritePackedVarints(
274 field_number, values, internal::VarintType::kNormal);
275 }
276
277 // Writes a repeated uint64 using packed encoding.
278 //
279 // Precondition: Encoder has no active child encoder.
280 Status WriteRepeatedUint64(uint32_t field_number,
281 const pw::Vector<uint64_t>& values) {
282 return WritePackedVarints(field_number,
283 span(values.data(), values.size()),
284 internal::VarintType::kNormal);
285 }
286
287 // Writes a proto int32 key-value pair.
288 //
289 // Precondition: Encoder has no active child encoder.
290 Status WriteInt32(uint32_t field_number, int32_t value) {
291 // Signed numbers are sent as 2's complement so this cast is correct.
292 return WriteUint64(field_number, static_cast<uint64_t>(value));
293 }
294
295 // Writes a repeated int32 using packed encoding.
296 //
297 // Precondition: Encoder has no active child encoder.
298 Status WritePackedInt32(uint32_t field_number, span<const int32_t> values) {
299 return WritePackedVarints(
300 field_number,
301 span(reinterpret_cast<const uint32_t*>(values.data()), values.size()),
302 internal::VarintType::kNormal);
303 }
304
305 // Writes a repeated int32 using packed encoding.
306 //
307 // Precondition: Encoder has no active child encoder.
308 Status WriteRepeatedInt32(uint32_t field_number,
309 const pw::Vector<int32_t>& values) {
310 return WritePackedVarints(
311 field_number,
312 span(reinterpret_cast<const uint32_t*>(values.data()), values.size()),
313 internal::VarintType::kNormal);
314 }
315
316 // Writes a proto int64 key-value pair.
317 //
318 // Precondition: Encoder has no active child encoder.
319 Status WriteInt64(uint32_t field_number, int64_t value) {
320 // Signed numbers are sent as 2's complement so this cast is correct.
321 return WriteUint64(field_number, static_cast<uint64_t>(value));
322 }
323
324 // Writes a repeated int64 using packed encoding.
325 //
326 // Precondition: Encoder has no active child encoder.
327 Status WritePackedInt64(uint32_t field_number, span<const int64_t> values) {
328 return WritePackedVarints(
329 field_number,
330 span(reinterpret_cast<const uint64_t*>(values.data()), values.size()),
331 internal::VarintType::kNormal);
332 }
333
334 // Writes a repeated int64 using packed encoding.
335 //
336 // Precondition: Encoder has no active child encoder.
337 Status WriteRepeatedInt64(uint32_t field_number,
338 const pw::Vector<int64_t>& values) {
339 return WritePackedVarints(
340 field_number,
341 span(reinterpret_cast<const uint64_t*>(values.data()), values.size()),
342 internal::VarintType::kNormal);
343 }
344
345 // Writes a proto sint32 key-value pair.
346 //
347 // Precondition: Encoder has no active child encoder.
348 Status WriteSint32(uint32_t field_number, int32_t value) {
349 return WriteUint64(field_number, varint::ZigZagEncode(value));
350 }
351
352 // Writes a repeated sint32 using packed encoding.
353 //
354 // Precondition: Encoder has no active child encoder.
355 Status WritePackedSint32(uint32_t field_number, span<const int32_t> values) {
356 return WritePackedVarints(
357 field_number,
358 span(reinterpret_cast<const uint32_t*>(values.data()), values.size()),
359 internal::VarintType::kZigZag);
360 }
361
362 // Writes a repeated sint32 using packed encoding.
363 //
364 // Precondition: Encoder has no active child encoder.
365 Status WriteRepeatedSint32(uint32_t field_number,
366 const pw::Vector<int32_t>& values) {
367 return WritePackedVarints(
368 field_number,
369 span(reinterpret_cast<const uint32_t*>(values.data()), values.size()),
370 internal::VarintType::kZigZag);
371 }
372
373 // Writes a proto sint64 key-value pair.
374 //
375 // Precondition: Encoder has no active child encoder.
376 Status WriteSint64(uint32_t field_number, int64_t value) {
377 return WriteUint64(field_number, varint::ZigZagEncode(value));
378 }
379
380 // Writes a repeated sint64 using packed encoding.
381 //
382 // Precondition: Encoder has no active child encoder.
383 Status WritePackedSint64(uint32_t field_number, span<const int64_t> values) {
384 return WritePackedVarints(
385 field_number,
386 span(reinterpret_cast<const uint64_t*>(values.data()), values.size()),
387 internal::VarintType::kZigZag);
388 }
389
390 // Writes a repeated sint64 using packed encoding.
391 //
392 // Precondition: Encoder has no active child encoder.
393 Status WriteRepeatedSint64(uint32_t field_number,
394 const pw::Vector<int64_t>& values) {
395 return WritePackedVarints(
396 field_number,
397 span(reinterpret_cast<const uint64_t*>(values.data()), values.size()),
398 internal::VarintType::kZigZag);
399 }
400
401 // Writes a proto bool key-value pair.
402 //
403 // Precondition: Encoder has no active child encoder.
404 Status WriteBool(uint32_t field_number, bool value) {
405 return WriteUint32(field_number, static_cast<uint32_t>(value));
406 }
407
408 // Writes a repeated bool using packed encoding.
409 //
410 // Precondition: Encoder has no active child encoder.
411 Status WritePackedBool(uint32_t field_number, span<const bool> values) {
412 static_assert(sizeof(bool) == sizeof(uint8_t),
413 "bool must be same size as uint8_t");
414 return WritePackedVarints(
415 field_number,
416 span(reinterpret_cast<const uint8_t*>(values.data()), values.size()),
417 internal::VarintType::kNormal);
418 }
419
420 // Writes a repeated bool using packed encoding.
421 //
422 // Precondition: Encoder has no active child encoder.
423 Status WriteRepeatedBool(uint32_t field_number,
424 const pw::Vector<bool>& values) {
425 static_assert(sizeof(bool) == sizeof(uint8_t),
426 "bool must be same size as uint8_t");
427
428 return WritePackedVarints(
429 field_number,
430 span(reinterpret_cast<const uint8_t*>(values.data()), values.size()),
431 internal::VarintType::kNormal);
432 }
433
434 // Writes a proto fixed32 key-value pair.
435 //
436 // Precondition: Encoder has no active child encoder.
437 Status WriteFixed32(uint32_t field_number, uint32_t value) {
438 std::array<std::byte, sizeof(value)> data =
439 bytes::CopyInOrder(endian::little, value);
440 return WriteFixed(field_number, data);
441 }
442
443 // Writes a repeated fixed32 field using packed encoding.
444 //
445 // Precondition: Encoder has no active child encoder.
446 Status WritePackedFixed32(uint32_t field_number,
447 span<const uint32_t> values) {
448 return WritePackedFixed(field_number, as_bytes(values), sizeof(uint32_t));
449 }
450
451 // Writes a repeated fixed32 field using packed encoding.
452 //
453 // Precondition: Encoder has no active child encoder.
454 Status WriteRepeatedFixed32(uint32_t field_number,
455 const pw::Vector<uint32_t>& values) {
456 return WritePackedFixed(field_number,
457 as_bytes(span(values.data(), values.size())),
458 sizeof(uint32_t));
459 }
460
461 // Writes a proto fixed64 key-value pair.
462 //
463 // Precondition: Encoder has no active child encoder.
464 Status WriteFixed64(uint32_t field_number, uint64_t value) {
465 std::array<std::byte, sizeof(value)> data =
466 bytes::CopyInOrder(endian::little, value);
467 return WriteFixed(field_number, data);
468 }
469
470 // Writes a repeated fixed64 field using packed encoding.
471 //
472 // Precondition: Encoder has no active child encoder.
473 Status WritePackedFixed64(uint32_t field_number,
474 span<const uint64_t> values) {
475 return WritePackedFixed(field_number, as_bytes(values), sizeof(uint64_t));
476 }
477
478 // Writes a repeated fixed64 field using packed encoding.
479 //
480 // Precondition: Encoder has no active child encoder.
481 Status WriteRepeatedFixed64(uint32_t field_number,
482 const pw::Vector<uint64_t>& values) {
483 return WritePackedFixed(field_number,
484 as_bytes(span(values.data(), values.size())),
485 sizeof(uint64_t));
486 }
487
488 // Writes a proto sfixed32 key-value pair.
489 //
490 // Precondition: Encoder has no active child encoder.
491 Status WriteSfixed32(uint32_t field_number, int32_t value) {
492 return WriteFixed32(field_number, static_cast<uint32_t>(value));
493 }
494
495 // Writes a repeated sfixed32 field using packed encoding.
496 //
497 // Precondition: Encoder has no active child encoder.
498 Status WritePackedSfixed32(uint32_t field_number,
499 span<const int32_t> values) {
500 return WritePackedFixed(field_number, as_bytes(values), sizeof(int32_t));
501 }
502
503 // Writes a repeated fixed32 field using packed encoding.
504 //
505 // Precondition: Encoder has no active child encoder.
506 Status WriteRepeatedSfixed32(uint32_t field_number,
507 const pw::Vector<int32_t>& values) {
508 return WritePackedFixed(field_number,
509 as_bytes(span(values.data(), values.size())),
510 sizeof(int32_t));
511 }
512
513 // Writes a proto sfixed64 key-value pair.
514 //
515 // Precondition: Encoder has no active child encoder.
516 Status WriteSfixed64(uint32_t field_number, int64_t value) {
517 return WriteFixed64(field_number, static_cast<uint64_t>(value));
518 }
519
520 // Writes a repeated sfixed64 field using packed encoding.
521 //
522 // Precondition: Encoder has no active child encoder.
523 Status WritePackedSfixed64(uint32_t field_number,
524 span<const int64_t> values) {
525 return WritePackedFixed(field_number, as_bytes(values), sizeof(int64_t));
526 }
527
528 // Writes a repeated fixed64 field using packed encoding.
529 //
530 // Precondition: Encoder has no active child encoder.
531 Status WriteRepeatedSfixed64(uint32_t field_number,
532 const pw::Vector<int64_t>& values) {
533 return WritePackedFixed(field_number,
534 as_bytes(span(values.data(), values.size())),
535 sizeof(int64_t));
536 }
537
538 // Writes a proto float key-value pair.
539 //
540 // Precondition: Encoder has no active child encoder.
541 Status WriteFloat(uint32_t field_number, float value) {
542 static_assert(sizeof(float) == sizeof(uint32_t),
543 "Float and uint32_t are not the same size");
544 uint32_t integral_value;
545 std::memcpy(&integral_value, &value, sizeof(value));
546 std::array<std::byte, sizeof(value)> data =
547 bytes::CopyInOrder(endian::little, integral_value);
548 return WriteFixed(field_number, data);
549 }
550
551 // Writes a repeated float field using packed encoding.
552 //
553 // Precondition: Encoder has no active child encoder.
554 Status WritePackedFloat(uint32_t field_number, span<const float> values) {
555 return WritePackedFixed(field_number, as_bytes(values), sizeof(float));
556 }
557
558 // Writes a repeated float field using packed encoding.
559 //
560 // Precondition: Encoder has no active child encoder.
561 Status WriteRepeatedFloat(uint32_t field_number,
562 const pw::Vector<float>& values) {
563 return WritePackedFixed(field_number,
564 as_bytes(span(values.data(), values.size())),
565 sizeof(float));
566 }
567
568 // Writes a proto double key-value pair.
569 //
570 // Precondition: Encoder has no active child encoder.
571 Status WriteDouble(uint32_t field_number, double value) {
572 static_assert(sizeof(double) == sizeof(uint64_t),
573 "Double and uint64_t are not the same size");
574 uint64_t integral_value;
575 std::memcpy(&integral_value, &value, sizeof(value));
576 std::array<std::byte, sizeof(value)> data =
577 bytes::CopyInOrder(endian::little, integral_value);
578 return WriteFixed(field_number, data);
579 }
580
581 // Writes a repeated double field using packed encoding.
582 //
583 // Precondition: Encoder has no active child encoder.
584 Status WritePackedDouble(uint32_t field_number, span<const double> values) {
585 return WritePackedFixed(field_number, as_bytes(values), sizeof(double));
586 }
587
588 // Writes a repeated double field using packed encoding.
589 //
590 // Precondition: Encoder has no active child encoder.
591 Status WriteRepeatedDouble(uint32_t field_number,
592 const pw::Vector<double>& values) {
593 return WritePackedFixed(field_number,
594 as_bytes(span(values.data(), values.size())),
595 sizeof(double));
596 }
597
598 // Writes a proto `bytes` field as a key-value pair. This can also be used to
599 // write a pre-encoded nested submessage directly without using a nested
600 // encoder.
601 //
602 // Precondition: Encoder has no active child encoder.
603 Status WriteBytes(uint32_t field_number, ConstByteSpan value) {
604 return WriteLengthDelimitedField(field_number, value);
605 }
606
612 Status WriteBytes(uint32_t field_number,
613 size_t num_bytes,
614 const Function<Status(stream::Writer&)>& write_func) {
615 return WriteLengthDelimitedFieldFromCallback(
616 field_number, num_bytes, write_func);
617 }
618
619 // Writes a proto 'bytes' field from the stream bytes_reader.
620 //
621 // The payload for the value is provided through the stream::Reader
622 // `bytes_reader`. The method reads a chunk of the data from the reader using
623 // the `stream_pipe_buffer` and writes it to the encoder.
624 //
625 // Precondition: The stream_pipe_buffer.byte_size() >= 1
626 // Precondition: Encoder has no active child encoder.
627 //
628 // Returns:
629 // OK - Bytes field is written successfully.
630 // RESOURCE_EXHAUSTED - Exceeds write limits.
631 // OUT_OF_RANGE - `bytes_reader` is exhausted before `num_bytes` of
632 // bytes is read.
633 //
634 // Other errors encountered by the writer will be returned as it is.
635 Status WriteBytesFromStream(uint32_t field_number,
636 stream::Reader& bytes_reader,
637 size_t num_bytes,
638 ByteSpan stream_pipe_buffer) {
639 return WriteLengthDelimitedFieldFromStream(
640 field_number, bytes_reader, num_bytes, stream_pipe_buffer);
641 }
642
643 // Writes a proto string key-value pair.
644 //
645 // Precondition: Encoder has no active child encoder.
646 Status WriteString(uint32_t field_number, std::string_view value) {
647 return WriteBytes(field_number, as_bytes(span<const char>(value)));
648 }
649
650 // Writes a proto string key-value pair.
651 //
652 // Precondition: Encoder has no active child encoder.
653 Status WriteString(uint32_t field_number, const char* value, size_t len) {
654 return WriteBytes(field_number, as_bytes(span(value, len)));
655 }
656
657 // Writes a proto 'string' field from the stream bytes_reader.
658 //
659 // The payload for the value is provided through the stream::Reader
660 // `bytes_reader`. The method reads a chunk of the data from the reader using
661 // the `stream_pipe_buffer` and writes it to the encoder.
662 //
663 // Precondition: The stream_pipe_buffer.byte_size() >= 1
664 // Precondition: Encoder has no active child encoder.
665 //
666 // Returns:
667 // OK - String field is written successfully.
668 // RESOURCE_EXHAUSTED - Exceeds write limits.
669 // OUT_OF_RANGE - `bytes_reader` is exhausted before `num_bytes` of
670 // bytes is read.
671 //
672 // Other errors encountered by the writer will be returned as it is.
673 Status WriteStringFromStream(uint32_t field_number,
674 stream::Reader& bytes_reader,
675 size_t num_bytes,
676 ByteSpan stream_pipe_buffer) {
677 return WriteBytesFromStream(
678 field_number, bytes_reader, num_bytes, stream_pipe_buffer);
679 }
680
681 protected:
682 // Specialized move constructor used only for codegen.
683 //
684 // Postcondition: The other encoder is invalidated and cannot be used as it
685 // acts like a parent encoder with an active child encoder.
686 constexpr StreamEncoder(StreamEncoder&& other)
687 : status_(other.status_),
688 write_when_empty_(true),
689 parent_(other.parent_),
690 nested_field_number_(other.nested_field_number_),
691 memory_writer_(std::move(other.memory_writer_)),
692 writer_(&other.writer_ == &other.memory_writer_ ? memory_writer_
693 : other.writer_) {
694 PW_ASSERT(nested_field_number_ == 0);
695 // Make the nested encoder look like it has an open child to block writes
696 // for the remainder of the object's life.
697 other.nested_field_number_ = kFirstReservedNumber;
698 other.parent_ = nullptr;
699 }
700
701 // Writes proto values to the stream from the structure contained within
702 // message, according to the description of fields in table.
703 //
704 // This is called by codegen subclass Write() functions that accept a typed
705 // struct Message reference, using the appropriate codegen MessageField table
706 // corresponding to that type.
707 Status Write(span<const std::byte> message,
708 span<const internal::MessageField> table);
709
710 // Protected method to create a nested encoder, specifying whether the field
711 // should be written when no fields were added to the nested encoder. Exposed
712 // using an enum in the public API, for better readability.
713 StreamEncoder GetNestedEncoder(uint32_t field_number, bool write_when_empty);
714
715 private:
716 friend class MemoryEncoder;
717
718 constexpr StreamEncoder(StreamEncoder& parent,
719 ByteSpan scratch_buffer,
720 bool write_when_empty = true)
721 : status_(OkStatus()),
722 write_when_empty_(write_when_empty),
723 parent_(&parent),
724 nested_field_number_(0),
725 memory_writer_(scratch_buffer),
726 writer_(memory_writer_) {
727 // If this encoder was spawned from a failed encoder, it should also start
728 // in a failed state.
729 if (&parent != this) {
730 status_.Update(parent.status_);
731 }
732 if (scratch_buffer.empty()) {
734 }
735 }
736
737 bool nested_encoder_open() const { return nested_field_number_ != 0; }
738
739 // CloseNestedMessage() is called on the parent encoder as part of the nested
740 // encoder destructor.
741 void CloseNestedMessage(StreamEncoder& nested);
742
743 ByteSpan GetNestedScratchBuffer(uint32_t field_number);
744
745 // Implementation for encoding all varint field types.
746 Status WriteVarintField(uint32_t field_number, uint64_t value);
747
748 // Implementation for encoding all length-delimited field types.
749 Status WriteLengthDelimitedField(uint32_t field_number, ConstByteSpan data);
750
751 Status WriteLengthDelimitedFieldFromCallback(
752 uint32_t field_number,
753 size_t num_bytes,
754 const Function<Status(stream::Writer&)>& write_func);
755
756 // Encoding of length-delimited field where payload comes from `bytes_reader`.
757 Status WriteLengthDelimitedFieldFromStream(uint32_t field_number,
758 stream::Reader& bytes_reader,
759 size_t num_bytes,
760 ByteSpan stream_pipe_buffer);
761
762 // Implementation for encoding all fixed-length integer types.
763 Status WriteFixed(uint32_t field_number, ConstByteSpan data);
764
765 // Encodes a base-128 varint to the buffer. This function assumes the caller
766 // has already checked UpdateStatusForWrite() to ensure the writer's
767 // conservative write limit indicates the Writer has sufficient buffer space.
768 Status WriteVarint(uint64_t value) {
769 PW_TRY(status_);
770 status_.Update(::pw::protobuf::WriteVarint(value, writer_));
771 return status_;
772 }
773
774 Status WriteZigzagVarint(int64_t value) {
775 return WriteVarint(varint::ZigZagEncode(value));
776 }
777
778 // Writes a list of varints to the buffer in length-delimited packed encoding.
779 template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
780 Status WritePackedVarints(uint32_t field_number,
781 span<T> values,
782 internal::VarintType encode_type) {
783 static_assert(std::is_same<T, const uint8_t>::value ||
784 std::is_same<T, const uint32_t>::value ||
785 std::is_same<T, const int32_t>::value ||
786 std::is_same<T, const uint64_t>::value ||
787 std::is_same<T, const int64_t>::value,
788 "Packed varints must be of type bool, uint32_t, int32_t, "
789 "uint64_t, or int64_t");
790
791 size_t payload_size = 0;
792 for (T val : values) {
793 if (encode_type == internal::VarintType::kZigZag) {
794 int64_t integer =
795 static_cast<int64_t>(static_cast<std::make_signed_t<T>>(val));
796 payload_size += varint::EncodedSize(varint::ZigZagEncode(integer));
797 } else {
798 uint64_t integer = static_cast<uint64_t>(val);
799 payload_size += varint::EncodedSize(integer);
800 }
801 }
802
803 if (!UpdateStatusForWrite(field_number, WireType::kDelimited, payload_size)
804 .ok()) {
805 return status_;
806 }
807
808 WriteVarint(FieldKey(field_number, WireType::kDelimited))
809 .IgnoreError(); // TODO: b/242598609 - Handle Status properly
810 WriteVarint(payload_size)
811 .IgnoreError(); // TODO: b/242598609 - Handle Status properly
812 for (T value : values) {
813 if (encode_type == internal::VarintType::kZigZag) {
814 WriteZigzagVarint(static_cast<std::make_signed_t<T>>(value))
815 .IgnoreError(); // TODO: b/242598609 - Handle Status properly
816 } else {
817 WriteVarint(value)
818 .IgnoreError(); // TODO: b/242598609 - Handle Status properly
819 }
820 }
821
822 return status_;
823 }
824
825 // Writes a list of fixed-size types to the buffer in length-delimited
826 // packed encoding. Only float, double, uint32_t, int32_t, uint64_t, and
827 // int64_t are permitted
828 Status WritePackedFixed(uint32_t field_number,
829 span<const std::byte> values,
830 size_t elem_size);
831
832 template <typename Container>
833 Status WriteStringOrBytes(uint32_t field_number,
834 const std::byte* raw_container) {
835 const auto& container = *reinterpret_cast<Container*>(raw_container);
836 if (container.empty()) {
837 return OkStatus();
838 }
839 return WriteLengthDelimitedField(field_number, as_bytes(span(container)));
840 }
841
842 class AnyMessageWriter;
843
844 // Non-templated method which handles WriteNestedMessage calls.
845 // TODO: b/432525176 - Drop this indirection when we have pw::FunctionRef.
846 Status DoWriteNestedMessage(uint32_t field_number,
847 AnyMessageWriter const& write_message,
848 bool write_when_empty);
849
850 // Checks if a write is invalid or will cause the encoder to enter an error
851 // state, and preemptively sets this encoder's status to that error to block
852 // the write. Only the first error encountered is tracked.
853 //
854 // Precondition: Encoder has no active child encoder.
855 //
856 // Returns:
857 // InvalidArgument: The field number provided was invalid.
858 // ResourceExhausted: The requested write would have exceeded the
859 // stream::Writer's conservative write limit.
860 // Other: If any Write() operations on the stream::Writer caused an error,
861 // that error will be repeated here.
862 Status UpdateStatusForWrite(uint32_t field_number,
863 WireType type,
864 size_t data_size);
865
866 // Callbacks for oneof fields set a flag to ensure they are only invoked once.
867 // To maintain logical constness of message structs passed to write, this
868 // resets each callback's invoked flag following a write operation.
869 void ResetOneOfCallbacks(ConstByteSpan message,
870 span<const internal::MessageField> table);
871
872 // The current encoder status. This status is only updated to reflect the
873 // first error encountered. Any further write operations are blocked when the
874 // encoder enters an error state.
875 Status status_;
876
877 // Checked by the parent when the nested encoder is closed, and if no bytes
878 // were written, the field is not written.
879 bool write_when_empty_;
880
881 // If this is a nested encoder, this points to the encoder that created it.
882 // For user-created MemoryEncoders, parent_ points to this object as an
883 // optimization for the MemoryEncoder and nested encoders to use the same
884 // underlying buffer.
885 StreamEncoder* parent_;
886
887 // If an encoder has a child encoder open, this is the field number of that
888 // submessage. Otherwise, this is 0 to indicate no child encoder is open.
889 uint32_t nested_field_number_;
890
891 // This memory writer is used for staging proto submessages to the
892 // scratch_buffer.
893 stream::MemoryWriter memory_writer_;
894
895 // All proto encode operations are directly written to this writer.
896 stream::Writer& writer_;
897};
898
899// AnyMessageWriter is essentially a non-owning delegate. It exists to allow
900// any callable to be passed to WriteNestedMessage without being constrained
901// by the capture limitations of pw::Function. It works by type-erasing the
902// write_message callable, hiding it behind a void*.
903// TODO: b/432525176 - Drop this indirection when we have pw::FunctionRef.
905 public:
906 template <typename WriteFunc>
907 AnyMessageWriter(WriteFunc* write_message)
908 : trampoline_(&Trampoline<WriteFunc>), target_(write_message) {}
909
910 Status operator()(StreamEncoder& encoder) const {
911 return trampoline_(target_, encoder);
912 }
913
914 private:
915 using TrampolineSignature = Status(void* context, StreamEncoder& encoder);
916 TrampolineSignature* trampoline_;
917 void* target_;
918
919 template <typename WriteFunc>
920 static Status Trampoline(void* erased_func, StreamEncoder& encoder) {
921 return std::invoke(*static_cast<WriteFunc*>(erased_func), encoder);
922 }
923};
924
925template <typename WriteFunc>
926Status StreamEncoder::WriteNestedMessage(
927 uint32_t field_number,
928 WriteFunc write_message,
929 EmptyEncoderBehavior empty_encoder_behavior) {
930 static_assert(std::is_invocable_r_v<Status, WriteFunc, StreamEncoder&>,
931 "Callable parameter must have signature compatible with "
932 "Status(StreamEncoder&)");
933 return DoWriteNestedMessage(field_number,
934 AnyMessageWriter(&write_message),
935 /*write_when_empty=*/empty_encoder_behavior ==
936 EmptyEncoderBehavior::kWriteFieldNumber);
937}
938
939// A protobuf encoder that writes directly to a provided buffer.
940//
941// Example:
942//
943// // Writes a proto response to the provided buffer, returning the encode
944// // status and number of bytes written.
945// StatusWithSize WriteProtoResponse(ByteSpan response) {
946// // All proto writes are directly written to the `response` buffer.
947// MemoryEncoder encoder(response);
948// encoder.WriteUint32(kMagicNumberField, 0x1a1a2b2b);
949// encoder.WriteString(kFavoriteFood, "cookies");
950// return encoder.status_with_size();
951// }
952//
953// Note: Avoid using a MemoryEncoder reference as an argument for a function.
954// The StreamEncoder is more generic.
956 public:
957 constexpr MemoryEncoder(ByteSpan dest) : StreamEncoder(*this, dest) {}
958
959 // Precondition: Encoder has no active child encoder.
960 //
961 // Postcondition: If this encoder is a nested one, the parent encoder is
962 // unlocked and proto encoding may resume on the parent.
963 ~MemoryEncoder() = default;
964
965 // Disallow copy/assign to avoid confusion about who owns the buffer.
966 MemoryEncoder(const MemoryEncoder& other) = delete;
967 MemoryEncoder& operator=(const MemoryEncoder& other) = delete;
968
969 // It's not safe to move an encoder as it could cause another encoder's
970 // parent_ pointer to become invalid.
971 MemoryEncoder& operator=(MemoryEncoder&& other) = delete;
972
973 const std::byte* data() const { return memory_writer_.data(); }
974 size_t size() const { return memory_writer_.bytes_written(); }
975
976 const std::byte* begin() const { return data(); }
977 const std::byte* end() const { return data() + size(); }
978
979 StatusWithSize status_with_size() const {
980 return StatusWithSize(status(), size());
981 }
982
983 protected:
984 // This is needed by codegen.
985 MemoryEncoder(MemoryEncoder&& other) = default;
986};
987
988// pw_protobuf guarantees that all generated StreamEncoder classes can be
989// converted among each other. It's also safe to convert any MemoryEncoder to
990// any other StreamEncoder.
991//
992// This guarantee exists to facilitate usage of protobuf overlays. Protobuf
993// overlays are protobuf message definitions that deliberately ensure that
994// fields defined in one message will not conflict with fields defined in other
995// messages.
996//
997// Example:
998//
999// // The first half of the overlaid message.
1000// message BaseMessage {
1001// uint32 length = 1;
1002// reserved 2; // Reserved for Overlay
1003// }
1004//
1005// // OK: The second half of the overlaid message.
1006// message Overlay {
1007// reserved 1; // Reserved for BaseMessage
1008// uint32 height = 2;
1009// }
1010//
1011// // OK: A message that overlays and bundles both types together.
1012// message Both {
1013// uint32 length = 1; // Defined independently by BaseMessage
1014// uint32 height = 2; // Defined independently by Overlay
1015// }
1016//
1017// // BAD: Diverges from BaseMessage's definition, and can cause decode
1018// // errors/corruption.
1019// message InvalidOverlay {
1020// fixed32 length = 1;
1021// }
1022//
1023// While this use case is somewhat uncommon, it's a core supported use case of
1024// pw_protobuf.
1025//
1026// Warning: Using this to convert one stream encoder to another when the
1027// messages themselves do not safely overlay will result in corrupt protos.
1028// Be careful when doing this as there's no compile-time way to detect whether
1029// or not two messages are meant to overlay.
1030template <typename ToStreamEncoder, typename FromStreamEncoder>
1031inline ToStreamEncoder& StreamEncoderCast(FromStreamEncoder& encoder) {
1032 static_assert(std::is_base_of<StreamEncoder, FromStreamEncoder>::value,
1033 "Provided argument is not a derived class of "
1034 "pw::protobuf::StreamEncoder");
1035 static_assert(std::is_base_of<StreamEncoder, ToStreamEncoder>::value,
1036 "Cannot cast to a type that is not a derived class of "
1037 "pw::protobuf::StreamEncoder");
1038 return pw::internal::SiblingCast<ToStreamEncoder&, StreamEncoder>(encoder);
1039}
1040
1042
1043} // namespace pw::protobuf
Definition: status.h:120
constexpr void Update(Status other)
Definition: status.h:419
constexpr void IgnoreError() const
Definition: status.h:430
static constexpr Status ResourceExhausted()
Definition: status.h:230
Definition: status_with_size.h:51
Definition: vector.h:66
Definition: encoder.h:955
Definition: encoder.h:109
Status WriteBytes(uint32_t field_number, size_t num_bytes, const Function< Status(stream::Writer &)> &write_func)
Definition: encoder.h:612
Definition: stream.h:326
size_t ConservativeWriteLimit() const
Definition: stream.h:229
Definition: stream.h:415
constexpr void CopyInOrder(endian order, T value, U *dest)
Definition: endian.h:145
fit::function_impl< function_internal::config::kInlineCallableSize, !function_internal::config::kEnableDynamicAllocation, FunctionType, PW_FUNCTION_DEFAULT_ALLOCATOR_TYPE > Function
Definition: function.h:73
#define PW_TRY(expr)
Returns early if expr is a non-OK Status or Result.
Definition: try.h:27
constexpr Status OkStatus()
Definition: status.h:450
constexpr std::make_unsigned_t< T > ZigZagEncode(T value)
Definition: varint.h:365
constexpr size_t EncodeLittleEndianBase128(uint64_t value, ByteSpan out_encoded)
Definition: varint.h:372
constexpr size_t EncodedSize(T value)
Computes the size of an integer when encoded as a varint.
Definition: varint.h:123