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