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");
}