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