Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "proto_zero_generator.h" | 5 #include "proto_zero_generator.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 #include <memory> | 8 #include <memory> |
| 9 #include <set> | 9 #include <set> |
| 10 #include <string> | 10 #include <string> |
| 11 | 11 |
| 12 #include "third_party/protobuf/src/google/protobuf/descriptor.h" | 12 #include "third_party/protobuf/src/google/protobuf/descriptor.h" |
| 13 #include "third_party/protobuf/src/google/protobuf/io/printer.h" | 13 #include "third_party/protobuf/src/google/protobuf/io/printer.h" |
| 14 #include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream.h" | 14 #include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream.h" |
| 15 #include "third_party/protobuf/src/google/protobuf/stubs/strutil.h" | 15 #include "third_party/protobuf/src/google/protobuf/stubs/strutil.h" |
| 16 | 16 |
| 17 namespace tracing { | 17 namespace tracing { |
| 18 namespace proto { | 18 namespace proto { |
| 19 | 19 |
| 20 using google::protobuf::Descriptor; // Message descriptor. | 20 using google::protobuf::Descriptor; // Message descriptor. |
| 21 using google::protobuf::EnumDescriptor; | 21 using google::protobuf::EnumDescriptor; |
| 22 using google::protobuf::EnumValueDescriptor; | 22 using google::protobuf::EnumValueDescriptor; |
| 23 using google::protobuf::FieldDescriptor; | 23 using google::protobuf::FieldDescriptor; |
| 24 using google::protobuf::FileDescriptor; | 24 using google::protobuf::FileDescriptor; |
| 25 using google::protobuf::compiler::GeneratorContext; | 25 using google::protobuf::compiler::GeneratorContext; |
| 26 using google::protobuf::io::Printer; | 26 using google::protobuf::io::Printer; |
| 27 using google::protobuf::io::ZeroCopyOutputStream; | 27 using google::protobuf::io::ZeroCopyOutputStream; |
| 28 | 28 |
| 29 using google::protobuf::LowerString; | |
| 29 using google::protobuf::Split; | 30 using google::protobuf::Split; |
| 30 using google::protobuf::StripPrefixString; | 31 using google::protobuf::StripPrefixString; |
| 31 using google::protobuf::StripString; | 32 using google::protobuf::StripString; |
| 32 using google::protobuf::StripSuffixString; | 33 using google::protobuf::StripSuffixString; |
| 33 using google::protobuf::UpperString; | 34 using google::protobuf::UpperString; |
| 34 | 35 |
| 35 namespace { | 36 namespace { |
| 36 | 37 |
| 37 inline std::string ProtoStubName(const FileDescriptor* proto) { | 38 inline std::string ProtoStubName(const FileDescriptor* proto) { |
| 38 return StripSuffixString(proto->name(), ".proto") + ".pbzero"; | 39 return StripSuffixString(proto->name(), ".proto") + ".pbzero"; |
| 39 } | 40 } |
| 40 | 41 |
| 42 enum CharCase { | |
| 43 UPPER, | |
| 44 LOWER, | |
| 45 DIGIT, | |
| 46 SYMBOL, | |
| 47 UNDEFINED, | |
| 48 }; | |
| 49 | |
| 50 inline CharCase GetCaseOfChar(char x) { | |
| 51 if ('A' <= x && x <= 'Z') | |
| 52 return CharCase::UPPER; | |
|
Primiano Tucci (use gerrit)
2016/08/05 08:52:05
I think here you are reinventing cctype's isupper(
kraynov
2016/08/05 11:05:53
Acknowledged.
| |
| 53 else if ('a' <= x && x <= 'z') | |
| 54 return CharCase::LOWER; | |
| 55 else if ('0' <= x && x <= '9') | |
| 56 return CharCase::DIGIT; | |
| 57 else | |
| 58 return CharCase::SYMBOL; | |
| 59 } | |
| 60 | |
| 61 std::string ExtractWord(const std::string& source, size_t* offset) { | |
| 62 std::string word; | |
| 63 CharCase start_case = CharCase::UNDEFINED; | |
| 64 CharCase end_case = CharCase::UNDEFINED; | |
| 65 | |
| 66 while (*offset < source.size()) { | |
| 67 char current = source.at(*offset); | |
| 68 CharCase current_case = GetCaseOfChar(current); | |
| 69 | |
| 70 // Skip underscores. | |
| 71 if (current_case == CharCase::SYMBOL) { | |
| 72 while (current_case == CharCase::SYMBOL) { | |
| 73 (*offset)++; | |
| 74 if (*offset == source.size()) | |
| 75 return word; | |
| 76 current = source.at(*offset); | |
| 77 current_case = GetCaseOfChar(current); | |
| 78 } | |
| 79 if (!word.empty()) | |
| 80 return word; | |
| 81 } | |
| 82 // Current case must be UPPER, LOWER or DIGIT below this line. | |
| 83 | |
| 84 // Detect word's case. | |
| 85 if (word.empty()) { | |
| 86 start_case = current_case; | |
| 87 if (start_case == CharCase::LOWER || start_case == CharCase::DIGIT) { | |
| 88 end_case = start_case; | |
| 89 } | |
| 90 } else if (end_case == CharCase::UNDEFINED) { | |
| 91 // Must be a second character in this condition. | |
| 92 // Word starting with upper letter has two options (e.g. FOO or Bar). | |
| 93 end_case = current_case; | |
| 94 } | |
| 95 | |
| 96 // Finish the word if case changes. | |
| 97 if (current_case != end_case && end_case != CharCase::UNDEFINED) { | |
| 98 if (end_case == CharCase::UPPER && current_case == CharCase::LOWER) { | |
| 99 // Rollback one character. | |
| 100 // E.g. [URL]Encoder, not [URLE]ncoder. | |
| 101 (*offset)--; | |
| 102 word.erase(word.size() - 1, 1); | |
| 103 } | |
| 104 return word; | |
| 105 } | |
| 106 | |
| 107 word.push_back(current); | |
| 108 (*offset)++; | |
| 109 } | |
| 110 return word; | |
| 111 } | |
| 112 | |
| 113 std::string NameToCamelCase(const std::string& name) { | |
| 114 std::string camel; | |
| 115 size_t offset = 0; | |
| 116 while (offset < name.size()) { | |
| 117 // Offset is mutable by ExtractWord function. | |
| 118 std::string word = ExtractWord(name, &offset); | |
| 119 if (word.empty()) | |
| 120 continue; | |
| 121 LowerString(&word); | |
| 122 if (GetCaseOfChar(word.at(0)) == LOWER) | |
| 123 word.at(0) -= ('a' - 'A'); | |
| 124 camel += word; | |
| 125 } | |
| 126 return camel; | |
| 127 } | |
| 128 | |
| 41 class GeneratorJob { | 129 class GeneratorJob { |
| 42 public: | 130 public: |
| 43 GeneratorJob(const FileDescriptor *file, | 131 GeneratorJob(const FileDescriptor *file, |
| 44 Printer* stub_h_printer, | 132 Printer* stub_h_printer, |
| 45 Printer* stub_cc_printer) | 133 Printer* stub_cc_printer) |
| 46 : source_(file), | 134 : source_(file), |
| 47 stub_h_(stub_h_printer), | 135 stub_h_(stub_h_printer), |
| 48 stub_cc_(stub_cc_printer) {} | 136 stub_cc_(stub_cc_printer) {} |
| 49 | 137 |
| 50 bool GenerateStubs() { | 138 bool GenerateStubs() { |
| (...skipping 405 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 456 for (int j = 0; j < nested_enum->value_count(); ++j) { | 544 for (int j = 0; j < nested_enum->value_count(); ++j) { |
| 457 const EnumValueDescriptor* value = nested_enum->value(j); | 545 const EnumValueDescriptor* value = nested_enum->value(j); |
| 458 stub_h_->Print( | 546 stub_h_->Print( |
| 459 "static const $class$ $name$ = $full_name$;\n", | 547 "static const $class$ $name$ = $full_name$;\n", |
| 460 "class", nested_enum->name(), | 548 "class", nested_enum->name(), |
| 461 "name", value->name(), | 549 "name", value->name(), |
| 462 "full_name", value_name_prefix + value->name()); | 550 "full_name", value_name_prefix + value->name()); |
| 463 } | 551 } |
| 464 } | 552 } |
| 465 | 553 |
| 466 // Fields descriptors. | 554 // Field numbers. |
| 555 if (message->field_count() > 0) { | |
| 556 stub_h_->Print("enum : int32_t {\n"); | |
| 557 stub_h_->Indent(); | |
| 558 | |
| 559 for (int i = 0; i < message->field_count(); ++i) { | |
| 560 const FieldDescriptor* field = message->field(i); | |
| 561 std::string name = NameToCamelCase(field->name()); | |
|
Primiano Tucci (use gerrit)
2016/08/05 08:52:05
can you not just use field->camelcase_name()
kraynov
2016/08/05 11:05:53
Acknowledged.
| |
| 562 if (name.empty()) | |
| 563 Abort("Empty field name in camel case notation."); | |
| 564 | |
| 565 stub_h_->Print( | |
| 566 "k$name$FieldNumber = $id$,\n", | |
| 567 "name", name, | |
| 568 "id", std::to_string(field->number())); | |
| 569 } | |
| 570 stub_h_->Outdent(); | |
| 571 stub_h_->Print("};\n"); | |
| 572 } | |
| 573 | |
| 574 // Field descriptors. | |
| 467 for (int i = 0; i < message->field_count(); ++i) { | 575 for (int i = 0; i < message->field_count(); ++i) { |
| 468 const FieldDescriptor* field = message->field(i); | 576 const FieldDescriptor* field = message->field(i); |
| 469 if (field->is_packed()) { | 577 if (field->is_packed()) { |
| 470 Abort("Packed repeated fields are not supported."); | 578 Abort("Packed repeated fields are not supported."); |
| 471 return; | 579 return; |
| 472 } | 580 } |
| 473 if (field->type() != FieldDescriptor::TYPE_MESSAGE) { | 581 if (field->type() != FieldDescriptor::TYPE_MESSAGE) { |
| 474 GenerateSimpleFieldDescriptor(field); | 582 GenerateSimpleFieldDescriptor(field); |
| 475 } else { | 583 } else { |
| 476 GenerateNestedMessageFieldDescriptor(field); | 584 GenerateNestedMessageFieldDescriptor(field); |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 531 GeneratorJob job(file, &stub_h_printer, &stub_cc_printer); | 639 GeneratorJob job(file, &stub_h_printer, &stub_cc_printer); |
| 532 if (!job.GenerateStubs()) { | 640 if (!job.GenerateStubs()) { |
| 533 *error = job.GetFirstError(); | 641 *error = job.GetFirstError(); |
| 534 return false; | 642 return false; |
| 535 } | 643 } |
| 536 return true; | 644 return true; |
| 537 } | 645 } |
| 538 | 646 |
| 539 } // namespace proto | 647 } // namespace proto |
| 540 } // namespace tracing | 648 } // namespace tracing |
| OLD | NEW |