blob: fdc6821e0aa163511a105c9b736cb263306b82e9 [file] [log] [blame]
Primiano Tucci10613fa2019-10-15 17:25:51 +01001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19
20#include <fstream>
21#include <iostream>
Primiano Tucci6a7813d2019-11-19 11:08:50 +000022#include <map>
Primiano Tucci10613fa2019-10-15 17:25:51 +010023#include <set>
24#include <stack>
25#include <vector>
26
27#include <google/protobuf/compiler/code_generator.h>
28#include <google/protobuf/compiler/importer.h>
29#include <google/protobuf/compiler/plugin.h>
30#include <google/protobuf/dynamic_message.h>
31#include <google/protobuf/io/printer.h>
32#include <google/protobuf/io/zero_copy_stream_impl.h>
33#include <google/protobuf/util/field_comparator.h>
34#include <google/protobuf/util/message_differencer.h>
35
36#include "perfetto/ext/base/string_utils.h"
37
38namespace protozero {
39namespace {
40
41using namespace google::protobuf;
42using namespace google::protobuf::compiler;
43using namespace google::protobuf::io;
44using perfetto::base::SplitString;
45using perfetto::base::StripChars;
46using perfetto::base::StripSuffix;
47using perfetto::base::ToUpper;
48
49static constexpr auto TYPE_MESSAGE = FieldDescriptor::TYPE_MESSAGE;
Primiano Tucci6a7813d2019-11-19 11:08:50 +000050static constexpr auto TYPE_SINT32 = FieldDescriptor::TYPE_SINT32;
51static constexpr auto TYPE_SINT64 = FieldDescriptor::TYPE_SINT64;
Primiano Tucci10613fa2019-10-15 17:25:51 +010052
53static const char kHeader[] =
Primiano Tucci57dd66b2019-10-15 23:09:04 +010054 "// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin\n";
Primiano Tucci10613fa2019-10-15 17:25:51 +010055
Primiano Tucci10613fa2019-10-15 17:25:51 +010056class CppObjGenerator : public ::google::protobuf::compiler::CodeGenerator {
57 public:
58 CppObjGenerator();
59 ~CppObjGenerator() override;
60
61 // CodeGenerator implementation
62 bool Generate(const google::protobuf::FileDescriptor* file,
63 const std::string& options,
64 GeneratorContext* context,
65 std::string* error) const override;
66
67 private:
Primiano Tuccie8020f92019-11-26 13:24:01 +000068 std::string GetCppType(const FieldDescriptor* field, bool constref) const;
69 std::string GetProtozeroSetter(const FieldDescriptor* field) const;
70 std::string GetPackedBuffer(const FieldDescriptor* field) const;
71 std::string GetPackedWireType(const FieldDescriptor* field) const;
Primiano Tucci6a7813d2019-11-19 11:08:50 +000072
Primiano Tucci10613fa2019-10-15 17:25:51 +010073 void GenEnum(const EnumDescriptor*, Printer*) const;
74 void GenEnumAliases(const EnumDescriptor*, Printer*) const;
75 void GenClassDecl(const Descriptor*, Printer*) const;
76 void GenClassDef(const Descriptor*, Printer*) const;
Primiano Tuccie8020f92019-11-26 13:24:01 +000077
78 std::vector<std::string> GetNamespaces(const FileDescriptor* file) const {
79 std::string pkg = file->package() + wrapper_namespace_;
80 return SplitString(pkg, ".");
81 }
82
83 template <typename T = Descriptor>
84 std::string GetFullName(const T* msg, bool with_namespace = false) const {
85 std::string full_type;
86 full_type.append(msg->name());
87 for (const Descriptor* par = msg->containing_type(); par;
88 par = par->containing_type()) {
89 full_type.insert(0, par->name() + "_");
90 }
91 if (with_namespace) {
92 std::string prefix;
93 for (const std::string& ns : GetNamespaces(msg->file())) {
94 prefix += ns + "::";
95 }
96 full_type = prefix + full_type;
97 }
98 return full_type;
99 }
100
101 mutable std::string wrapper_namespace_;
Primiano Tucci10613fa2019-10-15 17:25:51 +0100102};
103
104CppObjGenerator::CppObjGenerator() = default;
105CppObjGenerator::~CppObjGenerator() = default;
106
107bool CppObjGenerator::Generate(const google::protobuf::FileDescriptor* file,
Primiano Tuccie8020f92019-11-26 13:24:01 +0000108 const std::string& options,
Primiano Tucci10613fa2019-10-15 17:25:51 +0100109 GeneratorContext* context,
110 std::string* error) const {
Primiano Tuccie8020f92019-11-26 13:24:01 +0000111 for (const std::string& option : SplitString(options, ",")) {
112 std::vector<std::string> option_pair = SplitString(option, "=");
113 if (option_pair[0] == "wrapper_namespace") {
114 wrapper_namespace_ =
115 option_pair.size() == 2 ? "." + option_pair[1] : std::string();
116 } else {
117 *error = "Unknown plugin option: " + option_pair[0];
118 return false;
119 }
120 }
121
Primiano Tucci10613fa2019-10-15 17:25:51 +0100122 auto get_file_name = [](const FileDescriptor* proto) {
123 return StripSuffix(proto->name(), ".proto") + ".gen";
124 };
125
126 const std::unique_ptr<ZeroCopyOutputStream> h_fstream(
127 context->Open(get_file_name(file) + ".h"));
128 const std::unique_ptr<ZeroCopyOutputStream> cc_fstream(
129 context->Open(get_file_name(file) + ".cc"));
130
131 // Variables are delimited by $.
132 Printer h_printer(h_fstream.get(), '$');
133 Printer cc_printer(cc_fstream.get(), '$');
134
135 std::string include_guard = file->package() + "_" + file->name() + "_CPP_H_";
136 include_guard = ToUpper(include_guard);
137 include_guard = StripChars(include_guard, ".-/\\", '_');
138
139 h_printer.Print(kHeader);
140 h_printer.Print("#ifndef $g$\n#define $g$\n\n", "g", include_guard);
141 h_printer.Print("#include <stdint.h>\n");
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000142 h_printer.Print("#include <bitset>\n");
Primiano Tucci10613fa2019-10-15 17:25:51 +0100143 h_printer.Print("#include <vector>\n");
144 h_printer.Print("#include <string>\n");
145 h_printer.Print("#include <type_traits>\n\n");
Primiano Tuccicb8cea22019-11-22 11:05:35 +0000146 h_printer.Print("#include \"perfetto/protozero/cpp_message_obj.h\"\n");
Primiano Tuccie4144b72019-11-07 16:10:19 +0000147 h_printer.Print("#include \"perfetto/protozero/copyable_ptr.h\"\n");
Primiano Tucci10613fa2019-10-15 17:25:51 +0100148 h_printer.Print("#include \"perfetto/base/export.h\"\n\n");
149
Primiano Tucci19121602019-11-21 15:39:09 +0000150 cc_printer.Print("#include \"perfetto/protozero/message.h\"\n");
151 cc_printer.Print(
152 "#include \"perfetto/protozero/packed_repeated_fields.h\"\n");
153 cc_printer.Print("#include \"perfetto/protozero/proto_decoder.h\"\n");
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000154 cc_printer.Print("#include \"perfetto/protozero/scattered_heap_buffer.h\"\n");
Primiano Tucci10613fa2019-10-15 17:25:51 +0100155 cc_printer.Print(kHeader);
Primiano Tucci63201702020-12-04 17:38:12 +0100156 cc_printer.Print("#if defined(__GNUC__) || defined(__clang__)\n");
Primiano Tucci10613fa2019-10-15 17:25:51 +0100157 cc_printer.Print("#pragma GCC diagnostic push\n");
158 cc_printer.Print("#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n");
Primiano Tucci63201702020-12-04 17:38:12 +0100159 cc_printer.Print("#endif\n");
Primiano Tucci10613fa2019-10-15 17:25:51 +0100160
161 // Generate includes for translated types of dependencies.
162
163 // Figure out the subset of imports that are used only for lazy fields. We
164 // won't emit a C++ #include for them. This code is overly aggressive at
165 // removing imports: it rules them out as soon as it sees one lazy field
166 // whose type is defined in that import. A 100% correct solution would require
167 // to check that *all* dependent types for a given import are lazy before
168 // excluding that. In practice we don't need that because we don't use imports
169 // for both lazy and non-lazy fields.
170 std::set<std::string> lazy_imports;
171 for (int m = 0; m < file->message_type_count(); m++) {
172 const Descriptor* msg = file->message_type(m);
173 for (int i = 0; i < msg->field_count(); i++) {
174 const FieldDescriptor* field = msg->field(i);
175 if (field->options().lazy()) {
176 lazy_imports.insert(field->message_type()->file()->name());
177 }
178 }
179 }
180
Primiano Tucci10613fa2019-10-15 17:25:51 +0100181 // Recursively traverse all imports and turn them into #include(s).
182 std::vector<const FileDescriptor*> imports_to_visit;
183 std::set<const FileDescriptor*> imports_visited;
184 imports_to_visit.push_back(file);
185
186 while (!imports_to_visit.empty()) {
187 const FileDescriptor* cur = imports_to_visit.back();
188 imports_to_visit.pop_back();
189 imports_visited.insert(cur);
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000190 std::string base_name = StripSuffix(cur->name(), ".proto");
191 cc_printer.Print("#include \"$f$.gen.h\"\n", "f", base_name);
Primiano Tucci10613fa2019-10-15 17:25:51 +0100192 for (int i = 0; i < cur->dependency_count(); i++) {
193 const FileDescriptor* dep = cur->dependency(i);
194 if (imports_visited.count(dep) || lazy_imports.count(dep->name()))
195 continue;
196 imports_to_visit.push_back(dep);
197 }
198 }
199
200 // Compute all nested types to generate forward declarations later.
201
Eric Seckler0230ace2019-10-30 12:33:06 +0000202 std::set<const Descriptor*> all_types_seen; // All deps
203 std::set<const EnumDescriptor*> all_enums_seen;
204
205 // We track the types additionally in vectors to guarantee a stable order in
206 // the generated output.
Eric Secklerd85805a2019-10-30 08:52:15 +0000207 std::vector<const Descriptor*> local_types; // Cur .proto file only.
208 std::vector<const Descriptor*> all_types; // All deps
209 std::vector<const EnumDescriptor*> local_enums;
210 std::vector<const EnumDescriptor*> all_enums;
Primiano Tucci10613fa2019-10-15 17:25:51 +0100211
Eric Seckler0230ace2019-10-30 12:33:06 +0000212 auto add_enum = [&local_enums, &all_enums, &all_enums_seen,
Primiano Tucci10613fa2019-10-15 17:25:51 +0100213 &file](const EnumDescriptor* enum_desc) {
Eric Seckler0230ace2019-10-30 12:33:06 +0000214 if (all_enums_seen.count(enum_desc))
Primiano Tucci10613fa2019-10-15 17:25:51 +0100215 return;
Eric Seckler0230ace2019-10-30 12:33:06 +0000216 all_enums_seen.insert(enum_desc);
Eric Secklerd85805a2019-10-30 08:52:15 +0000217 all_enums.push_back(enum_desc);
Primiano Tucci10613fa2019-10-15 17:25:51 +0100218 if (enum_desc->file() == file)
Eric Secklerd85805a2019-10-30 08:52:15 +0000219 local_enums.push_back(enum_desc);
Primiano Tucci10613fa2019-10-15 17:25:51 +0100220 };
221
Primiano Tuccife502c42019-12-11 01:00:27 +0000222 for (int i = 0; i < file->enum_type_count(); i++)
223 add_enum(file->enum_type(i));
224
Primiano Tucci10613fa2019-10-15 17:25:51 +0100225 std::stack<const Descriptor*> recursion_stack;
226 for (int i = 0; i < file->message_type_count(); i++)
227 recursion_stack.push(file->message_type(i));
228
229 while (!recursion_stack.empty()) {
230 const Descriptor* msg = recursion_stack.top();
231 recursion_stack.pop();
Eric Seckler0230ace2019-10-30 12:33:06 +0000232 if (all_types_seen.count(msg))
Primiano Tucci10613fa2019-10-15 17:25:51 +0100233 continue;
Eric Seckler0230ace2019-10-30 12:33:06 +0000234 all_types_seen.insert(msg);
Eric Secklerd85805a2019-10-30 08:52:15 +0000235 all_types.push_back(msg);
Primiano Tucci10613fa2019-10-15 17:25:51 +0100236 if (msg->file() == file)
Eric Secklerd85805a2019-10-30 08:52:15 +0000237 local_types.push_back(msg);
Primiano Tucci10613fa2019-10-15 17:25:51 +0100238
239 for (int i = 0; i < msg->nested_type_count(); i++)
240 recursion_stack.push(msg->nested_type(i));
241
242 for (int i = 0; i < msg->enum_type_count(); i++)
243 add_enum(msg->enum_type(i));
244
245 for (int i = 0; i < msg->field_count(); i++) {
246 const FieldDescriptor* field = msg->field(i);
247 if (field->has_default_value()) {
248 *error = "field " + field->name() +
249 ": Explicitly declared default values are not supported";
250 return false;
251 }
252 if (field->options().lazy() &&
253 (field->is_repeated() || field->type() != TYPE_MESSAGE)) {
254 *error = "[lazy=true] is supported only on non-repeated fields\n";
255 return false;
256 }
257
258 if (field->type() == TYPE_MESSAGE && !field->options().lazy())
259 recursion_stack.push(field->message_type());
260
261 if (field->type() == FieldDescriptor::TYPE_ENUM)
262 add_enum(field->enum_type());
263 }
264 } // while (!recursion_stack.empty())
265
266 // Generate forward declarations in the header for proto types.
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000267 // Note: do NOT add #includes to other generated headers (either .gen.h or
268 // .pbzero.h). Doing so is extremely hard to handle at the build-system level
269 // and requires propagating public_deps everywhere.
270 cc_printer.Print("\n");
Primiano Tucci10613fa2019-10-15 17:25:51 +0100271
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000272 // -- Begin of fwd declarations.
Primiano Tucci10613fa2019-10-15 17:25:51 +0100273
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000274 // Build up the map of forward declarations.
275 std::multimap<std::string /*namespace*/, std::string /*decl*/> fwd_decls;
276 enum FwdType { kClass, kEnum };
277 auto add_fwd_decl = [&fwd_decls](FwdType cpp_type,
278 const std::string& full_name) {
279 auto dot = full_name.rfind("::");
280 PERFETTO_CHECK(dot != std::string::npos);
281 auto package = full_name.substr(0, dot);
282 auto name = full_name.substr(dot + 2);
283 if (cpp_type == kClass) {
284 fwd_decls.emplace(package, "class " + name + ";");
285 } else {
286 PERFETTO_CHECK(cpp_type == kEnum);
287 fwd_decls.emplace(package, "enum " + name + " : int;");
288 }
289 };
Primiano Tucci10613fa2019-10-15 17:25:51 +0100290
Primiano Tucci19121602019-11-21 15:39:09 +0000291 add_fwd_decl(kClass, "protozero::Message");
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000292 for (const Descriptor* msg : all_types) {
293 add_fwd_decl(kClass, GetFullName(msg, true));
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000294 }
Primiano Tucci10613fa2019-10-15 17:25:51 +0100295 for (const EnumDescriptor* enm : all_enums) {
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000296 add_fwd_decl(kEnum, GetFullName(enm, true));
Primiano Tucci10613fa2019-10-15 17:25:51 +0100297 }
298
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000299 // Emit forward declarations grouping by package.
300 std::string last_package;
301 auto close_last_package = [&last_package, &h_printer] {
302 if (!last_package.empty()) {
303 for (const std::string& ns : SplitString(last_package, "::"))
304 h_printer.Print("} // namespace $ns$\n", "ns", ns);
305 h_printer.Print("\n");
306 }
307 };
308 for (const auto& kv : fwd_decls) {
309 const std::string& package = kv.first;
310 if (package != last_package) {
311 close_last_package();
312 last_package = package;
313 for (const std::string& ns : SplitString(package, "::"))
314 h_printer.Print("namespace $ns$ {\n", "ns", ns);
315 }
316 h_printer.Print("$decl$\n", "decl", kv.second);
317 }
318 close_last_package();
319
320 // -- End of fwd declarations.
321
Primiano Tuccie8020f92019-11-26 13:24:01 +0000322 for (const std::string& ns : GetNamespaces(file)) {
323 h_printer.Print("namespace $n$ {\n", "n", ns);
324 cc_printer.Print("namespace $n$ {\n", "n", ns);
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000325 }
Primiano Tucci10613fa2019-10-15 17:25:51 +0100326
327 // Generate declarations and definitions.
328 for (const EnumDescriptor* enm : local_enums)
329 GenEnum(enm, &h_printer);
330
331 for (const Descriptor* msg : local_types) {
332 GenClassDecl(msg, &h_printer);
333 GenClassDef(msg, &cc_printer);
334 }
335
Primiano Tuccie8020f92019-11-26 13:24:01 +0000336 for (const std::string& ns : GetNamespaces(file)) {
337 h_printer.Print("} // namespace $n$\n", "n", ns);
338 cc_printer.Print("} // namespace $n$\n", "n", ns);
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000339 }
Primiano Tucci63201702020-12-04 17:38:12 +0100340 cc_printer.Print("#if defined(__GNUC__) || defined(__clang__)\n");
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000341 cc_printer.Print("#pragma GCC diagnostic pop\n");
Primiano Tucci63201702020-12-04 17:38:12 +0100342 cc_printer.Print("#endif\n");
343
Primiano Tucci10613fa2019-10-15 17:25:51 +0100344 h_printer.Print("\n#endif // $g$\n", "g", include_guard);
345
346 return true;
347}
348
349std::string CppObjGenerator::GetCppType(const FieldDescriptor* field,
Primiano Tuccie8020f92019-11-26 13:24:01 +0000350 bool constref) const {
Primiano Tucci10613fa2019-10-15 17:25:51 +0100351 switch (field->type()) {
352 case FieldDescriptor::TYPE_DOUBLE:
353 return "double";
354 case FieldDescriptor::TYPE_FLOAT:
355 return "float";
356 case FieldDescriptor::TYPE_FIXED32:
357 case FieldDescriptor::TYPE_UINT32:
358 return "uint32_t";
359 case FieldDescriptor::TYPE_SFIXED32:
360 case FieldDescriptor::TYPE_INT32:
361 case FieldDescriptor::TYPE_SINT32:
362 return "int32_t";
363 case FieldDescriptor::TYPE_FIXED64:
364 case FieldDescriptor::TYPE_UINT64:
365 return "uint64_t";
366 case FieldDescriptor::TYPE_SFIXED64:
367 case FieldDescriptor::TYPE_SINT64:
368 case FieldDescriptor::TYPE_INT64:
369 return "int64_t";
370 case FieldDescriptor::TYPE_BOOL:
371 return "bool";
372 case FieldDescriptor::TYPE_STRING:
373 case FieldDescriptor::TYPE_BYTES:
374 return constref ? "const std::string&" : "std::string";
375 case FieldDescriptor::TYPE_MESSAGE:
376 assert(!field->options().lazy());
377 return constref ? "const " + GetFullName(field->message_type()) + "&"
378 : GetFullName(field->message_type());
379 case FieldDescriptor::TYPE_ENUM:
380 return GetFullName(field->enum_type());
381 case FieldDescriptor::TYPE_GROUP:
382 abort();
383 }
384 abort(); // for gcc
385}
386
Primiano Tuccie8020f92019-11-26 13:24:01 +0000387std::string CppObjGenerator::GetProtozeroSetter(
388 const FieldDescriptor* field) const {
Primiano Tucci19121602019-11-21 15:39:09 +0000389 switch (field->type()) {
390 case FieldDescriptor::TYPE_BOOL:
391 return "AppendTinyVarInt";
392 case FieldDescriptor::TYPE_INT32:
393 case FieldDescriptor::TYPE_INT64:
394 case FieldDescriptor::TYPE_UINT32:
395 case FieldDescriptor::TYPE_UINT64:
396 case FieldDescriptor::TYPE_ENUM:
397 return "AppendVarInt";
398 case FieldDescriptor::TYPE_SINT32:
399 case FieldDescriptor::TYPE_SINT64:
400 return "AppendSignedVarInt";
401 case FieldDescriptor::TYPE_FIXED32:
402 case FieldDescriptor::TYPE_FIXED64:
403 case FieldDescriptor::TYPE_SFIXED32:
404 case FieldDescriptor::TYPE_SFIXED64:
405 case FieldDescriptor::TYPE_FLOAT:
406 case FieldDescriptor::TYPE_DOUBLE:
407 return "AppendFixed";
408 case FieldDescriptor::TYPE_STRING:
409 case FieldDescriptor::TYPE_BYTES:
410 return "AppendString";
411 case FieldDescriptor::TYPE_GROUP:
412 case FieldDescriptor::TYPE_MESSAGE:
413 abort();
414 }
415 abort();
416}
417
Primiano Tuccie8020f92019-11-26 13:24:01 +0000418std::string CppObjGenerator::GetPackedBuffer(
419 const FieldDescriptor* field) const {
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000420 switch (field->type()) {
421 case FieldDescriptor::TYPE_FIXED32:
422 return "::protozero::PackedFixedSizeInt<uint32_t>";
423 case FieldDescriptor::TYPE_SFIXED32:
424 return "::protozero::PackedFixedSizeInt<int32_t>";
425 case FieldDescriptor::TYPE_FIXED64:
426 return "::protozero::PackedFixedSizeInt<uint64_t>";
427 case FieldDescriptor::TYPE_SFIXED64:
428 return "::protozero::PackedFixedSizeInt<int64_t>";
429 case FieldDescriptor::TYPE_DOUBLE:
430 return "::protozero::PackedFixedSizeInt<double>";
431 case FieldDescriptor::TYPE_FLOAT:
432 return "::protozero::PackedFixedSizeInt<float>";
433 case FieldDescriptor::TYPE_INT32:
434 case FieldDescriptor::TYPE_SINT32:
435 case FieldDescriptor::TYPE_UINT32:
436 case FieldDescriptor::TYPE_INT64:
437 case FieldDescriptor::TYPE_UINT64:
438 case FieldDescriptor::TYPE_SINT64:
439 case FieldDescriptor::TYPE_BOOL:
440 return "::protozero::PackedVarInt";
441 case FieldDescriptor::TYPE_STRING:
442 case FieldDescriptor::TYPE_BYTES:
443 case FieldDescriptor::TYPE_MESSAGE:
444 case FieldDescriptor::TYPE_ENUM:
445 case FieldDescriptor::TYPE_GROUP:
446 break; // Will abort()
447 }
448 abort();
449}
450
Primiano Tuccie8020f92019-11-26 13:24:01 +0000451std::string CppObjGenerator::GetPackedWireType(
452 const FieldDescriptor* field) const {
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000453 switch (field->type()) {
454 case FieldDescriptor::TYPE_FIXED32:
455 case FieldDescriptor::TYPE_SFIXED32:
456 case FieldDescriptor::TYPE_FLOAT:
457 return "::protozero::proto_utils::ProtoWireType::kFixed32";
458 case FieldDescriptor::TYPE_FIXED64:
459 case FieldDescriptor::TYPE_SFIXED64:
460 case FieldDescriptor::TYPE_DOUBLE:
461 return "::protozero::proto_utils::ProtoWireType::kFixed64";
462 case FieldDescriptor::TYPE_INT32:
463 case FieldDescriptor::TYPE_SINT32:
464 case FieldDescriptor::TYPE_UINT32:
465 case FieldDescriptor::TYPE_INT64:
466 case FieldDescriptor::TYPE_UINT64:
467 case FieldDescriptor::TYPE_SINT64:
468 case FieldDescriptor::TYPE_BOOL:
469 return "::protozero::proto_utils::ProtoWireType::kVarInt";
470 case FieldDescriptor::TYPE_STRING:
471 case FieldDescriptor::TYPE_BYTES:
472 case FieldDescriptor::TYPE_MESSAGE:
473 case FieldDescriptor::TYPE_ENUM:
474 case FieldDescriptor::TYPE_GROUP:
475 break; // Will abort()
476 }
477 abort();
478}
479
Primiano Tucci10613fa2019-10-15 17:25:51 +0100480void CppObjGenerator::GenEnum(const EnumDescriptor* enum_desc,
481 Printer* p) const {
482 std::string full_name = GetFullName(enum_desc);
Primiano Tucciba784e52019-11-13 07:04:52 -0800483
484 // When generating enums, there are two cases:
485 // 1. Enums nested in a message (most frequent case), e.g.:
486 // message MyMsg { enum MyEnum { FOO=1; BAR=2; } }
487 // 2. Enum defined at the package level, outside of any message.
488 //
489 // In the case 1, the C++ code generated by the official protobuf library is:
490 // enum MyEnum { MyMsg_MyEnum_FOO=1, MyMsg_MyEnum_BAR=2 }
491 // class MyMsg { static const auto FOO = MyMsg_MyEnum_FOO; ... same for BAR }
492 //
493 // In the case 2, the C++ code is simply:
494 // enum MyEnum { FOO=1, BAR=2 }
495 // Hence this |prefix| logic.
496 std::string prefix = enum_desc->containing_type() ? full_name + "_" : "";
Primiano Tucci10613fa2019-10-15 17:25:51 +0100497 p->Print("enum $f$ : int {\n", "f", full_name);
498 for (int e = 0; e < enum_desc->value_count(); e++) {
499 const EnumValueDescriptor* value = enum_desc->value(e);
Primiano Tucciba784e52019-11-13 07:04:52 -0800500 p->Print(" $p$$n$ = $v$,\n", "p", prefix, "n", value->name(), "v",
Primiano Tucci10613fa2019-10-15 17:25:51 +0100501 std::to_string(value->number()));
502 }
503 p->Print("};\n");
504}
505
506void CppObjGenerator::GenEnumAliases(const EnumDescriptor* enum_desc,
507 Printer* p) const {
Primiano Tuccif5a94cc2020-03-26 10:02:13 +0000508 int min_value = std::numeric_limits<int>::max();
509 int max_value = std::numeric_limits<int>::min();
510 std::string min_name;
511 std::string max_name;
Primiano Tucci10613fa2019-10-15 17:25:51 +0100512 std::string full_name = GetFullName(enum_desc);
513 for (int e = 0; e < enum_desc->value_count(); e++) {
514 const EnumValueDescriptor* value = enum_desc->value(e);
515 p->Print("static constexpr auto $n$ = $f$_$n$;\n", "f", full_name, "n",
516 value->name());
Primiano Tuccif5a94cc2020-03-26 10:02:13 +0000517 if (value->number() < min_value) {
518 min_value = value->number();
519 min_name = full_name + "_" + value->name();
520 }
521 if (value->number() > max_value) {
522 max_value = value->number();
523 max_name = full_name + "_" + value->name();
524 }
Primiano Tucci10613fa2019-10-15 17:25:51 +0100525 }
Primiano Tuccif5a94cc2020-03-26 10:02:13 +0000526 p->Print("static constexpr auto $n$_MIN = $m$;\n", "n", enum_desc->name(),
527 "m", min_name);
528 p->Print("static constexpr auto $n$_MAX = $m$;\n", "n", enum_desc->name(),
529 "m", max_name);
Primiano Tucci10613fa2019-10-15 17:25:51 +0100530}
531
532void CppObjGenerator::GenClassDecl(const Descriptor* msg, Printer* p) const {
533 std::string full_name = GetFullName(msg);
Primiano Tuccicb8cea22019-11-22 11:05:35 +0000534 p->Print(
Daniele Di Proiettoadaabb62022-04-26 13:27:24 +0100535 "\nclass PERFETTO_COMPONENT_EXPORT $n$ : public "
536 "::protozero::CppMessageObj {\n",
Primiano Tuccicb8cea22019-11-22 11:05:35 +0000537 "n", full_name);
Primiano Tucci10613fa2019-10-15 17:25:51 +0100538 p->Print(" public:\n");
539 p->Indent();
540
541 // Do a first pass to generate aliases for nested types.
542 // e.g., using Foo = Parent_Foo;
543 for (int i = 0; i < msg->nested_type_count(); i++) {
544 const Descriptor* nested_msg = msg->nested_type(i);
545 p->Print("using $n$ = $f$;\n", "n", nested_msg->name(), "f",
546 GetFullName(nested_msg));
547 }
548 for (int i = 0; i < msg->enum_type_count(); i++) {
549 const EnumDescriptor* nested_enum = msg->enum_type(i);
550 p->Print("using $n$ = $f$;\n", "n", nested_enum->name(), "f",
551 GetFullName(nested_enum));
552 GenEnumAliases(nested_enum, p);
553 }
554
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000555 // Generate constants with field numbers.
556 p->Print("enum FieldNumbers {\n");
557 for (int i = 0; i < msg->field_count(); i++) {
558 const FieldDescriptor* field = msg->field(i);
559 std::string name = field->camelcase_name();
560 name[0] = perfetto::base::Uppercase(name[0]);
561 p->Print(" k$n$FieldNumber = $num$,\n", "n", name, "num",
562 std::to_string(field->number()));
563 }
564 p->Print("};\n\n");
565
Primiano Tucci10613fa2019-10-15 17:25:51 +0100566 p->Print("$n$();\n", "n", full_name);
Primiano Tuccicb8cea22019-11-22 11:05:35 +0000567 p->Print("~$n$() override;\n", "n", full_name);
Primiano Tucci10613fa2019-10-15 17:25:51 +0100568 p->Print("$n$($n$&&) noexcept;\n", "n", full_name);
569 p->Print("$n$& operator=($n$&&);\n", "n", full_name);
570 p->Print("$n$(const $n$&);\n", "n", full_name);
571 p->Print("$n$& operator=(const $n$&);\n", "n", full_name);
572 p->Print("bool operator==(const $n$&) const;\n", "n", full_name);
573 p->Print(
574 "bool operator!=(const $n$& other) const { return !(*this == other); }\n",
575 "n", full_name);
576 p->Print("\n");
577
578 std::string proto_type = GetFullName(msg, true);
Primiano Tuccicb8cea22019-11-22 11:05:35 +0000579 p->Print("bool ParseFromArray(const void*, size_t) override;\n");
580 p->Print("std::string SerializeAsString() const override;\n");
581 p->Print("std::vector<uint8_t> SerializeAsArray() const override;\n");
Primiano Tucci19121602019-11-21 15:39:09 +0000582 p->Print("void Serialize(::protozero::Message*) const;\n");
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000583
Primiano Tucci10613fa2019-10-15 17:25:51 +0100584 // Generate accessors.
585 for (int i = 0; i < msg->field_count(); i++) {
586 const FieldDescriptor* field = msg->field(i);
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000587 auto set_bit = "_has_field_.set(" + std::to_string(field->number()) + ")";
Primiano Tucci10613fa2019-10-15 17:25:51 +0100588 p->Print("\n");
589 if (field->options().lazy()) {
590 p->Print("const std::string& $n$_raw() const { return $n$_; }\n", "n",
591 field->lowercase_name());
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000592 p->Print(
593 "void set_$n$_raw(const std::string& raw) { $n$_ = raw; $s$; }\n",
594 "n", field->lowercase_name(), "s", set_bit);
Primiano Tucci10613fa2019-10-15 17:25:51 +0100595 } else if (!field->is_repeated()) {
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000596 p->Print("bool has_$n$() const { return _has_field_[$bit$]; }\n", "n",
597 field->lowercase_name(), "bit", std::to_string(field->number()));
Primiano Tucci10613fa2019-10-15 17:25:51 +0100598 if (field->type() == TYPE_MESSAGE) {
599 p->Print("$t$ $n$() const { return *$n$_; }\n", "t",
600 GetCppType(field, true), "n", field->lowercase_name());
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000601 p->Print("$t$* mutable_$n$() { $s$; return $n$_.get(); }\n", "t",
602 GetCppType(field, false), "n", field->lowercase_name(), "s",
603 set_bit);
Primiano Tucci10613fa2019-10-15 17:25:51 +0100604 } else {
605 p->Print("$t$ $n$() const { return $n$_; }\n", "t",
606 GetCppType(field, true), "n", field->lowercase_name());
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000607 p->Print("void set_$n$($t$ value) { $n$_ = value; $s$; }\n", "t",
608 GetCppType(field, true), "n", field->lowercase_name(), "s",
609 set_bit);
Primiano Tucci10613fa2019-10-15 17:25:51 +0100610 if (field->type() == FieldDescriptor::TYPE_BYTES) {
611 p->Print(
612 "void set_$n$(const void* p, size_t s) { "
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000613 "$n$_.assign(reinterpret_cast<const char*>(p), s); $s$; }\n",
614 "n", field->lowercase_name(), "s", set_bit);
Primiano Tucci10613fa2019-10-15 17:25:51 +0100615 }
616 }
617 } else { // is_repeated()
Primiano Tucci10613fa2019-10-15 17:25:51 +0100618 p->Print("const std::vector<$t$>& $n$() const { return $n$_; }\n", "t",
619 GetCppType(field, false), "n", field->lowercase_name());
620 p->Print("std::vector<$t$>* mutable_$n$() { return &$n$_; }\n", "t",
621 GetCppType(field, false), "n", field->lowercase_name());
Sami Kyostila61412fc2020-09-14 13:37:51 +0100622
623 // Generate accessors for repeated message types in the .cc file so that
624 // the header doesn't depend on the full definition of all nested types.
625 if (field->type() == TYPE_MESSAGE) {
626 p->Print("int $n$_size() const;\n", "t", GetCppType(field, false), "n",
627 field->lowercase_name());
628 p->Print("void clear_$n$();\n", "n", field->lowercase_name());
629 p->Print("$t$* add_$n$();\n", "t", GetCppType(field, false), "n",
630 field->lowercase_name());
631 } else { // Primitive type.
632 p->Print(
633 "int $n$_size() const { return static_cast<int>($n$_.size()); }\n",
634 "t", GetCppType(field, false), "n", field->lowercase_name());
635 p->Print("void clear_$n$() { $n$_.clear(); }\n", "n",
636 field->lowercase_name());
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000637 p->Print("void add_$n$($t$ value) { $n$_.emplace_back(value); }\n", "t",
638 GetCppType(field, false), "n", field->lowercase_name());
Sami Kyostila61412fc2020-09-14 13:37:51 +0100639 // TODO(primiano): this should be done only for TYPE_MESSAGE.
640 // Unfortuntely we didn't realize before and now we have a bunch of code
641 // that does: *msg->add_int_value() = 42 instead of
642 // msg->add_int_value(42).
643 p->Print(
644 "$t$* add_$n$() { $n$_.emplace_back(); return &$n$_.back(); }\n",
645 "t", GetCppType(field, false), "n", field->lowercase_name());
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000646 }
Primiano Tucci10613fa2019-10-15 17:25:51 +0100647 }
648 }
649 p->Outdent();
650 p->Print("\n private:\n");
651 p->Indent();
652
653 // Generate fields.
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000654 int max_field_id = 1;
Primiano Tucci10613fa2019-10-15 17:25:51 +0100655 for (int i = 0; i < msg->field_count(); i++) {
656 const FieldDescriptor* field = msg->field(i);
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000657 max_field_id = std::max(max_field_id, field->number());
Primiano Tucci10613fa2019-10-15 17:25:51 +0100658 if (field->options().lazy()) {
659 p->Print("std::string $n$_; // [lazy=true]\n", "n",
660 field->lowercase_name());
661 } else if (!field->is_repeated()) {
662 std::string type = GetCppType(field, false);
663 if (field->type() == TYPE_MESSAGE) {
Primiano Tuccie4144b72019-11-07 16:10:19 +0000664 type = "::protozero::CopyablePtr<" + type + ">";
Primiano Tucci10613fa2019-10-15 17:25:51 +0100665 p->Print("$t$ $n$_;\n", "t", type, "n", field->lowercase_name());
666 } else {
667 p->Print("$t$ $n$_{};\n", "t", type, "n", field->lowercase_name());
668 }
669 } else { // is_repeated()
670 p->Print("std::vector<$t$> $n$_;\n", "t", GetCppType(field, false), "n",
671 field->lowercase_name());
672 }
673 }
674 p->Print("\n");
675 p->Print("// Allows to preserve unknown protobuf fields for compatibility\n");
676 p->Print("// with future versions of .proto files.\n");
677 p->Print("std::string unknown_fields_;\n");
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000678
679 p->Print("\nstd::bitset<$id$> _has_field_{};\n", "id",
680 std::to_string(max_field_id + 1));
681
Primiano Tucci10613fa2019-10-15 17:25:51 +0100682 p->Outdent();
683 p->Print("};\n\n");
684}
685
686void CppObjGenerator::GenClassDef(const Descriptor* msg, Printer* p) const {
687 p->Print("\n");
688 std::string full_name = GetFullName(msg);
689
690 p->Print("$n$::$n$() = default;\n", "n", full_name);
691 p->Print("$n$::~$n$() = default;\n", "n", full_name);
692 p->Print("$n$::$n$(const $n$&) = default;\n", "n", full_name);
693 p->Print("$n$& $n$::operator=(const $n$&) = default;\n", "n", full_name);
694 p->Print("$n$::$n$($n$&&) noexcept = default;\n", "n", full_name);
695 p->Print("$n$& $n$::operator=($n$&&) = default;\n", "n", full_name);
696
697 p->Print("\n");
698
699 // Comparison operator
700 p->Print("bool $n$::operator==(const $n$& other) const {\n", "n", full_name);
701 p->Indent();
702
703 p->Print("return unknown_fields_ == other.unknown_fields_");
704 for (int i = 0; i < msg->field_count(); i++)
705 p->Print("\n && $n$_ == other.$n$_", "n", msg->field(i)->lowercase_name());
706 p->Print(";");
707 p->Outdent();
708 p->Print("\n}\n\n");
709
Sami Kyostila61412fc2020-09-14 13:37:51 +0100710 // Accessors for repeated message fields.
711 for (int i = 0; i < msg->field_count(); i++) {
712 const FieldDescriptor* field = msg->field(i);
713 if (field->options().lazy() || !field->is_repeated() ||
714 field->type() != TYPE_MESSAGE) {
715 continue;
716 }
717 p->Print(
718 "int $c$::$n$_size() const { return static_cast<int>($n$_.size()); }\n",
719 "c", full_name, "t", GetCppType(field, false), "n",
720 field->lowercase_name());
721 p->Print("void $c$::clear_$n$() { $n$_.clear(); }\n", "c", full_name, "n",
722 field->lowercase_name());
723 p->Print(
724 "$t$* $c$::add_$n$() { $n$_.emplace_back(); return &$n$_.back(); }\n",
725 "c", full_name, "t", GetCppType(field, false), "n",
726 field->lowercase_name());
727 }
728
Primiano Tucci10613fa2019-10-15 17:25:51 +0100729 std::string proto_type = GetFullName(msg, true);
730
Primiano Tuccicb8cea22019-11-22 11:05:35 +0000731 // Generate the ParseFromArray() method definition.
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000732 p->Print("bool $f$::ParseFromArray(const void* raw, size_t size) {\n", "f",
Primiano Tucci10613fa2019-10-15 17:25:51 +0100733 full_name);
734 p->Indent();
Primiano Tucci10613fa2019-10-15 17:25:51 +0100735 for (int i = 0; i < msg->field_count(); i++) {
Primiano Tucci10613fa2019-10-15 17:25:51 +0100736 const FieldDescriptor* field = msg->field(i);
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000737 if (field->is_repeated())
Primiano Tucci10613fa2019-10-15 17:25:51 +0100738 p->Print("$n$_.clear();\n", "n", field->lowercase_name());
Primiano Tucci10613fa2019-10-15 17:25:51 +0100739 }
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000740 p->Print("unknown_fields_.clear();\n");
741 p->Print("bool packed_error = false;\n");
742 p->Print("\n");
743 p->Print("::protozero::ProtoDecoder dec(raw, size);\n");
744 p->Print("for (auto field = dec.ReadField(); field.valid(); ");
745 p->Print("field = dec.ReadField()) {\n");
746 p->Indent();
747 p->Print("if (field.id() < _has_field_.size()) {\n");
748 p->Print(" _has_field_.set(field.id());\n");
749 p->Print("}\n");
750 p->Print("switch (field.id()) {\n");
751 p->Indent();
752 for (int i = 0; i < msg->field_count(); i++) {
753 const FieldDescriptor* field = msg->field(i);
754 p->Print("case $id$ /* $n$ */:\n", "id", std::to_string(field->number()),
755 "n", field->lowercase_name());
756 p->Indent();
757 if (field->options().lazy()) {
758 p->Print("$n$_ = field.as_std_string();\n", "n", field->lowercase_name());
759 } else {
760 std::string statement;
761 if (field->type() == TYPE_MESSAGE) {
Sami Kyostilab052f012020-11-23 13:01:43 +0000762 statement = "$rval$.ParseFromArray(field.data(), field.size());\n";
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000763 } else {
764 if (field->type() == TYPE_SINT32 || field->type() == TYPE_SINT64) {
765 // sint32/64 fields are special and need to be zig-zag-decoded.
766 statement = "field.get_signed(&$rval$);\n";
767 } else {
768 statement = "field.get(&$rval$);\n";
769 }
770 }
771 if (field->is_packed()) {
772 PERFETTO_CHECK(field->is_repeated());
773 if (field->type() == TYPE_SINT32 || field->type() == TYPE_SINT64) {
774 PERFETTO_FATAL("packed signed (zigzag) fields are not supported");
775 }
776 p->Print(
777 "for (::protozero::PackedRepeatedFieldIterator<$w$, $c$> "
778 "rep(field.data(), field.size(), &packed_error); rep; ++rep) {\n",
779 "w", GetPackedWireType(field), "c", GetCppType(field, false));
780 p->Print(" $n$_.emplace_back(*rep);\n", "n", field->lowercase_name());
781 p->Print("}\n");
782 } else if (field->is_repeated()) {
783 p->Print("$n$_.emplace_back();\n", "n", field->lowercase_name());
784 p->Print(statement.c_str(), "rval",
785 field->lowercase_name() + "_.back()");
786 } else if (field->type() == TYPE_MESSAGE) {
787 p->Print(statement.c_str(), "rval",
788 "(*" + field->lowercase_name() + "_)");
789 } else {
790 p->Print(statement.c_str(), "rval", field->lowercase_name() + "_");
791 }
792 }
793 p->Print("break;\n");
794 p->Outdent();
795 } // for (field)
796 p->Print("default:\n");
797 p->Print(" field.SerializeAndAppendTo(&unknown_fields_);\n");
798 p->Print(" break;\n");
799 p->Outdent();
800 p->Print("}\n"); // switch(field.id)
801 p->Outdent();
802 p->Print("}\n"); // for(field)
803 p->Print("return !packed_error && !dec.bytes_left();\n"); // for(field)
Primiano Tucci10613fa2019-10-15 17:25:51 +0100804 p->Outdent();
805 p->Print("}\n\n");
806
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000807 // Generate the SerializeAsString() method definition.
808 p->Print("std::string $f$::SerializeAsString() const {\n", "f", full_name);
Primiano Tucci10613fa2019-10-15 17:25:51 +0100809 p->Indent();
Primiano Tucci19121602019-11-21 15:39:09 +0000810 p->Print("::protozero::HeapBuffered<::protozero::Message> msg;\n");
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000811 p->Print("Serialize(msg.get());\n");
812 p->Print("return msg.SerializeAsString();\n");
813 p->Outdent();
814 p->Print("}\n\n");
815
816 // Generate the SerializeAsArray() method definition.
817 p->Print("std::vector<uint8_t> $f$::SerializeAsArray() const {\n", "f",
818 full_name);
819 p->Indent();
Primiano Tucci19121602019-11-21 15:39:09 +0000820 p->Print("::protozero::HeapBuffered<::protozero::Message> msg;\n");
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000821 p->Print("Serialize(msg.get());\n");
822 p->Print("return msg.SerializeAsArray();\n");
823 p->Outdent();
824 p->Print("}\n\n");
825
Primiano Tucci19121602019-11-21 15:39:09 +0000826 // Generate the Serialize() method that writes the fields into the passed
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000827 // protozero |msg| write-only interface |msg|.
Primiano Tucci19121602019-11-21 15:39:09 +0000828 p->Print("void $f$::Serialize(::protozero::Message* msg) const {\n", "f",
829 full_name);
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000830 p->Indent();
Primiano Tucci10613fa2019-10-15 17:25:51 +0100831 for (int i = 0; i < msg->field_count(); i++) {
Primiano Tucci10613fa2019-10-15 17:25:51 +0100832 const FieldDescriptor* field = msg->field(i);
Primiano Tucci19121602019-11-21 15:39:09 +0000833 std::map<std::string, std::string> args;
834 args["id"] = std::to_string(field->number());
835 args["n"] = field->lowercase_name();
836 p->Print(args, "// Field $id$: $n$\n");
837 if (field->is_packed()) {
838 PERFETTO_CHECK(field->is_repeated());
839 p->Print("{\n");
840 p->Indent();
841 p->Print("$p$ pack;\n", "p", GetPackedBuffer(field));
842 p->Print(args, "for (auto& it : $n$_)\n");
843 p->Print(args, " pack.Append(it);\n");
844 p->Print(args, "msg->AppendBytes($id$, pack.data(), pack.size());\n");
845 p->Outdent();
846 p->Print("}\n");
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000847 } else {
Primiano Tucci19121602019-11-21 15:39:09 +0000848 if (field->is_repeated()) {
849 p->Print(args, "for (auto& it : $n$_) {\n");
850 args["lvalue"] = "it";
851 args["rvalue"] = "it";
852 } else {
853 p->Print(args, "if (_has_field_[$id$]) {\n");
854 args["lvalue"] = "(*" + field->lowercase_name() + "_)";
855 args["rvalue"] = field->lowercase_name() + "_";
Primiano Tucci10613fa2019-10-15 17:25:51 +0100856 }
Primiano Tucci19121602019-11-21 15:39:09 +0000857 p->Indent();
858 if (field->options().lazy()) {
859 p->Print(args, "msg->AppendString($id$, $rvalue$);\n");
860 } else if (field->type() == TYPE_MESSAGE) {
861 p->Print(args,
862 "$lvalue$.Serialize("
863 "msg->BeginNestedMessage<::protozero::Message>($id$));\n");
864 } else {
865 args["setter"] = GetProtozeroSetter(field);
866 p->Print(args, "msg->$setter$($id$, $rvalue$);\n");
867 }
868 p->Outdent();
869 p->Print("}\n");
Primiano Tucci10613fa2019-10-15 17:25:51 +0100870 }
Primiano Tucci19121602019-11-21 15:39:09 +0000871
872 p->Print("\n");
Primiano Tucci6a7813d2019-11-19 11:08:50 +0000873 } // for (field)
874 p->Print(
875 "msg->AppendRawProtoBytes(unknown_fields_.data(), "
876 "unknown_fields_.size());\n");
Primiano Tucci10613fa2019-10-15 17:25:51 +0100877 p->Outdent();
878 p->Print("}\n\n");
879}
880
881} // namespace
882} // namespace protozero
883
884int main(int argc, char** argv) {
885 ::protozero::CppObjGenerator generator;
886 return google::protobuf::compiler::PluginMain(argc, argv, &generator);
887}