1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google LLC. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 #include "google/protobuf/compiler/hpb/gen_enums.h"
9
10 #include <algorithm>
11 #include <limits>
12 #include <string>
13 #include <vector>
14
15 #include "google/protobuf/descriptor.pb.h"
16 #include "google/protobuf/compiler/hpb/gen_utils.h"
17 #include "google/protobuf/compiler/hpb/names.h"
18 #include "google/protobuf/descriptor.h"
19
20 namespace google::protobuf::hpb_generator {
21
22 namespace protobuf = ::proto2;
23
24 // Convert enum value to C++ literal.
25 //
26 // In C++, an value of -2147483648 gets interpreted as the negative of
27 // 2147483648, and since 2147483648 can't fit in an integer, this produces a
28 // compiler warning. This works around that issue.
EnumInt32ToString(int number)29 std::string EnumInt32ToString(int number) {
30 if (number == std::numeric_limits<int32_t>::min()) {
31 // This needs to be special-cased, see explanation here:
32 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52661
33 return absl::StrCat(number + 1, " - 1");
34 } else {
35 return absl::StrCat(number);
36 }
37 }
38
EnumTypeName(const protobuf::EnumDescriptor * enum_descriptor)39 std::string EnumTypeName(const protobuf::EnumDescriptor* enum_descriptor) {
40 auto containing_type = enum_descriptor->containing_type();
41 if (containing_type == nullptr) {
42 // enums types with no package name are prefixed with protos_ to prevent
43 // conflicts with generated C headers.
44 if (enum_descriptor->file()->package().empty()) {
45 return absl::StrCat(kNoPackageNamePrefix,
46 ToCIdent(enum_descriptor->name()));
47 }
48 return ToCIdent(enum_descriptor->name());
49 } else {
50 // Since the enum is in global name space (no package), it will have the
51 // same classified name as the C header include, to prevent collision
52 // rename as above.
53 if (containing_type->file()->package().empty()) {
54 return ToCIdent(absl::StrCat(containing_type->name(), "_",
55 kNoPackageNamePrefix,
56 enum_descriptor->name()));
57 } else {
58 return ToCIdent(
59 absl::StrCat(containing_type->name(), "_", enum_descriptor->name()));
60 }
61 }
62 }
63
EnumValueSymbolInNameSpace(const protobuf::EnumDescriptor * desc,const protobuf::EnumValueDescriptor * value)64 std::string EnumValueSymbolInNameSpace(
65 const protobuf::EnumDescriptor* desc,
66 const protobuf::EnumValueDescriptor* value) {
67 auto containing_type = desc->containing_type();
68 if (containing_type != nullptr) {
69 return ToCIdent(absl::StrCat(containing_type->name(), "_", desc->name(),
70 "_", value->name()));
71 } else {
72 // protos enum values with no package name are prefixed with protos_ to
73 // prevent conflicts with generated C headers.
74 if (desc->file()->package().empty()) {
75 return absl::StrCat(kNoPackageNamePrefix, ToCIdent(value->name()));
76 }
77 return ToCIdent(value->name());
78 }
79 }
80
WriteEnumValues(const protobuf::EnumDescriptor * desc,Output & output)81 void WriteEnumValues(const protobuf::EnumDescriptor* desc, Output& output) {
82 std::vector<const protobuf::EnumValueDescriptor*> values;
83 auto value_count = desc->value_count();
84 values.reserve(value_count);
85 for (int i = 0; i < value_count; i++) {
86 values.push_back(desc->value(i));
87 }
88 std::stable_sort(values.begin(), values.end(),
89 [](const protobuf::EnumValueDescriptor* a,
90 const protobuf::EnumValueDescriptor* b) {
91 return a->number() < b->number();
92 });
93
94 for (size_t i = 0; i < values.size(); i++) {
95 auto value = values[i];
96 output(" $0", EnumValueSymbolInNameSpace(desc, value));
97 output(" = $0", EnumInt32ToString(value->number()));
98 if (i != values.size() - 1) {
99 output(",");
100 }
101 output("\n");
102 }
103 }
104
WriteEnumDeclarations(const std::vector<const protobuf::EnumDescriptor * > & enums,Output & output)105 void WriteEnumDeclarations(
106 const std::vector<const protobuf::EnumDescriptor*>& enums, Output& output) {
107 for (auto enumdesc : enums) {
108 output("enum $0 : int {\n", EnumTypeName(enumdesc));
109 WriteEnumValues(enumdesc, output);
110 output("};\n\n");
111 }
112 }
113
WriteHeaderEnumForwardDecls(std::vector<const protobuf::EnumDescriptor * > & enums,Output & output)114 void WriteHeaderEnumForwardDecls(
115 std::vector<const protobuf::EnumDescriptor*>& enums, Output& output) {
116 for (const auto* enumdesc : enums) {
117 output("enum $0 : int;\n", EnumTypeName(enumdesc));
118 }
119 }
120
121 } // namespace protobuf
122 } // namespace google::hpb_generator
123