4 #include "absl/container/flat_hash_map.h"
5 #include "absl/strings/ascii.h"
6 #include "absl/strings/substitute.h"
7 #include "google/protobuf/compiler/code_generator.h"
8 #include "google/protobuf/compiler/plugin.h"
9 #include "google/protobuf/descriptor.h"
10 #include "google/protobuf/descriptor.pb.h"
11 #include "google/protobuf/wire_format.h"
12 #include "upbc/common.h"
13 #include "upbc/message_layout.h"
18 namespace protoc = ::google::protobuf::compiler;
19 namespace protobuf = ::google::protobuf;
21 std::string HeaderFilename(std::string proto_filename) {
22 return StripExtension(proto_filename) + ".upb.h";
25 std::string SourceFilename(std::string proto_filename) {
26 return StripExtension(proto_filename) + ".upb.c";
29 void AddEnums(const protobuf::Descriptor* message,
30 std::vector<const protobuf::EnumDescriptor*>* enums) {
31 for (int i = 0; i < message->enum_type_count(); i++) {
32 enums->push_back(message->enum_type(i));
34 for (int i = 0; i < message->nested_type_count(); i++) {
35 AddEnums(message->nested_type(i), enums);
40 void SortDefs(std::vector<T>* defs) {
41 std::sort(defs->begin(), defs->end(),
42 [](T a, T b) { return a->full_name() < b->full_name(); });
45 std::vector<const protobuf::EnumDescriptor*> SortedEnums(
46 const protobuf::FileDescriptor* file) {
47 std::vector<const protobuf::EnumDescriptor*> enums;
48 for (int i = 0; i < file->enum_type_count(); i++) {
49 enums.push_back(file->enum_type(i));
51 for (int i = 0; i < file->message_type_count(); i++) {
52 AddEnums(file->message_type(i), &enums);
58 std::vector<const protobuf::FieldDescriptor*> FieldNumberOrder(
59 const protobuf::Descriptor* message) {
60 std::vector<const protobuf::FieldDescriptor*> fields;
61 for (int i = 0; i < message->field_count(); i++) {
62 fields.push_back(message->field(i));
64 std::sort(fields.begin(), fields.end(),
65 [](const protobuf::FieldDescriptor* a,
66 const protobuf::FieldDescriptor* b) {
67 return a->number() < b->number();
72 std::vector<const protobuf::FieldDescriptor*> SortedSubmessages(
73 const protobuf::Descriptor* message) {
74 std::vector<const protobuf::FieldDescriptor*> ret;
75 for (int i = 0; i < message->field_count(); i++) {
76 if (message->field(i)->cpp_type() ==
77 protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
78 ret.push_back(message->field(i));
81 std::sort(ret.begin(), ret.end(),
82 [](const protobuf::FieldDescriptor* a,
83 const protobuf::FieldDescriptor* b) {
84 return a->message_type()->full_name() <
85 b->message_type()->full_name();
90 std::string EnumValueSymbol(const protobuf::EnumValueDescriptor* value) {
91 return ToCIdent(value->full_name());
94 std::string GetSizeInit(const MessageLayout::Size& size) {
95 return absl::Substitute("UPB_SIZE($0, $1)", size.size32, size.size64);
98 std::string CTypeInternal(const protobuf::FieldDescriptor* field,
100 std::string maybe_const = is_const ? "const " : "";
101 switch (field->cpp_type()) {
102 case protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
103 std::string maybe_struct =
104 field->file() != field->message_type()->file() ? "struct " : "";
105 return maybe_const + maybe_struct + MessageName(field->message_type()) +
108 case protobuf::FieldDescriptor::CPPTYPE_BOOL:
110 case protobuf::FieldDescriptor::CPPTYPE_FLOAT:
112 case protobuf::FieldDescriptor::CPPTYPE_INT32:
113 case protobuf::FieldDescriptor::CPPTYPE_ENUM:
115 case protobuf::FieldDescriptor::CPPTYPE_UINT32:
117 case protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
119 case protobuf::FieldDescriptor::CPPTYPE_INT64:
121 case protobuf::FieldDescriptor::CPPTYPE_UINT64:
123 case protobuf::FieldDescriptor::CPPTYPE_STRING:
124 return "upb_strview";
126 fprintf(stderr, "Unexpected type");
131 std::string SizeLg2(const protobuf::FieldDescriptor* field) {
132 switch (field->cpp_type()) {
133 case protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
134 return "UPB_SIZE(2, 3)";
135 case protobuf::FieldDescriptor::CPPTYPE_ENUM:
136 return std::to_string(2);
137 case protobuf::FieldDescriptor::CPPTYPE_BOOL:
138 return std::to_string(1);
139 case protobuf::FieldDescriptor::CPPTYPE_FLOAT:
140 return std::to_string(2);
141 case protobuf::FieldDescriptor::CPPTYPE_INT32:
142 return std::to_string(2);
143 case protobuf::FieldDescriptor::CPPTYPE_UINT32:
144 return std::to_string(2);
145 case protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
146 return std::to_string(3);
147 case protobuf::FieldDescriptor::CPPTYPE_INT64:
148 return std::to_string(3);
149 case protobuf::FieldDescriptor::CPPTYPE_UINT64:
150 return std::to_string(3);
151 case protobuf::FieldDescriptor::CPPTYPE_STRING:
152 return "UPB_SIZE(3, 4)";
154 fprintf(stderr, "Unexpected type");
159 std::string FieldDefault(const protobuf::FieldDescriptor* field) {
160 switch (field->cpp_type()) {
161 case protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
163 case protobuf::FieldDescriptor::CPPTYPE_STRING:
164 return absl::Substitute("upb_strview_make(\"$0\", strlen(\"$0\"))",
165 absl::CEscape(field->default_value_string()));
166 case protobuf::FieldDescriptor::CPPTYPE_INT32:
167 return absl::StrCat(field->default_value_int32());
168 case protobuf::FieldDescriptor::CPPTYPE_INT64:
169 return absl::StrCat(field->default_value_int64());
170 case protobuf::FieldDescriptor::CPPTYPE_UINT32:
171 return absl::StrCat(field->default_value_uint32());
172 case protobuf::FieldDescriptor::CPPTYPE_UINT64:
173 return absl::StrCat(field->default_value_uint64());
174 case protobuf::FieldDescriptor::CPPTYPE_FLOAT:
175 return absl::StrCat(field->default_value_float());
176 case protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
177 return absl::StrCat(field->default_value_double());
178 case protobuf::FieldDescriptor::CPPTYPE_BOOL:
179 return field->default_value_bool() ? "true" : "false";
180 case protobuf::FieldDescriptor::CPPTYPE_ENUM:
181 // Use a number instead of a symbolic name so that we don't require
182 // this enum's header to be included.
183 return absl::StrCat(field->default_value_enum()->number());
189 std::string CType(const protobuf::FieldDescriptor* field) {
190 return CTypeInternal(field, false);
193 std::string CTypeConst(const protobuf::FieldDescriptor* field) {
194 return CTypeInternal(field, true);
197 void DumpEnumValues(const protobuf::EnumDescriptor* desc, Output& output) {
198 std::vector<const protobuf::EnumValueDescriptor*> values;
199 for (int i = 0; i < desc->value_count(); i++) {
200 values.push_back(desc->value(i));
202 std::sort(values.begin(), values.end(),
203 [](const protobuf::EnumValueDescriptor* a,
204 const protobuf::EnumValueDescriptor* b) {
205 return a->number() < b->number();
208 for (size_t i = 0; i < values.size(); i++) {
209 auto value = values[i];
210 output(" $0 = $1", EnumValueSymbol(value), value->number());
211 if (i != values.size() - 1) {
218 void GenerateMessageInHeader(const protobuf::Descriptor* message, Output& output) {
219 MessageLayout layout(message);
221 output("/* $0 */\n\n", message->full_name());
222 std::string msgname = ToCIdent(message->full_name());
224 if (!message->options().map_entry()) {
226 "UPB_INLINE $0 *$0_new(upb_arena *arena) {\n"
227 " return ($0 *)_upb_msg_new(&$1, arena);\n"
229 "UPB_INLINE $0 *$0_parse(const char *buf, size_t size,\n"
230 " upb_arena *arena) {\n"
231 " $0 *ret = $0_new(arena);\n"
232 " return (ret && upb_decode(buf, size, ret, &$1, arena)) ? ret : NULL;\n"
234 "UPB_INLINE $0 *$0_parse_ex(const char *buf, size_t size,\n"
235 " upb_arena *arena, int options) {\n"
236 " $0 *ret = $0_new(arena);\n"
237 " return (ret && _upb_decode(buf, size, ret, &$1, arena, options))\n"
240 "UPB_INLINE char *$0_serialize(const $0 *msg, upb_arena *arena, size_t "
242 " return upb_encode(msg, &$1, arena, len);\n"
245 MessageName(message), MessageInit(message));
248 for (int i = 0; i < message->real_oneof_decl_count(); i++) {
249 const protobuf::OneofDescriptor* oneof = message->oneof_decl(i);
250 std::string fullname = ToCIdent(oneof->full_name());
251 output("typedef enum {\n");
252 for (int j = 0; j < oneof->field_count(); j++) {
253 const protobuf::FieldDescriptor* field = oneof->field(j);
254 output(" $0_$1 = $2,\n", fullname, field->name(), field->number());
258 "} $0_oneofcases;\n",
261 "UPB_INLINE $0_oneofcases $1_$2_case(const $1* msg) { "
262 "return ($0_oneofcases)*UPB_PTR_AT(msg, $3, int32_t); }\n"
264 fullname, msgname, oneof->name(),
265 GetSizeInit(layout.GetOneofCaseOffset(oneof)));
268 // Generate const methods.
270 for (auto field : FieldNumberOrder(message)) {
271 // Generate hazzer (if any).
272 if (layout.HasHasbit(field)) {
274 "UPB_INLINE bool $0_has_$1(const $0 *msg) { "
275 "return _upb_hasbit(msg, $2); }\n",
276 msgname, field->name(), layout.GetHasbitIndex(field));
277 } else if (field->real_containing_oneof()) {
279 "UPB_INLINE bool $0_has_$1(const $0 *msg) { "
280 "return _upb_getoneofcase(msg, $2) == $3; }\n",
281 msgname, field->name(),
283 layout.GetOneofCaseOffset(field->real_containing_oneof())),
285 } else if (field->message_type()) {
287 "UPB_INLINE bool $0_has_$1(const $0 *msg) { "
288 "return _upb_has_submsg_nohasbit(msg, $2); }\n",
289 msgname, field->name(), GetSizeInit(layout.GetFieldOffset(field)));
293 if (field->is_map()) {
294 const protobuf::Descriptor* entry = field->message_type();
295 const protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1);
296 const protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2);
298 "UPB_INLINE size_t $0_$1_size(const $0 *msg) {"
299 "return _upb_msg_map_size(msg, $2); }\n",
300 msgname, field->name(), GetSizeInit(layout.GetFieldOffset(field)));
302 "UPB_INLINE bool $0_$1_get(const $0 *msg, $2 key, $3 *val) { "
303 "return _upb_msg_map_get(msg, $4, &key, $5, val, $6); }\n",
304 msgname, field->name(), CType(key), CType(val),
305 GetSizeInit(layout.GetFieldOffset(field)),
306 key->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING
309 val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING
313 "UPB_INLINE $0 $1_$2_next(const $1 *msg, size_t* iter) { "
314 "return ($0)_upb_msg_map_next(msg, $3, iter); }\n",
315 CTypeConst(field), msgname, field->name(),
316 GetSizeInit(layout.GetFieldOffset(field)));
317 } else if (message->options().map_entry()) {
319 "UPB_INLINE $0 $1_$2(const $1 *msg) {\n"
321 " _upb_msg_map_$2(msg, &ret, $4);\n"
324 CTypeConst(field), msgname, field->name(), CType(field),
325 field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING
328 } else if (field->is_repeated()) {
330 "UPB_INLINE $0 const* $1_$2(const $1 *msg, size_t *len) { "
331 "return ($0 const*)_upb_array_accessor(msg, $3, len); }\n",
332 CTypeConst(field), msgname, field->name(),
333 GetSizeInit(layout.GetFieldOffset(field)));
334 } else if (field->real_containing_oneof()) {
336 "UPB_INLINE $0 $1_$2(const $1 *msg) { "
337 "return UPB_READ_ONEOF(msg, $0, $3, $4, $5, $6); }\n",
338 CTypeConst(field), msgname, field->name(),
339 GetSizeInit(layout.GetFieldOffset(field)),
340 GetSizeInit(layout.GetOneofCaseOffset(field->real_containing_oneof())),
341 field->number(), FieldDefault(field));
344 "UPB_INLINE $0 $1_$2(const $1 *msg) { "
345 "return *UPB_PTR_AT(msg, $3, $0); }\n",
346 CTypeConst(field), msgname, field->name(),
347 GetSizeInit(layout.GetFieldOffset(field)));
353 // Generate mutable methods.
355 for (auto field : FieldNumberOrder(message)) {
356 if (field->is_map()) {
357 // TODO(haberman): add map-based mutators.
358 const protobuf::Descriptor* entry = field->message_type();
359 const protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1);
360 const protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2);
362 "UPB_INLINE void $0_$1_clear($0 *msg) { _upb_msg_map_clear(msg, $2); }\n",
363 msgname, field->name(),
364 GetSizeInit(layout.GetFieldOffset(field)));
366 "UPB_INLINE bool $0_$1_set($0 *msg, $2 key, $3 val, upb_arena *a) { "
367 "return _upb_msg_map_set(msg, $4, &key, $5, &val, $6, a); }\n",
368 msgname, field->name(), CType(key), CType(val),
369 GetSizeInit(layout.GetFieldOffset(field)),
370 key->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING
373 val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING
377 "UPB_INLINE bool $0_$1_delete($0 *msg, $2 key) { "
378 "return _upb_msg_map_delete(msg, $3, &key, $4); }\n",
379 msgname, field->name(), CType(key),
380 GetSizeInit(layout.GetFieldOffset(field)),
381 key->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING
385 "UPB_INLINE $0 $1_$2_nextmutable($1 *msg, size_t* iter) { "
386 "return ($0)_upb_msg_map_next(msg, $3, iter); }\n",
387 CType(field), msgname, field->name(),
388 GetSizeInit(layout.GetFieldOffset(field)));
389 } else if (field->is_repeated()) {
391 "UPB_INLINE $0* $1_mutable_$2($1 *msg, size_t *len) {\n"
392 " return ($0*)_upb_array_mutable_accessor(msg, $3, len);\n"
394 CType(field), msgname, field->name(),
395 GetSizeInit(layout.GetFieldOffset(field)));
397 "UPB_INLINE $0* $1_resize_$2($1 *msg, size_t len, "
398 "upb_arena *arena) {\n"
399 " return ($0*)_upb_array_resize_accessor2(msg, $3, len, $4, arena);\n"
401 CType(field), msgname, field->name(),
402 GetSizeInit(layout.GetFieldOffset(field)),
404 if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
406 "UPB_INLINE struct $0* $1_add_$2($1 *msg, upb_arena *arena) {\n"
407 " struct $0* sub = (struct $0*)_upb_msg_new(&$3, arena);\n"
408 " bool ok = _upb_array_append_accessor2(\n"
409 " msg, $4, $5, &sub, arena);\n"
410 " if (!ok) return NULL;\n"
413 MessageName(field->message_type()), msgname, field->name(),
414 MessageInit(field->message_type()),
415 GetSizeInit(layout.GetFieldOffset(field)),
419 "UPB_INLINE bool $1_add_$2($1 *msg, $0 val, upb_arena *arena) {\n"
420 " return _upb_array_append_accessor2(msg, $3, $4, &val,\n"
423 CType(field), msgname, field->name(),
424 GetSizeInit(layout.GetFieldOffset(field)),
428 // Non-repeated field.
429 if (message->options().map_entry() && field->name() == "key") {
430 // Key cannot be mutated.
434 // The common function signature for all setters. Varying implementations
436 output("UPB_INLINE void $0_set_$1($0 *msg, $2 value) {\n", msgname,
437 field->name(), CType(field));
439 if (message->options().map_entry()) {
441 " _upb_msg_map_set_value(msg, &value, $0);\n"
443 field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING
445 : "sizeof(" + CType(field) + ")");
446 } else if (field->real_containing_oneof()) {
448 " UPB_WRITE_ONEOF(msg, $0, $1, value, $2, $3);\n"
450 CType(field), GetSizeInit(layout.GetFieldOffset(field)),
452 layout.GetOneofCaseOffset(field->real_containing_oneof())),
455 if (MessageLayout::HasHasbit(field)) {
456 output(" _upb_sethas(msg, $0);\n", layout.GetHasbitIndex(field));
459 " *UPB_PTR_AT(msg, $1, $0) = value;\n"
461 CType(field), GetSizeInit(layout.GetFieldOffset(field)));
464 if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE &&
465 !message->options().map_entry()) {
467 "UPB_INLINE struct $0* $1_mutable_$2($1 *msg, upb_arena *arena) {\n"
468 " struct $0* sub = (struct $0*)$1_$2(msg);\n"
469 " if (sub == NULL) {\n"
470 " sub = (struct $0*)_upb_msg_new(&$3, arena);\n"
471 " if (!sub) return NULL;\n"
472 " $1_set_$2(msg, sub);\n"
476 MessageName(field->message_type()), msgname, field->name(),
477 MessageInit(field->message_type()));
485 void WriteHeader(const protobuf::FileDescriptor* file, Output& output) {
486 EmitFileWarning(file, output);
488 "#ifndef $0_UPB_H_\n"
489 "#define $0_UPB_H_\n\n"
490 "#include \"upb/msg.h\"\n"
491 "#include \"upb/decode.h\"\n"
492 "#include \"upb/decode_fast.h\"\n"
493 "#include \"upb/encode.h\"\n\n",
494 ToPreproc(file->name()));
496 for (int i = 0; i < file->public_dependency_count(); i++) {
497 const auto& name = file->public_dependency(i)->name();
499 output("/* Public Imports. */\n");
501 output("#include \"$0\"\n", HeaderFilename(name));
502 if (i == file->public_dependency_count() - 1) {
508 "#include \"upb/port_def.inc\"\n"
510 "#ifdef __cplusplus\n"
515 std::vector<const protobuf::Descriptor*> this_file_messages =
516 SortedMessages(file);
518 // Forward-declare types defined in this file.
519 for (auto message : this_file_messages) {
520 output("struct $0;\n", ToCIdent(message->full_name()));
522 for (auto message : this_file_messages) {
523 output("typedef struct $0 $0;\n", ToCIdent(message->full_name()));
525 for (auto message : this_file_messages) {
526 output("extern const upb_msglayout $0;\n", MessageInit(message));
529 // Forward-declare types not in this file, but used as submessages.
530 // Order by full name for consistent ordering.
531 std::map<std::string, const protobuf::Descriptor*> forward_messages;
533 for (auto message : SortedMessages(file)) {
534 for (int i = 0; i < message->field_count(); i++) {
535 const protobuf::FieldDescriptor* field = message->field(i);
536 if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE &&
537 field->file() != field->message_type()->file()) {
538 forward_messages[field->message_type()->full_name()] =
539 field->message_type();
543 for (const auto& pair : forward_messages) {
544 output("struct $0;\n", MessageName(pair.second));
546 for (const auto& pair : forward_messages) {
547 output("extern const upb_msglayout $0;\n", MessageInit(pair.second));
550 if (!this_file_messages.empty()) {
554 std::vector<const protobuf::EnumDescriptor*> this_file_enums =
557 for (auto enumdesc : this_file_enums) {
558 output("typedef enum {\n");
559 DumpEnumValues(enumdesc, output);
560 output("} $0;\n\n", ToCIdent(enumdesc->full_name()));
565 for (auto message : this_file_messages) {
566 GenerateMessageInHeader(message, output);
570 "#ifdef __cplusplus\n"
571 "} /* extern \"C\" */\n"
574 "#include \"upb/port_undef.inc\"\n"
576 "#endif /* $0_UPB_H_ */\n",
577 ToPreproc(file->name()));
580 int TableDescriptorType(const protobuf::FieldDescriptor* field) {
581 if (field->file()->syntax() == protobuf::FileDescriptor::SYNTAX_PROTO2 &&
582 field->type() == protobuf::FieldDescriptor::TYPE_STRING) {
583 // From the perspective of the binary encoder/decoder, proto2 string fields
584 // are identical to bytes fields. Only in proto3 do we check UTF-8 for
585 // string fields at parse time.
587 // If we ever use these tables for JSON encoding/decoding (for example by
588 // embedding field names on the side) we will have to revisit this, because
589 // string vs. bytes behavior is not affected by proto2 vs proto3.
590 return protobuf::FieldDescriptor::TYPE_BYTES;
592 return field->type();
598 SubmsgArray(const protobuf::Descriptor* message) : message_(message) {
599 MessageLayout layout(message);
600 std::vector<const protobuf::FieldDescriptor*> sorted_submsgs =
601 SortedSubmessages(message);
603 for (auto submsg : sorted_submsgs) {
604 if (indexes_.find(submsg->message_type()) != indexes_.end()) {
607 submsgs_.push_back(submsg->message_type());
608 indexes_[submsg->message_type()] = i++;
612 const std::vector<const protobuf::Descriptor*>& submsgs() const {
616 int GetIndex(const protobuf::FieldDescriptor* field) {
618 assert(field->containing_type() == message_);
619 auto it = indexes_.find(field->message_type());
620 assert(it != indexes_.end());
625 const protobuf::Descriptor* message_;
626 std::vector<const protobuf::Descriptor*> submsgs_;
627 absl::flat_hash_map<const protobuf::Descriptor*, int> indexes_;
630 typedef std::pair<std::string, uint64_t> TableEntry;
632 uint64_t GetEncodedTag(const protobuf::FieldDescriptor* field) {
633 protobuf::internal::WireFormatLite::WireType wire_type =
634 protobuf::internal::WireFormat::WireTypeForField(field);
635 uint32_t unencoded_tag =
636 protobuf::internal::WireFormatLite::MakeTag(field->number(), wire_type);
637 uint8_t tag_bytes[10] = {0};
638 protobuf::io::CodedOutputStream::WriteVarint32ToArray(unencoded_tag,
640 uint64_t encoded_tag = 0;
641 memcpy(&encoded_tag, tag_bytes, sizeof(encoded_tag));
642 // TODO: byte-swap for big endian.
646 int GetTableSlot(const protobuf::FieldDescriptor* field) {
647 uint64_t tag = GetEncodedTag(field);
649 // Tag must fit within a two-byte varint.
652 return (tag & 0xf8) >> 3;
655 bool TryFillTableEntry(const protobuf::Descriptor* message,
656 const MessageLayout& layout,
657 const protobuf::FieldDescriptor* field,
659 std::string type = "";
660 std::string cardinality = "";
661 switch (field->type()) {
662 case protobuf::FieldDescriptor::TYPE_BOOL:
665 case protobuf::FieldDescriptor::TYPE_INT32:
666 case protobuf::FieldDescriptor::TYPE_ENUM:
667 case protobuf::FieldDescriptor::TYPE_UINT32:
670 case protobuf::FieldDescriptor::TYPE_INT64:
671 case protobuf::FieldDescriptor::TYPE_UINT64:
674 case protobuf::FieldDescriptor::TYPE_FIXED32:
675 case protobuf::FieldDescriptor::TYPE_SFIXED32:
676 case protobuf::FieldDescriptor::TYPE_FLOAT:
679 case protobuf::FieldDescriptor::TYPE_FIXED64:
680 case protobuf::FieldDescriptor::TYPE_SFIXED64:
681 case protobuf::FieldDescriptor::TYPE_DOUBLE:
684 case protobuf::FieldDescriptor::TYPE_SINT32:
687 case protobuf::FieldDescriptor::TYPE_SINT64:
690 case protobuf::FieldDescriptor::TYPE_STRING:
691 if (field->file()->syntax() == protobuf::FileDescriptor::SYNTAX_PROTO3) {
692 // Only proto3 validates UTF-8.
696 ABSL_FALLTHROUGH_INTENDED;
697 case protobuf::FieldDescriptor::TYPE_BYTES:
700 case protobuf::FieldDescriptor::TYPE_MESSAGE:
701 if (field->is_map()) {
702 return false; // Not supported yet (ever?).
707 return false; // Not supported yet.
710 switch (field->label()) {
711 case protobuf::FieldDescriptor::LABEL_REPEATED:
712 if (field->is_packed()) {
718 case protobuf::FieldDescriptor::LABEL_OPTIONAL:
719 case protobuf::FieldDescriptor::LABEL_REQUIRED:
720 if (field->real_containing_oneof()) {
728 uint64_t expected_tag = GetEncodedTag(field);
729 MessageLayout::Size offset = layout.GetFieldOffset(field);
734 // |--------|--------|--------|--------|--------|--------|--------|--------|
735 // | offset (16) |case offset (16) |presence| submsg | exp. tag (16) |
736 // |--------|--------|--------|--------|--------|--------|--------|--------|
738 // - |presence| is either hasbit index or field number for oneofs.
740 uint64_t data = offset.size64 << 48 | expected_tag;
742 if (field->is_repeated()) {
743 // No hasbit/oneof-related fields.
744 } if (field->real_containing_oneof()) {
745 MessageLayout::Size case_offset =
746 layout.GetOneofCaseOffset(field->real_containing_oneof());
747 if (case_offset.size64 > 0xffff) return false;
748 assert(field->number() < 256);
749 data |= field->number() << 24;
750 data |= case_offset.size64 << 32;
752 uint64_t hasbit_index = 63; // No hasbit (set a high, unused bit).
753 if (layout.HasHasbit(field)) {
754 hasbit_index = layout.GetHasbitIndex(field);
755 if (hasbit_index > 31) return false;
757 data |= hasbit_index << 24;
760 if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
761 SubmsgArray submsg_array(message);
762 uint64_t idx = submsg_array.GetIndex(field);
763 if (idx > 255) return false;
766 std::string size_ceil = "max";
767 size_t size = SIZE_MAX;
768 if (field->message_type()->file() == field->file()) {
769 // We can only be guaranteed the size of the sub-message if it is in the
770 // same file as us. We could relax this to increase the speed of
771 // cross-file sub-message parsing if we are comfortable requiring that
772 // users compile all messages at the same time.
773 MessageLayout sub_layout(field->message_type());
774 size = sub_layout.message_size().size64 + 8;
776 std::vector<size_t> breaks = {64, 128, 192, 256};
777 for (auto brk : breaks) {
779 size_ceil = std::to_string(brk);
783 ent.first = absl::Substitute("upb_p$0$1_$2bt_max$3b", cardinality, type,
784 expected_tag > 0xff ? "2" : "1", size_ceil);
787 ent.first = absl::Substitute("upb_p$0$1_$2bt", cardinality, type,
788 expected_tag > 0xff ? "2" : "1");
794 std::vector<TableEntry> FastDecodeTable(const protobuf::Descriptor* message,
795 const MessageLayout& layout) {
796 std::vector<TableEntry> table;
797 for (const auto field : FieldHotnessOrder(message)) {
799 int slot = GetTableSlot(field);
800 // std::cerr << "table slot: " << field->number() << ": " << slot << "\n";
802 // Tag can't fit in the table.
805 if (!TryFillTableEntry(message, layout, field, ent)) {
806 // Unsupported field type or offset, hasbit index, etc. doesn't fit.
809 while ((size_t)slot >= table.size()) {
810 size_t size = std::max(static_cast<size_t>(1), table.size() * 2);
811 table.resize(size, TableEntry{"fastdecode_generic", 0});
813 if (table[slot].first != "fastdecode_generic") {
814 // A hotter field already filled this slot.
822 void WriteSource(const protobuf::FileDescriptor* file, Output& output,
823 bool fasttable_enabled) {
824 EmitFileWarning(file, output);
827 "#include <stddef.h>\n"
828 "#include \"upb/msg.h\"\n"
830 HeaderFilename(file->name()));
832 for (int i = 0; i < file->dependency_count(); i++) {
833 output("#include \"$0\"\n", HeaderFilename(file->dependency(i)->name()));
838 "#include \"upb/port_def.inc\"\n"
842 for (auto message : SortedMessages(file)) {
843 std::string msgname = ToCIdent(message->full_name());
844 std::string fields_array_ref = "NULL";
845 std::string submsgs_array_ref = "NULL";
846 MessageLayout layout(message);
847 SubmsgArray submsg_array(message);
849 if (!submsg_array.submsgs().empty()) {
850 // TODO(haberman): could save a little bit of space by only generating a
851 // "submsgs" array for every strongly-connected component.
852 std::string submsgs_array_name = msgname + "_submsgs";
853 submsgs_array_ref = "&" + submsgs_array_name + "[0]";
854 output("static const upb_msglayout *const $0[$1] = {\n",
855 submsgs_array_name, submsg_array.submsgs().size());
857 for (auto submsg : submsg_array.submsgs()) {
858 output(" &$0,\n", MessageInit(submsg));
864 std::vector<const protobuf::FieldDescriptor*> field_number_order =
865 FieldNumberOrder(message);
866 if (!field_number_order.empty()) {
867 std::string fields_array_name = msgname + "__fields";
868 fields_array_ref = "&" + fields_array_name + "[0]";
869 output("static const upb_msglayout_field $0[$1] = {\n",
870 fields_array_name, field_number_order.size());
871 for (auto field : field_number_order) {
872 int submsg_index = 0;
873 std::string presence = "0";
875 if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
876 submsg_index = submsg_array.GetIndex(field);
879 if (MessageLayout::HasHasbit(field)) {
880 int index = layout.GetHasbitIndex(field);
882 presence = absl::StrCat(index);
883 } else if (field->real_containing_oneof()) {
884 MessageLayout::Size case_offset =
885 layout.GetOneofCaseOffset(field->real_containing_oneof());
887 // We encode as negative to distinguish from hasbits.
888 case_offset.size32 = ~case_offset.size32;
889 case_offset.size64 = ~case_offset.size64;
890 assert(case_offset.size32 < 0);
891 assert(case_offset.size64 < 0);
892 presence = GetSizeInit(case_offset);
896 if (field->is_map()) {
897 label = "_UPB_LABEL_MAP";
898 } else if (field->is_packed()) {
899 label = "_UPB_LABEL_PACKED";
901 label = absl::StrCat(field->label());
904 output(" {$0, $1, $2, $3, $4, $5},\n",
906 GetSizeInit(layout.GetFieldOffset(field)),
909 TableDescriptorType(field),
915 std::vector<TableEntry> table;
916 uint8_t table_mask = -1;
918 if (fasttable_enabled) {
919 table = FastDecodeTable(message, layout);
922 if (table.size() > 1) {
923 assert((table.size() & (table.size() - 1)) == 0);
924 table_mask = (table.size() - 1) << 3;
927 output("const upb_msglayout $0 = {\n", MessageInit(message));
928 output(" $0,\n", submsgs_array_ref);
929 output(" $0,\n", fields_array_ref);
930 output(" $0, $1, $2, $3,\n", GetSizeInit(layout.message_size()),
931 field_number_order.size(),
932 "false", // TODO: extendable
935 if (!table.empty()) {
936 output(" UPB_FASTTABLE_INIT({\n");
937 for (const auto& ent : table) {
938 output(" {0x$1, &$0},\n", ent.first,
939 absl::StrCat(absl::Hex(ent.second, absl::kZeroPad16)));
946 output("#include \"upb/port_undef.inc\"\n");
950 class Generator : public protoc::CodeGenerator {
951 ~Generator() override {}
952 bool Generate(const protobuf::FileDescriptor* file,
953 const std::string& parameter, protoc::GeneratorContext* context,
954 std::string* error) const override;
955 uint64_t GetSupportedFeatures() const override {
956 return FEATURE_PROTO3_OPTIONAL;
960 bool Generator::Generate(const protobuf::FileDescriptor* file,
961 const std::string& parameter,
962 protoc::GeneratorContext* context,
963 std::string* error) const {
964 bool fasttable_enabled = false;
965 std::vector<std::pair<std::string, std::string>> params;
966 google::protobuf::compiler::ParseGeneratorParameter(parameter, ¶ms);
968 for (const auto& pair : params) {
969 if (pair.first == "fasttable") {
970 fasttable_enabled = true;
972 *error = "Unknown parameter: " + pair.first;
977 Output h_output(context->Open(HeaderFilename(file->name())));
978 WriteHeader(file, h_output);
980 Output c_output(context->Open(SourceFilename(file->name())));
981 WriteSource(file, c_output, fasttable_enabled);
989 int main(int argc, char** argv) {
990 std::unique_ptr<google::protobuf::compiler::CodeGenerator> generator(
991 new upbc::Generator());
992 return google::protobuf::compiler::PluginMain(argc, argv, generator.get());