pw_protobuf: Add helper for map entry write

Expose low level APIs in pw_protobuf for estimating field size and
writing length-delimited field key and length prefix. Use them to
implement a helper function in pw_software_update for writing proto
map<string, bytes> entries.

Context: UpdateBundle needs to construct the following Manifest
proto message from blob storage:

message Manifest {
  optional SnapshotMetadata snapshot_metadata = 1;
  map<string, TargetsMetadata> targets_metadata = 2;
}

For wire format generation, it is essentially equivalent to the
following definition with a nested `Entry` message.

message Entry {
    string key = 1;
    bytes value = 2;
}

message Manifest {
    optional bytes snapshot_metadata = 1;
    repeated Entry targets_metadata = 2;
}

Although protobuf::StreamEncoder has capability for nested message
encoding, it requires a scratch buffer that shall be at least the
largest sub-message size. In this case, it will be largest
target metadata in the update software bundle, which however, can be
fairly large and difficult to estimate. To avoid the issue, the CL
takes an approach to construct the message from lower level.
Specifically, the CL constructs the delimited field key and length
prefix for `Entry` on its own and write to output, then followed by
writing a regular string field of `key` and bytes field of `value` via
the normal StreamEncoder approach.

Change-Id: Ie5f10f483ebceb587660a4a36e5c6674a09ce096
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/59681
Reviewed-by: Armando Montanez <amontanez@google.com>
Commit-Queue: Yecheng Zhao <zyecheng@google.com>
diff --git a/pw_protobuf/encoder.cc b/pw_protobuf/encoder.cc
index 93cc777..156469a 100644
--- a/pw_protobuf/encoder.cc
+++ b/pw_protobuf/encoder.cc
@@ -20,6 +20,7 @@
 
 #include "pw_assert/check.h"
 #include "pw_bytes/span.h"
+#include "pw_protobuf/serialized_size.h"
 #include "pw_protobuf/wire_format.h"
 #include "pw_status/status.h"
 #include "pw_status/try.h"
@@ -119,10 +120,9 @@
 Status StreamEncoder::WriteLengthDelimitedField(uint32_t field_number,
                                                 ConstByteSpan data) {
   PW_TRY(UpdateStatusForWrite(field_number, WireType::kDelimited, data.size()));
-  WriteVarint(FieldKey(field_number, WireType::kDelimited))
-      .IgnoreError();  // TODO(pwbug/387): Handle Status properly
-  WriteVarint(data.size_bytes())
-      .IgnoreError();  // TODO(pwbug/387): Handle Status properly
+  status_.Update(WriteLengthDelimitedKeyAndLengthPrefix(
+      field_number, data.size(), writer_));
+  PW_TRY(status_);
   if (Status status = writer_.Write(data); !status.ok()) {
     status_ = status;
   }
@@ -137,10 +137,8 @@
   PW_CHECK_UINT_GT(
       stream_pipe_buffer.size(), 0, "Transfer buffer cannot be 0 size");
   PW_TRY(UpdateStatusForWrite(field_number, WireType::kDelimited, num_bytes));
-  // Ignore the error until we explicitly check status_ below to minimize
-  // the number of branches.
-  WriteVarint(FieldKey(field_number, WireType::kDelimited)).IgnoreError();
-  WriteVarint(num_bytes).IgnoreError();
+  status_.Update(
+      WriteLengthDelimitedKeyAndLengthPrefix(field_number, num_bytes, writer_));
   PW_TRY(status_);
 
   // Stream data from `bytes_reader` to `writer_`.
@@ -177,28 +175,6 @@
   return status_;
 }
 
-// Encodes a base-128 varint to the buffer. This function assumes the caller
-// has already checked UpdateStatusForWrite() to ensure the writer's
-// conservative write limit indicates the Writer has sufficient buffer space.
-Status StreamEncoder::WriteVarint(uint64_t value) {
-  if (!status_.ok()) {
-    return status_;
-  }
-
-  std::array<std::byte, varint::kMaxVarint64SizeBytes> varint_encode_buffer;
-  size_t varint_size = pw::varint::EncodeLittleEndianBase128(
-      value, std::span(varint_encode_buffer));
-
-  if (Status status =
-          writer_.Write(std::span(varint_encode_buffer).first(varint_size));
-      !status.ok()) {
-    status_ = status;
-    return status_;
-  }
-
-  return OkStatus();
-}
-
 Status StreamEncoder::WritePackedFixed(uint32_t field_number,
                                        std::span<const std::byte> values,
                                        size_t elem_size) {
@@ -236,23 +212,20 @@
                                            WireType type,
                                            size_t data_size) {
   PW_CHECK(!nested_encoder_open());
-  if (!status_.ok()) {
-    return status_;
-  }
+  PW_TRY(status_);
+
   if (!ValidFieldNumber(field_number)) {
-    status_ = Status::InvalidArgument();
-    return status_;
+    return status_ = Status::InvalidArgument();
   }
 
-  size_t size = varint::EncodedSize(FieldKey(field_number, type));
-  if (type == WireType::kDelimited) {
-    size += varint::EncodedSize(data_size);
-  }
-  size += data_size;
+  const Result<size_t> field_size = SizeOfField(field_number, type, data_size);
+  status_.Update(field_size.status());
+  PW_TRY(status_);
 
-  if (size > writer_.ConservativeWriteLimit()) {
+  if (field_size.value() > writer_.ConservativeWriteLimit()) {
     status_ = Status::ResourceExhausted();
   }
+
   return status_;
 }