protozero: Reduce binary size of protozero gen

This reduces the size of the generated ParseFromArray and Serialize
methods of generated protobuf message (the gen messages, not the
protozero message).

Before:
  stripped/example_system_wide size: 708304
After:
  stripped/example_system_wide size: 664816

Change-Id: I1583057af5d96094f776ab05d609144c89eaa36c
diff --git a/Android.bp b/Android.bp
index eadd256..946816b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8849,6 +8849,7 @@
     name: "perfetto_src_protozero_protozero",
     srcs: [
         "src/protozero/field.cc",
+        "src/protozero/gen_field_helpers.cc",
         "src/protozero/message.cc",
         "src/protozero/message_arena.cc",
         "src/protozero/message_handle.cc",
diff --git a/BUILD b/BUILD
index c4c9781..05a8baf 100644
--- a/BUILD
+++ b/BUILD
@@ -126,6 +126,7 @@
     name = "protozero",
     srcs = [
         "src/protozero/field.cc",
+        "src/protozero/gen_field_helpers.cc",
         "src/protozero/message.cc",
         "src/protozero/message_arena.cc",
         "src/protozero/message_handle.cc",
@@ -554,6 +555,7 @@
         "include/perfetto/protozero/cpp_message_obj.h",
         "include/perfetto/protozero/field.h",
         "include/perfetto/protozero/field_writer.h",
+        "include/perfetto/protozero/gen_field_helpers.h",
         "include/perfetto/protozero/message.h",
         "include/perfetto/protozero/message_arena.h",
         "include/perfetto/protozero/message_handle.h",
diff --git a/include/perfetto/protozero/BUILD.gn b/include/perfetto/protozero/BUILD.gn
index c99f7fd..4c6c08f 100644
--- a/include/perfetto/protozero/BUILD.gn
+++ b/include/perfetto/protozero/BUILD.gn
@@ -20,6 +20,7 @@
     "cpp_message_obj.h",
     "field.h",
     "field_writer.h",
+    "gen_field_helpers.h",
     "message.h",
     "message_arena.h",
     "message_handle.h",
diff --git a/include/perfetto/protozero/gen_field_helpers.h b/include/perfetto/protozero/gen_field_helpers.h
new file mode 100644
index 0000000..ad3dee8
--- /dev/null
+++ b/include/perfetto/protozero/gen_field_helpers.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PROTOZERO_GEN_FIELD_HELPERS_H_
+#define INCLUDE_PERFETTO_PROTOZERO_GEN_FIELD_HELPERS_H_
+
+#include "perfetto/protozero/message.h"
+#include "perfetto/protozero/proto_decoder.h"
+#include "perfetto/protozero/proto_utils.h"
+#include "perfetto/protozero/scattered_heap_buffer.h"
+
+namespace protozero {
+namespace internal {
+namespace gen_helpers {
+
+// This file implements some helpers used by the protobuf generated code in the
+// .gen.cc files.
+//
+// The .gen.cc generated protobuf implementation (as opposed to the .pbzero.h
+// implementation) is not zero-copy and is not supposed to be used in fast
+// paths, so most of these helpers are designed to reduce binary size.
+
+void DeserializeString(const protozero::Field& field, std::string* dst);
+
+// Read packed repeated elements (serialized as `wire_type`) from `field` into
+// the `*dst` vector. Returns false if some bytes of `field` could not be
+// interpreted correctly as `wire_type`.
+template <proto_utils::ProtoWireType wire_type, typename CppType>
+bool DeserializePackedRepeated(const protozero::Field& field,
+                               std::vector<CppType>* dst) {
+  bool parse_error = false;
+  for (::protozero::PackedRepeatedFieldIterator<wire_type, CppType> rep(
+           field.data(), field.size(), &parse_error);
+       rep; ++rep) {
+    dst->emplace_back(*rep);
+  }
+  return !parse_error;
+}
+
+extern template bool
+DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt, uint64_t>(
+    const protozero::Field& field,
+    std::vector<uint64_t>* dst);
+
+extern template bool
+DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt, int64_t>(
+    const protozero::Field& field,
+    std::vector<int64_t>* dst);
+
+extern template bool
+DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt, uint32_t>(
+    const protozero::Field& field,
+    std::vector<uint32_t>* dst);
+
+extern template bool
+DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt, int32_t>(
+    const protozero::Field& field,
+    std::vector<int32_t>* dst);
+
+// Serializers for different type of fields
+
+void SerializeTinyVarInt(uint32_t field_id, bool value, Message* msg);
+
+template <typename T>
+void SerializeExtendedVarInt(uint32_t field_id, T value, Message* msg) {
+  msg->AppendVarInt(field_id, value);
+}
+
+extern template void SerializeExtendedVarInt<uint64_t>(uint32_t field_id,
+                                                       uint64_t value,
+                                                       Message* msg);
+
+extern template void SerializeExtendedVarInt<uint32_t>(uint32_t field_id,
+                                                       uint32_t value,
+                                                       Message* msg);
+
+template <typename T>
+void SerializeVarInt(uint32_t field_id, T value, Message* msg) {
+  SerializeExtendedVarInt(
+      field_id, proto_utils::ExtendValueForVarIntSerialization(value), msg);
+}
+
+template <typename T>
+void SerializeSignedVarInt(uint32_t field_id, T value, Message* msg) {
+  SerializeVarInt(field_id, proto_utils::ZigZagEncode(value), msg);
+}
+
+template <typename T>
+void SerializeFixed(uint32_t field_id, T value, Message* msg) {
+  msg->AppendFixed(field_id, value);
+}
+
+extern template void SerializeFixed<double>(uint32_t field_id,
+                                            double value,
+                                            Message* msg);
+
+extern template void SerializeFixed<float>(uint32_t field_id,
+                                           float value,
+                                           Message* msg);
+
+extern template void SerializeFixed<uint64_t>(uint32_t field_id,
+                                              uint64_t value,
+                                              Message* msg);
+
+extern template void SerializeFixed<int64_t>(uint32_t field_id,
+                                             int64_t value,
+                                             Message* msg);
+
+extern template void SerializeFixed<uint32_t>(uint32_t field_id,
+                                              uint32_t value,
+                                              Message* msg);
+
+extern template void SerializeFixed<int32_t>(uint32_t field_id,
+                                             int32_t value,
+                                             Message* msg);
+
+void SerializeString(uint32_t field_id, const std::string& value, Message* msg);
+
+void SerializeUnknownFields(const std::string& unknown_fields, Message* msg);
+
+// Wrapper around HeapBuffered that avoids inlining.
+class MessageSerializer {
+ public:
+  MessageSerializer();
+  ~MessageSerializer();
+
+  Message* get() { return msg_.get(); }
+  std::vector<uint8_t> SerializeAsArray();
+  std::string SerializeAsString();
+
+ private:
+  HeapBuffered<Message> msg_;
+};
+
+}  // namespace gen_helpers
+}  // namespace internal
+}  // namespace protozero
+
+#endif  // INCLUDE_PERFETTO_PROTOZERO_GEN_FIELD_HELPERS_H_
diff --git a/include/perfetto/protozero/proto_utils.h b/include/perfetto/protozero/proto_utils.h
index eafd6ce..2896b3d 100644
--- a/include/perfetto/protozero/proto_utils.h
+++ b/include/perfetto/protozero/proto_utils.h
@@ -178,7 +178,9 @@
 }
 
 template <typename T>
-inline uint8_t* WriteVarInt(T value, uint8_t* target) {
+auto ExtendValueForVarIntSerialization(T value) -> typename std::make_unsigned<
+    typename std::conditional<std::is_unsigned<T>::value, T, int64_t>::type>::
+    type {
   // If value is <= 0 we must first sign extend to int64_t (see [1]).
   // Finally we always cast to an unsigned value to to avoid arithmetic
   // (sign expanding) shifts in the while loop.
@@ -198,6 +200,13 @@
   MaybeExtendedType extended_value = static_cast<MaybeExtendedType>(value);
   UnsignedType unsigned_value = static_cast<UnsignedType>(extended_value);
 
+  return unsigned_value;
+}
+
+template <typename T>
+inline uint8_t* WriteVarInt(T value, uint8_t* target) {
+  auto unsigned_value = ExtendValueForVarIntSerialization(value);
+
   while (unsigned_value >= 0x80) {
     *target++ = static_cast<uint8_t>(unsigned_value) | 0x80;
     unsigned_value >>= 7;
diff --git a/src/protozero/BUILD.gn b/src/protozero/BUILD.gn
index dcb85d3..d553aac 100644
--- a/src/protozero/BUILD.gn
+++ b/src/protozero/BUILD.gn
@@ -31,6 +31,7 @@
   ]
   sources = [
     "field.cc",
+    "gen_field_helpers.cc",
     "message.cc",
     "message_arena.cc",
     "message_handle.cc",
diff --git a/src/protozero/gen_field_helpers.cc b/src/protozero/gen_field_helpers.cc
new file mode 100644
index 0000000..85a41f7
--- /dev/null
+++ b/src/protozero/gen_field_helpers.cc
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/protozero/gen_field_helpers.h"
+
+namespace protozero {
+namespace internal {
+namespace gen_helpers {
+
+void DeserializeString(const protozero::Field& field, std::string* dst) {
+  field.get(dst);
+}
+
+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+                                        uint64_t>(const protozero::Field& field,
+                                                  std::vector<uint64_t>* dst);
+
+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+                                        int64_t>(const protozero::Field& field,
+                                                 std::vector<int64_t>* dst);
+
+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+                                        uint32_t>(const protozero::Field& field,
+                                                  std::vector<uint32_t>* dst);
+
+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+                                        int32_t>(const protozero::Field& field,
+                                                 std::vector<int32_t>* dst);
+
+void SerializeTinyVarInt(uint32_t field_id, bool value, Message* msg) {
+  msg->AppendTinyVarInt(field_id, value);
+}
+
+template void SerializeExtendedVarInt<uint64_t>(uint32_t field_id,
+                                                uint64_t value,
+                                                Message* msg);
+
+template void SerializeExtendedVarInt<uint32_t>(uint32_t field_id,
+                                                uint32_t value,
+                                                Message* msg);
+
+template void SerializeFixed<double>(uint32_t field_id,
+                                     double value,
+                                     Message* msg);
+
+template void SerializeFixed<float>(uint32_t field_id,
+                                    float value,
+                                    Message* msg);
+
+template void SerializeFixed<uint64_t>(uint32_t field_id,
+                                       uint64_t value,
+                                       Message* msg);
+
+template void SerializeFixed<int64_t>(uint32_t field_id,
+                                      int64_t value,
+                                      Message* msg);
+
+template void SerializeFixed<uint32_t>(uint32_t field_id,
+                                       uint32_t value,
+                                       Message* msg);
+
+template void SerializeFixed<int32_t>(uint32_t field_id,
+                                      int32_t value,
+                                      Message* msg);
+
+void SerializeString(uint32_t field_id,
+                     const std::string& value,
+                     Message* msg) {
+  msg->AppendString(field_id, value);
+}
+
+void SerializeUnknownFields(const std::string& unknown_fields, Message* msg) {
+  msg->AppendRawProtoBytes(unknown_fields.data(), unknown_fields.size());
+}
+
+MessageSerializer::MessageSerializer() = default;
+
+MessageSerializer::~MessageSerializer() = default;
+
+std::vector<uint8_t> MessageSerializer::SerializeAsArray() {
+  return msg_.SerializeAsArray();
+}
+
+std::string MessageSerializer::SerializeAsString() {
+  return msg_.SerializeAsString();
+}
+
+}  // namespace gen_helpers
+}  // namespace internal
+}  // namespace protozero
diff --git a/src/protozero/protoc_plugin/cppgen_plugin.cc b/src/protozero/protoc_plugin/cppgen_plugin.cc
index a963910..eadc23a 100644
--- a/src/protozero/protoc_plugin/cppgen_plugin.cc
+++ b/src/protozero/protoc_plugin/cppgen_plugin.cc
@@ -46,6 +46,7 @@
 using perfetto::base::StripSuffix;
 using perfetto::base::ToUpper;
 
+static constexpr auto TYPE_STRING = FieldDescriptor::TYPE_STRING;
 static constexpr auto TYPE_MESSAGE = FieldDescriptor::TYPE_MESSAGE;
 static constexpr auto TYPE_SINT32 = FieldDescriptor::TYPE_SINT32;
 static constexpr auto TYPE_SINT64 = FieldDescriptor::TYPE_SINT64;
@@ -147,6 +148,7 @@
   h_printer.Print("#include \"perfetto/protozero/copyable_ptr.h\"\n");
   h_printer.Print("#include \"perfetto/base/export.h\"\n\n");
 
+  cc_printer.Print("#include \"perfetto/protozero/gen_field_helpers.h\"\n");
   cc_printer.Print("#include \"perfetto/protozero/message.h\"\n");
   cc_printer.Print(
       "#include \"perfetto/protozero/packed_repeated_fields.h\"\n");
@@ -388,26 +390,26 @@
     const FieldDescriptor* field) const {
   switch (field->type()) {
     case FieldDescriptor::TYPE_BOOL:
-      return "AppendTinyVarInt";
+      return "::protozero::internal::gen_helpers::SerializeTinyVarInt";
     case FieldDescriptor::TYPE_INT32:
     case FieldDescriptor::TYPE_INT64:
     case FieldDescriptor::TYPE_UINT32:
     case FieldDescriptor::TYPE_UINT64:
     case FieldDescriptor::TYPE_ENUM:
-      return "AppendVarInt";
+      return "::protozero::internal::gen_helpers::SerializeVarInt";
     case FieldDescriptor::TYPE_SINT32:
     case FieldDescriptor::TYPE_SINT64:
-      return "AppendSignedVarInt";
+      return "::protozero::internal::gen_helpers::SerializeSignedVarInt";
     case FieldDescriptor::TYPE_FIXED32:
     case FieldDescriptor::TYPE_FIXED64:
     case FieldDescriptor::TYPE_SFIXED32:
     case FieldDescriptor::TYPE_SFIXED64:
     case FieldDescriptor::TYPE_FLOAT:
     case FieldDescriptor::TYPE_DOUBLE:
-      return "AppendFixed";
+      return "::protozero::internal::gen_helpers::SerializeFixed";
     case FieldDescriptor::TYPE_STRING:
     case FieldDescriptor::TYPE_BYTES:
-      return "AppendString";
+      return "::protozero::internal::gen_helpers::SerializeString";
     case FieldDescriptor::TYPE_GROUP:
     case FieldDescriptor::TYPE_MESSAGE:
       abort();
@@ -755,7 +757,10 @@
              "n", field->lowercase_name());
     p->Indent();
     if (field->options().lazy()) {
-      p->Print("$n$_ = field.as_std_string();\n", "n", field->lowercase_name());
+      p->Print(
+          "::protozero::internal::gen_helpers::DeserializeString(field, "
+          "&$n$_);\n",
+          "n", field->lowercase_name());
     } else {
       std::string statement;
       if (field->type() == TYPE_MESSAGE) {
@@ -764,6 +769,10 @@
         if (field->type() == TYPE_SINT32 || field->type() == TYPE_SINT64) {
           // sint32/64 fields are special and need to be zig-zag-decoded.
           statement = "field.get_signed(&$rval$);\n";
+        } else if (field->type() == TYPE_STRING) {
+          statement =
+              "::protozero::internal::gen_helpers::DeserializeString(field, "
+              "&$rval$);\n";
         } else {
           statement = "field.get(&$rval$);\n";
         }
@@ -774,10 +783,12 @@
           PERFETTO_FATAL("packed signed (zigzag) fields are not supported");
         }
         p->Print(
-            "for (::protozero::PackedRepeatedFieldIterator<$w$, $c$> "
-            "rep(field.data(), field.size(), &packed_error); rep; ++rep) {\n",
-            "w", GetPackedWireType(field), "c", GetCppType(field, false));
-        p->Print("  $n$_.emplace_back(*rep);\n", "n", field->lowercase_name());
+            "if "
+            "(!::protozero::internal::gen_helpers::DeserializePackedRepeated"
+            "<$w$, $c$>(field, &$n$_)) {\n",
+            "w", GetPackedWireType(field), "c", GetCppType(field, false), "n",
+            field->lowercase_name());
+        p->Print("  packed_error = true;");
         p->Print("}\n");
       } else if (field->is_repeated()) {
         p->Print("$n$_.emplace_back();\n", "n", field->lowercase_name());
@@ -807,7 +818,7 @@
   // Generate the SerializeAsString() method definition.
   p->Print("std::string $f$::SerializeAsString() const {\n", "f", full_name);
   p->Indent();
-  p->Print("::protozero::HeapBuffered<::protozero::Message> msg;\n");
+  p->Print("::protozero::internal::gen_helpers::MessageSerializer msg;\n");
   p->Print("Serialize(msg.get());\n");
   p->Print("return msg.SerializeAsString();\n");
   p->Outdent();
@@ -817,7 +828,7 @@
   p->Print("std::vector<uint8_t> $f$::SerializeAsArray() const {\n", "f",
            full_name);
   p->Indent();
-  p->Print("::protozero::HeapBuffered<::protozero::Message> msg;\n");
+  p->Print("::protozero::internal::gen_helpers::MessageSerializer msg;\n");
   p->Print("Serialize(msg.get());\n");
   p->Print("return msg.SerializeAsArray();\n");
   p->Outdent();
@@ -863,7 +874,7 @@
                  "msg->BeginNestedMessage<::protozero::Message>($id$));\n");
       } else {
         args["setter"] = GetProtozeroSetter(field);
-        p->Print(args, "msg->$setter$($id$, $rvalue$);\n");
+        p->Print(args, "$setter$($id$, $rvalue$, msg);\n");
       }
       p->Outdent();
       p->Print("}\n");
@@ -872,8 +883,8 @@
     p->Print("\n");
   }  // for (field)
   p->Print(
-      "msg->AppendRawProtoBytes(unknown_fields_.data(), "
-      "unknown_fields_.size());\n");
+      "protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_"
+      ", msg);\n");
   p->Outdent();
   p->Print("}\n\n");
 }