/* * Copyright (C) 2017 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "perfetto/ext/base/string_utils.h" namespace protozero { namespace { using namespace google::protobuf; using namespace google::protobuf::compiler; using namespace google::protobuf::io; using perfetto::base::SplitString; using perfetto::base::StripChars; using perfetto::base::StripSuffix; using perfetto::base::ToUpper; static constexpr auto TYPE_MESSAGE = FieldDescriptor::TYPE_MESSAGE; static constexpr auto TYPE_SINT32 = FieldDescriptor::TYPE_SINT32; static constexpr auto TYPE_SINT64 = FieldDescriptor::TYPE_SINT64; static const char kHeader[] = "// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin\n"; class CppObjGenerator : public ::google::protobuf::compiler::CodeGenerator { public: CppObjGenerator(); ~CppObjGenerator() override; // CodeGenerator implementation bool Generate(const google::protobuf::FileDescriptor* file, const std::string& options, GeneratorContext* context, std::string* error) const override; private: std::string GetCppType(const FieldDescriptor* field, bool constref) const; std::string GetProtozeroSetter(const FieldDescriptor* field) const; std::string GetPackedBuffer(const FieldDescriptor* field) const; std::string GetPackedWireType(const FieldDescriptor* field) const; void GenEnum(const EnumDescriptor*, Printer*) const; void GenEnumAliases(const EnumDescriptor*, Printer*) const; void GenClassDecl(const Descriptor*, Printer*) const; void GenClassDef(const Descriptor*, Printer*) const; std::vector GetNamespaces(const FileDescriptor* file) const { std::string pkg = file->package() + wrapper_namespace_; return SplitString(pkg, "."); } template std::string GetFullName(const T* msg, bool with_namespace = false) const { std::string full_type; full_type.append(msg->name()); for (const Descriptor* par = msg->containing_type(); par; par = par->containing_type()) { full_type.insert(0, par->name() + "_"); } if (with_namespace) { std::string prefix; for (const std::string& ns : GetNamespaces(msg->file())) { prefix += ns + "::"; } full_type = prefix + full_type; } return full_type; } mutable std::string wrapper_namespace_; }; CppObjGenerator::CppObjGenerator() = default; CppObjGenerator::~CppObjGenerator() = default; bool CppObjGenerator::Generate(const google::protobuf::FileDescriptor* file, const std::string& options, GeneratorContext* context, std::string* error) const { for (const std::string& option : SplitString(options, ",")) { std::vector option_pair = SplitString(option, "="); if (option_pair[0] == "wrapper_namespace") { wrapper_namespace_ = option_pair.size() == 2 ? "." + option_pair[1] : std::string(); } else { *error = "Unknown plugin option: " + option_pair[0]; return false; } } auto get_file_name = [](const FileDescriptor* proto) { return StripSuffix(proto->name(), ".proto") + ".gen"; }; const std::unique_ptr h_fstream( context->Open(get_file_name(file) + ".h")); const std::unique_ptr cc_fstream( context->Open(get_file_name(file) + ".cc")); // Variables are delimited by $. Printer h_printer(h_fstream.get(), '$'); Printer cc_printer(cc_fstream.get(), '$'); std::string include_guard = file->package() + "_" + file->name() + "_CPP_H_"; include_guard = ToUpper(include_guard); include_guard = StripChars(include_guard, ".-/\\", '_'); h_printer.Print(kHeader); h_printer.Print("#ifndef $g$\n#define $g$\n\n", "g", include_guard); h_printer.Print("#include \n"); h_printer.Print("#include \n"); h_printer.Print("#include \n"); h_printer.Print("#include \n"); h_printer.Print("#include \n\n"); h_printer.Print("#include \"perfetto/protozero/cpp_message_obj.h\"\n"); 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/message.h\"\n"); cc_printer.Print( "#include \"perfetto/protozero/packed_repeated_fields.h\"\n"); cc_printer.Print("#include \"perfetto/protozero/proto_decoder.h\"\n"); cc_printer.Print("#include \"perfetto/protozero/scattered_heap_buffer.h\"\n"); cc_printer.Print(kHeader); cc_printer.Print("#if defined(__GNUC__) || defined(__clang__)\n"); cc_printer.Print("#pragma GCC diagnostic push\n"); cc_printer.Print("#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n"); cc_printer.Print("#endif\n"); // Generate includes for translated types of dependencies. // Figure out the subset of imports that are used only for lazy fields. We // won't emit a C++ #include for them. This code is overly aggressive at // removing imports: it rules them out as soon as it sees one lazy field // whose type is defined in that import. A 100% correct solution would require // to check that *all* dependent types for a given import are lazy before // excluding that. In practice we don't need that because we don't use imports // for both lazy and non-lazy fields. std::set lazy_imports; for (int m = 0; m < file->message_type_count(); m++) { const Descriptor* msg = file->message_type(m); for (int i = 0; i < msg->field_count(); i++) { const FieldDescriptor* field = msg->field(i); if (field->options().lazy()) { lazy_imports.insert(field->message_type()->file()->name()); } } } // Recursively traverse all imports and turn them into #include(s). std::vector imports_to_visit; std::set imports_visited; imports_to_visit.push_back(file); while (!imports_to_visit.empty()) { const FileDescriptor* cur = imports_to_visit.back(); imports_to_visit.pop_back(); imports_visited.insert(cur); std::string base_name = StripSuffix(cur->name(), ".proto"); cc_printer.Print("#include \"$f$.gen.h\"\n", "f", base_name); for (int i = 0; i < cur->dependency_count(); i++) { const FileDescriptor* dep = cur->dependency(i); if (imports_visited.count(dep) || lazy_imports.count(dep->name())) continue; imports_to_visit.push_back(dep); } } // Compute all nested types to generate forward declarations later. std::set all_types_seen; // All deps std::set all_enums_seen; // We track the types additionally in vectors to guarantee a stable order in // the generated output. std::vector local_types; // Cur .proto file only. std::vector all_types; // All deps std::vector local_enums; std::vector all_enums; auto add_enum = [&local_enums, &all_enums, &all_enums_seen, &file](const EnumDescriptor* enum_desc) { if (all_enums_seen.count(enum_desc)) return; all_enums_seen.insert(enum_desc); all_enums.push_back(enum_desc); if (enum_desc->file() == file) local_enums.push_back(enum_desc); }; for (int i = 0; i < file->enum_type_count(); i++) add_enum(file->enum_type(i)); std::stack recursion_stack; for (int i = 0; i < file->message_type_count(); i++) recursion_stack.push(file->message_type(i)); while (!recursion_stack.empty()) { const Descriptor* msg = recursion_stack.top(); recursion_stack.pop(); if (all_types_seen.count(msg)) continue; all_types_seen.insert(msg); all_types.push_back(msg); if (msg->file() == file) local_types.push_back(msg); for (int i = 0; i < msg->nested_type_count(); i++) recursion_stack.push(msg->nested_type(i)); for (int i = 0; i < msg->enum_type_count(); i++) add_enum(msg->enum_type(i)); for (int i = 0; i < msg->field_count(); i++) { const FieldDescriptor* field = msg->field(i); if (field->has_default_value()) { *error = "field " + field->name() + ": Explicitly declared default values are not supported"; return false; } if (field->options().lazy() && (field->is_repeated() || field->type() != TYPE_MESSAGE)) { *error = "[lazy=true] is supported only on non-repeated fields\n"; return false; } if (field->type() == TYPE_MESSAGE && !field->options().lazy()) recursion_stack.push(field->message_type()); if (field->type() == FieldDescriptor::TYPE_ENUM) add_enum(field->enum_type()); } } // while (!recursion_stack.empty()) // Generate forward declarations in the header for proto types. // Note: do NOT add #includes to other generated headers (either .gen.h or // .pbzero.h). Doing so is extremely hard to handle at the build-system level // and requires propagating public_deps everywhere. cc_printer.Print("\n"); // -- Begin of fwd declarations. // Build up the map of forward declarations. std::multimap fwd_decls; enum FwdType { kClass, kEnum }; auto add_fwd_decl = [&fwd_decls](FwdType cpp_type, const std::string& full_name) { auto dot = full_name.rfind("::"); PERFETTO_CHECK(dot != std::string::npos); auto package = full_name.substr(0, dot); auto name = full_name.substr(dot + 2); if (cpp_type == kClass) { fwd_decls.emplace(package, "class " + name + ";"); } else { PERFETTO_CHECK(cpp_type == kEnum); fwd_decls.emplace(package, "enum " + name + " : int;"); } }; add_fwd_decl(kClass, "protozero::Message"); for (const Descriptor* msg : all_types) { add_fwd_decl(kClass, GetFullName(msg, true)); } for (const EnumDescriptor* enm : all_enums) { add_fwd_decl(kEnum, GetFullName(enm, true)); } // Emit forward declarations grouping by package. std::string last_package; auto close_last_package = [&last_package, &h_printer] { if (!last_package.empty()) { for (const std::string& ns : SplitString(last_package, "::")) h_printer.Print("} // namespace $ns$\n", "ns", ns); h_printer.Print("\n"); } }; for (const auto& kv : fwd_decls) { const std::string& package = kv.first; if (package != last_package) { close_last_package(); last_package = package; for (const std::string& ns : SplitString(package, "::")) h_printer.Print("namespace $ns$ {\n", "ns", ns); } h_printer.Print("$decl$\n", "decl", kv.second); } close_last_package(); // -- End of fwd declarations. for (const std::string& ns : GetNamespaces(file)) { h_printer.Print("namespace $n$ {\n", "n", ns); cc_printer.Print("namespace $n$ {\n", "n", ns); } // Generate declarations and definitions. for (const EnumDescriptor* enm : local_enums) GenEnum(enm, &h_printer); for (const Descriptor* msg : local_types) { GenClassDecl(msg, &h_printer); GenClassDef(msg, &cc_printer); } for (const std::string& ns : GetNamespaces(file)) { h_printer.Print("} // namespace $n$\n", "n", ns); cc_printer.Print("} // namespace $n$\n", "n", ns); } cc_printer.Print("#if defined(__GNUC__) || defined(__clang__)\n"); cc_printer.Print("#pragma GCC diagnostic pop\n"); cc_printer.Print("#endif\n"); h_printer.Print("\n#endif // $g$\n", "g", include_guard); return true; } std::string CppObjGenerator::GetCppType(const FieldDescriptor* field, bool constref) const { switch (field->type()) { case FieldDescriptor::TYPE_DOUBLE: return "double"; case FieldDescriptor::TYPE_FLOAT: return "float"; case FieldDescriptor::TYPE_FIXED32: case FieldDescriptor::TYPE_UINT32: return "uint32_t"; case FieldDescriptor::TYPE_SFIXED32: case FieldDescriptor::TYPE_INT32: case FieldDescriptor::TYPE_SINT32: return "int32_t"; case FieldDescriptor::TYPE_FIXED64: case FieldDescriptor::TYPE_UINT64: return "uint64_t"; case FieldDescriptor::TYPE_SFIXED64: case FieldDescriptor::TYPE_SINT64: case FieldDescriptor::TYPE_INT64: return "int64_t"; case FieldDescriptor::TYPE_BOOL: return "bool"; case FieldDescriptor::TYPE_STRING: case FieldDescriptor::TYPE_BYTES: return constref ? "const std::string&" : "std::string"; case FieldDescriptor::TYPE_MESSAGE: assert(!field->options().lazy()); return constref ? "const " + GetFullName(field->message_type()) + "&" : GetFullName(field->message_type()); case FieldDescriptor::TYPE_ENUM: return GetFullName(field->enum_type()); case FieldDescriptor::TYPE_GROUP: abort(); } abort(); // for gcc } std::string CppObjGenerator::GetProtozeroSetter( const FieldDescriptor* field) const { switch (field->type()) { case FieldDescriptor::TYPE_BOOL: return "AppendTinyVarInt"; case FieldDescriptor::TYPE_INT32: case FieldDescriptor::TYPE_INT64: case FieldDescriptor::TYPE_UINT32: case FieldDescriptor::TYPE_UINT64: case FieldDescriptor::TYPE_ENUM: return "AppendVarInt"; case FieldDescriptor::TYPE_SINT32: case FieldDescriptor::TYPE_SINT64: return "AppendSignedVarInt"; 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"; case FieldDescriptor::TYPE_STRING: case FieldDescriptor::TYPE_BYTES: return "AppendString"; case FieldDescriptor::TYPE_GROUP: case FieldDescriptor::TYPE_MESSAGE: abort(); } abort(); } std::string CppObjGenerator::GetPackedBuffer( const FieldDescriptor* field) const { switch (field->type()) { case FieldDescriptor::TYPE_FIXED32: return "::protozero::PackedFixedSizeInt"; case FieldDescriptor::TYPE_SFIXED32: return "::protozero::PackedFixedSizeInt"; case FieldDescriptor::TYPE_FIXED64: return "::protozero::PackedFixedSizeInt"; case FieldDescriptor::TYPE_SFIXED64: return "::protozero::PackedFixedSizeInt"; case FieldDescriptor::TYPE_DOUBLE: return "::protozero::PackedFixedSizeInt"; case FieldDescriptor::TYPE_FLOAT: return "::protozero::PackedFixedSizeInt"; case FieldDescriptor::TYPE_INT32: case FieldDescriptor::TYPE_SINT32: case FieldDescriptor::TYPE_UINT32: case FieldDescriptor::TYPE_INT64: case FieldDescriptor::TYPE_UINT64: case FieldDescriptor::TYPE_SINT64: case FieldDescriptor::TYPE_BOOL: return "::protozero::PackedVarInt"; case FieldDescriptor::TYPE_STRING: case FieldDescriptor::TYPE_BYTES: case FieldDescriptor::TYPE_MESSAGE: case FieldDescriptor::TYPE_ENUM: case FieldDescriptor::TYPE_GROUP: break; // Will abort() } abort(); } std::string CppObjGenerator::GetPackedWireType( const FieldDescriptor* field) const { switch (field->type()) { case FieldDescriptor::TYPE_FIXED32: case FieldDescriptor::TYPE_SFIXED32: case FieldDescriptor::TYPE_FLOAT: return "::protozero::proto_utils::ProtoWireType::kFixed32"; case FieldDescriptor::TYPE_FIXED64: case FieldDescriptor::TYPE_SFIXED64: case FieldDescriptor::TYPE_DOUBLE: return "::protozero::proto_utils::ProtoWireType::kFixed64"; case FieldDescriptor::TYPE_INT32: case FieldDescriptor::TYPE_SINT32: case FieldDescriptor::TYPE_UINT32: case FieldDescriptor::TYPE_INT64: case FieldDescriptor::TYPE_UINT64: case FieldDescriptor::TYPE_SINT64: case FieldDescriptor::TYPE_BOOL: return "::protozero::proto_utils::ProtoWireType::kVarInt"; case FieldDescriptor::TYPE_STRING: case FieldDescriptor::TYPE_BYTES: case FieldDescriptor::TYPE_MESSAGE: case FieldDescriptor::TYPE_ENUM: case FieldDescriptor::TYPE_GROUP: break; // Will abort() } abort(); } void CppObjGenerator::GenEnum(const EnumDescriptor* enum_desc, Printer* p) const { std::string full_name = GetFullName(enum_desc); // When generating enums, there are two cases: // 1. Enums nested in a message (most frequent case), e.g.: // message MyMsg { enum MyEnum { FOO=1; BAR=2; } } // 2. Enum defined at the package level, outside of any message. // // In the case 1, the C++ code generated by the official protobuf library is: // enum MyEnum { MyMsg_MyEnum_FOO=1, MyMsg_MyEnum_BAR=2 } // class MyMsg { static const auto FOO = MyMsg_MyEnum_FOO; ... same for BAR } // // In the case 2, the C++ code is simply: // enum MyEnum { FOO=1, BAR=2 } // Hence this |prefix| logic. std::string prefix = enum_desc->containing_type() ? full_name + "_" : ""; p->Print("enum $f$ : int {\n", "f", full_name); for (int e = 0; e < enum_desc->value_count(); e++) { const EnumValueDescriptor* value = enum_desc->value(e); p->Print(" $p$$n$ = $v$,\n", "p", prefix, "n", value->name(), "v", std::to_string(value->number())); } p->Print("};\n"); } void CppObjGenerator::GenEnumAliases(const EnumDescriptor* enum_desc, Printer* p) const { int min_value = std::numeric_limits::max(); int max_value = std::numeric_limits::min(); std::string min_name; std::string max_name; std::string full_name = GetFullName(enum_desc); for (int e = 0; e < enum_desc->value_count(); e++) { const EnumValueDescriptor* value = enum_desc->value(e); p->Print("static constexpr auto $n$ = $f$_$n$;\n", "f", full_name, "n", value->name()); if (value->number() < min_value) { min_value = value->number(); min_name = full_name + "_" + value->name(); } if (value->number() > max_value) { max_value = value->number(); max_name = full_name + "_" + value->name(); } } p->Print("static constexpr auto $n$_MIN = $m$;\n", "n", enum_desc->name(), "m", min_name); p->Print("static constexpr auto $n$_MAX = $m$;\n", "n", enum_desc->name(), "m", max_name); } void CppObjGenerator::GenClassDecl(const Descriptor* msg, Printer* p) const { std::string full_name = GetFullName(msg); p->Print( "\nclass PERFETTO_EXPORT $n$ : public ::protozero::CppMessageObj {\n", "n", full_name); p->Print(" public:\n"); p->Indent(); // Do a first pass to generate aliases for nested types. // e.g., using Foo = Parent_Foo; for (int i = 0; i < msg->nested_type_count(); i++) { const Descriptor* nested_msg = msg->nested_type(i); p->Print("using $n$ = $f$;\n", "n", nested_msg->name(), "f", GetFullName(nested_msg)); } for (int i = 0; i < msg->enum_type_count(); i++) { const EnumDescriptor* nested_enum = msg->enum_type(i); p->Print("using $n$ = $f$;\n", "n", nested_enum->name(), "f", GetFullName(nested_enum)); GenEnumAliases(nested_enum, p); } // Generate constants with field numbers. p->Print("enum FieldNumbers {\n"); for (int i = 0; i < msg->field_count(); i++) { const FieldDescriptor* field = msg->field(i); std::string name = field->camelcase_name(); name[0] = perfetto::base::Uppercase(name[0]); p->Print(" k$n$FieldNumber = $num$,\n", "n", name, "num", std::to_string(field->number())); } p->Print("};\n\n"); p->Print("$n$();\n", "n", full_name); p->Print("~$n$() override;\n", "n", full_name); p->Print("$n$($n$&&) noexcept;\n", "n", full_name); p->Print("$n$& operator=($n$&&);\n", "n", full_name); p->Print("$n$(const $n$&);\n", "n", full_name); p->Print("$n$& operator=(const $n$&);\n", "n", full_name); p->Print("bool operator==(const $n$&) const;\n", "n", full_name); p->Print( "bool operator!=(const $n$& other) const { return !(*this == other); }\n", "n", full_name); p->Print("\n"); std::string proto_type = GetFullName(msg, true); p->Print("bool ParseFromArray(const void*, size_t) override;\n"); p->Print("std::string SerializeAsString() const override;\n"); p->Print("std::vector SerializeAsArray() const override;\n"); p->Print("void Serialize(::protozero::Message*) const;\n"); // Generate accessors. for (int i = 0; i < msg->field_count(); i++) { const FieldDescriptor* field = msg->field(i); auto set_bit = "_has_field_.set(" + std::to_string(field->number()) + ")"; p->Print("\n"); if (field->options().lazy()) { p->Print("const std::string& $n$_raw() const { return $n$_; }\n", "n", field->lowercase_name()); p->Print( "void set_$n$_raw(const std::string& raw) { $n$_ = raw; $s$; }\n", "n", field->lowercase_name(), "s", set_bit); } else if (!field->is_repeated()) { p->Print("bool has_$n$() const { return _has_field_[$bit$]; }\n", "n", field->lowercase_name(), "bit", std::to_string(field->number())); if (field->type() == TYPE_MESSAGE) { p->Print("$t$ $n$() const { return *$n$_; }\n", "t", GetCppType(field, true), "n", field->lowercase_name()); p->Print("$t$* mutable_$n$() { $s$; return $n$_.get(); }\n", "t", GetCppType(field, false), "n", field->lowercase_name(), "s", set_bit); } else { p->Print("$t$ $n$() const { return $n$_; }\n", "t", GetCppType(field, true), "n", field->lowercase_name()); p->Print("void set_$n$($t$ value) { $n$_ = value; $s$; }\n", "t", GetCppType(field, true), "n", field->lowercase_name(), "s", set_bit); if (field->type() == FieldDescriptor::TYPE_BYTES) { p->Print( "void set_$n$(const void* p, size_t s) { " "$n$_.assign(reinterpret_cast(p), s); $s$; }\n", "n", field->lowercase_name(), "s", set_bit); } } } else { // is_repeated() p->Print("const std::vector<$t$>& $n$() const { return $n$_; }\n", "t", GetCppType(field, false), "n", field->lowercase_name()); p->Print("std::vector<$t$>* mutable_$n$() { return &$n$_; }\n", "t", GetCppType(field, false), "n", field->lowercase_name()); // Generate accessors for repeated message types in the .cc file so that // the header doesn't depend on the full definition of all nested types. if (field->type() == TYPE_MESSAGE) { p->Print("int $n$_size() const;\n", "t", GetCppType(field, false), "n", field->lowercase_name()); p->Print("void clear_$n$();\n", "n", field->lowercase_name()); p->Print("$t$* add_$n$();\n", "t", GetCppType(field, false), "n", field->lowercase_name()); } else { // Primitive type. p->Print( "int $n$_size() const { return static_cast($n$_.size()); }\n", "t", GetCppType(field, false), "n", field->lowercase_name()); p->Print("void clear_$n$() { $n$_.clear(); }\n", "n", field->lowercase_name()); p->Print("void add_$n$($t$ value) { $n$_.emplace_back(value); }\n", "t", GetCppType(field, false), "n", field->lowercase_name()); // TODO(primiano): this should be done only for TYPE_MESSAGE. // Unfortuntely we didn't realize before and now we have a bunch of code // that does: *msg->add_int_value() = 42 instead of // msg->add_int_value(42). p->Print( "$t$* add_$n$() { $n$_.emplace_back(); return &$n$_.back(); }\n", "t", GetCppType(field, false), "n", field->lowercase_name()); } } } p->Outdent(); p->Print("\n private:\n"); p->Indent(); // Generate fields. int max_field_id = 1; for (int i = 0; i < msg->field_count(); i++) { const FieldDescriptor* field = msg->field(i); max_field_id = std::max(max_field_id, field->number()); if (field->options().lazy()) { p->Print("std::string $n$_; // [lazy=true]\n", "n", field->lowercase_name()); } else if (!field->is_repeated()) { std::string type = GetCppType(field, false); if (field->type() == TYPE_MESSAGE) { type = "::protozero::CopyablePtr<" + type + ">"; p->Print("$t$ $n$_;\n", "t", type, "n", field->lowercase_name()); } else { p->Print("$t$ $n$_{};\n", "t", type, "n", field->lowercase_name()); } } else { // is_repeated() p->Print("std::vector<$t$> $n$_;\n", "t", GetCppType(field, false), "n", field->lowercase_name()); } } p->Print("\n"); p->Print("// Allows to preserve unknown protobuf fields for compatibility\n"); p->Print("// with future versions of .proto files.\n"); p->Print("std::string unknown_fields_;\n"); p->Print("\nstd::bitset<$id$> _has_field_{};\n", "id", std::to_string(max_field_id + 1)); p->Outdent(); p->Print("};\n\n"); } void CppObjGenerator::GenClassDef(const Descriptor* msg, Printer* p) const { p->Print("\n"); std::string full_name = GetFullName(msg); p->Print("$n$::$n$() = default;\n", "n", full_name); p->Print("$n$::~$n$() = default;\n", "n", full_name); p->Print("$n$::$n$(const $n$&) = default;\n", "n", full_name); p->Print("$n$& $n$::operator=(const $n$&) = default;\n", "n", full_name); p->Print("$n$::$n$($n$&&) noexcept = default;\n", "n", full_name); p->Print("$n$& $n$::operator=($n$&&) = default;\n", "n", full_name); p->Print("\n"); // Comparison operator p->Print("bool $n$::operator==(const $n$& other) const {\n", "n", full_name); p->Indent(); p->Print("return unknown_fields_ == other.unknown_fields_"); for (int i = 0; i < msg->field_count(); i++) p->Print("\n && $n$_ == other.$n$_", "n", msg->field(i)->lowercase_name()); p->Print(";"); p->Outdent(); p->Print("\n}\n\n"); // Accessors for repeated message fields. for (int i = 0; i < msg->field_count(); i++) { const FieldDescriptor* field = msg->field(i); if (field->options().lazy() || !field->is_repeated() || field->type() != TYPE_MESSAGE) { continue; } p->Print( "int $c$::$n$_size() const { return static_cast($n$_.size()); }\n", "c", full_name, "t", GetCppType(field, false), "n", field->lowercase_name()); p->Print("void $c$::clear_$n$() { $n$_.clear(); }\n", "c", full_name, "n", field->lowercase_name()); p->Print( "$t$* $c$::add_$n$() { $n$_.emplace_back(); return &$n$_.back(); }\n", "c", full_name, "t", GetCppType(field, false), "n", field->lowercase_name()); } std::string proto_type = GetFullName(msg, true); // Generate the ParseFromArray() method definition. p->Print("bool $f$::ParseFromArray(const void* raw, size_t size) {\n", "f", full_name); p->Indent(); for (int i = 0; i < msg->field_count(); i++) { const FieldDescriptor* field = msg->field(i); if (field->is_repeated()) p->Print("$n$_.clear();\n", "n", field->lowercase_name()); } p->Print("unknown_fields_.clear();\n"); p->Print("bool packed_error = false;\n"); p->Print("\n"); p->Print("::protozero::ProtoDecoder dec(raw, size);\n"); p->Print("for (auto field = dec.ReadField(); field.valid(); "); p->Print("field = dec.ReadField()) {\n"); p->Indent(); p->Print("if (field.id() < _has_field_.size()) {\n"); p->Print(" _has_field_.set(field.id());\n"); p->Print("}\n"); p->Print("switch (field.id()) {\n"); p->Indent(); for (int i = 0; i < msg->field_count(); i++) { const FieldDescriptor* field = msg->field(i); p->Print("case $id$ /* $n$ */:\n", "id", std::to_string(field->number()), "n", field->lowercase_name()); p->Indent(); if (field->options().lazy()) { p->Print("$n$_ = field.as_std_string();\n", "n", field->lowercase_name()); } else { std::string statement; if (field->type() == TYPE_MESSAGE) { statement = "$rval$.ParseFromArray(field.data(), field.size());\n"; } else { 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 { statement = "field.get(&$rval$);\n"; } } if (field->is_packed()) { PERFETTO_CHECK(field->is_repeated()); if (field->type() == TYPE_SINT32 || field->type() == TYPE_SINT64) { 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()); p->Print("}\n"); } else if (field->is_repeated()) { p->Print("$n$_.emplace_back();\n", "n", field->lowercase_name()); p->Print(statement.c_str(), "rval", field->lowercase_name() + "_.back()"); } else if (field->type() == TYPE_MESSAGE) { p->Print(statement.c_str(), "rval", "(*" + field->lowercase_name() + "_)"); } else { p->Print(statement.c_str(), "rval", field->lowercase_name() + "_"); } } p->Print("break;\n"); p->Outdent(); } // for (field) p->Print("default:\n"); p->Print(" field.SerializeAndAppendTo(&unknown_fields_);\n"); p->Print(" break;\n"); p->Outdent(); p->Print("}\n"); // switch(field.id) p->Outdent(); p->Print("}\n"); // for(field) p->Print("return !packed_error && !dec.bytes_left();\n"); // for(field) p->Outdent(); p->Print("}\n\n"); // 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("Serialize(msg.get());\n"); p->Print("return msg.SerializeAsString();\n"); p->Outdent(); p->Print("}\n\n"); // Generate the SerializeAsArray() method definition. p->Print("std::vector $f$::SerializeAsArray() const {\n", "f", full_name); p->Indent(); p->Print("::protozero::HeapBuffered<::protozero::Message> msg;\n"); p->Print("Serialize(msg.get());\n"); p->Print("return msg.SerializeAsArray();\n"); p->Outdent(); p->Print("}\n\n"); // Generate the Serialize() method that writes the fields into the passed // protozero |msg| write-only interface |msg|. p->Print("void $f$::Serialize(::protozero::Message* msg) const {\n", "f", full_name); p->Indent(); for (int i = 0; i < msg->field_count(); i++) { const FieldDescriptor* field = msg->field(i); std::map args; args["id"] = std::to_string(field->number()); args["n"] = field->lowercase_name(); p->Print(args, "// Field $id$: $n$\n"); if (field->is_packed()) { PERFETTO_CHECK(field->is_repeated()); p->Print("{\n"); p->Indent(); p->Print("$p$ pack;\n", "p", GetPackedBuffer(field)); p->Print(args, "for (auto& it : $n$_)\n"); p->Print(args, " pack.Append(it);\n"); p->Print(args, "msg->AppendBytes($id$, pack.data(), pack.size());\n"); p->Outdent(); p->Print("}\n"); } else { if (field->is_repeated()) { p->Print(args, "for (auto& it : $n$_) {\n"); args["lvalue"] = "it"; args["rvalue"] = "it"; } else { p->Print(args, "if (_has_field_[$id$]) {\n"); args["lvalue"] = "(*" + field->lowercase_name() + "_)"; args["rvalue"] = field->lowercase_name() + "_"; } p->Indent(); if (field->options().lazy()) { p->Print(args, "msg->AppendString($id$, $rvalue$);\n"); } else if (field->type() == TYPE_MESSAGE) { p->Print(args, "$lvalue$.Serialize(" "msg->BeginNestedMessage<::protozero::Message>($id$));\n"); } else { args["setter"] = GetProtozeroSetter(field); p->Print(args, "msg->$setter$($id$, $rvalue$);\n"); } p->Outdent(); p->Print("}\n"); } p->Print("\n"); } // for (field) p->Print( "msg->AppendRawProtoBytes(unknown_fields_.data(), " "unknown_fields_.size());\n"); p->Outdent(); p->Print("}\n\n"); } } // namespace } // namespace protozero int main(int argc, char** argv) { ::protozero::CppObjGenerator generator; return google::protobuf::compiler::PluginMain(argc, argv, &generator); }