| // Protocol Buffers - Google's data interchange format | |
| // Copyright 2023 Google LLC. All rights reserved. | |
| // | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file or at | |
| // https://developers.google.com/open-source/licenses/bsd | |
| #include "hpb_generator/gen_enums.h" | |
| #include <algorithm> | |
| #include <deque> | |
| #include <limits> | |
| #include <string> | |
| #include <vector> | |
| #include "google/protobuf/descriptor.pb.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/str_join.h" | |
| #include "absl/strings/string_view.h" | |
| #include "hpb_generator/context.h" | |
| #include "hpb_generator/gen_utils.h" | |
| #include "hpb_generator/keywords.h" | |
| #include "hpb_generator/names.h" | |
| #include "google/protobuf/descriptor.h" | |
| namespace google { | |
| namespace protobuf { | |
| namespace hpb_generator { | |
| using Sub = google::protobuf::io::Printer::Sub; | |
| namespace { | |
| std::string ContainingTypeNames(const google::protobuf::EnumDescriptor* enum_descriptor) { | |
| std::deque<absl::string_view> containing_type_names; | |
| auto containing_type = enum_descriptor->containing_type(); | |
| while (containing_type != nullptr) { | |
| containing_type_names.push_front(containing_type->name()); | |
| containing_type = containing_type->containing_type(); | |
| } | |
| return absl::StrJoin(containing_type_names, "_"); | |
| } | |
| } // namespace | |
| // Convert enum value to C++ literal. | |
| // | |
| // In C++, an value of -2147483648 gets interpreted as the negative of | |
| // 2147483648, and since 2147483648 can't fit in an integer, this produces a | |
| // compiler warning. This works around that issue. | |
| std::string EnumInt32ToString(int number) { | |
| if (number == std::numeric_limits<int32_t>::min()) { | |
| // This needs to be special-cased, see explanation here: | |
| // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52661 | |
| return absl::StrCat(number + 1, " - 1"); | |
| } else { | |
| return absl::StrCat(number); | |
| } | |
| } | |
| std::string EnumTypeName(const google::protobuf::EnumDescriptor* enum_descriptor) { | |
| const std::string containing_types = ContainingTypeNames(enum_descriptor); | |
| if (containing_types.empty()) { | |
| // enums types with no package name are prefixed with protos_ to prevent | |
| // conflicts with generated C headers. | |
| if (enum_descriptor->file()->package().empty()) { | |
| return absl::StrCat(kNoPackageNamePrefix, | |
| ToCIdent(enum_descriptor->name())); | |
| } | |
| return ResolveKeywordConflict(ToCIdent(enum_descriptor->name())); | |
| } else { | |
| // Since the enum is in global name space (no package), it will have the | |
| // same classified name as the C header include, to prevent collision | |
| // rename as above. | |
| if (enum_descriptor->containing_type()->file()->package().empty()) { | |
| return ToCIdent(absl::StrCat(containing_types, "_", kNoPackageNamePrefix, | |
| enum_descriptor->name())); | |
| } else { | |
| return ToCIdent( | |
| absl::StrCat(containing_types, "_", enum_descriptor->name())); | |
| } | |
| } | |
| } | |
| std::string EnumValueSymbolInNameSpace( | |
| const google::protobuf::EnumDescriptor* desc, | |
| const google::protobuf::EnumValueDescriptor* value) { | |
| const std::string containing_types = ContainingTypeNames(desc); | |
| if (!containing_types.empty()) { | |
| return ToCIdent( | |
| absl::StrCat(containing_types, "_", desc->name(), "_", value->name())); | |
| } else { | |
| // protos enum values with no package name are prefixed with protos_ to | |
| // prevent conflicts with generated C headers. | |
| if (desc->file()->package().empty()) { | |
| return absl::StrCat(kNoPackageNamePrefix, ToCIdent(value->name())); | |
| } | |
| return ResolveKeywordConflict(ToCIdent(value->name())); | |
| } | |
| } | |
| void WriteEnumValues(const google::protobuf::EnumDescriptor* desc, Context& ctx) { | |
| std::vector<const google::protobuf::EnumValueDescriptor*> values; | |
| auto value_count = desc->value_count(); | |
| values.reserve(value_count); | |
| for (int i = 0; i < value_count; i++) { | |
| values.push_back(desc->value(i)); | |
| } | |
| std::stable_sort(values.begin(), values.end(), | |
| [](const google::protobuf::EnumValueDescriptor* a, | |
| const google::protobuf::EnumValueDescriptor* b) { | |
| return a->number() < b->number(); | |
| }); | |
| for (size_t i = 0; i < values.size(); i++) { | |
| auto value = values[i]; | |
| ctx.Emit({{"name", EnumValueSymbolInNameSpace(desc, value)}, | |
| {"number", EnumInt32ToString(value->number())}, | |
| {"sep", i == values.size() - 1 ? "" : ","}}, | |
| R"cc( | |
| $name$ = $number$$sep$ | |
| )cc"); | |
| } | |
| } | |
| void WriteEnumDeclarations( | |
| const std::vector<const google::protobuf::EnumDescriptor*>& enums, Context& ctx) { | |
| for (auto enumdesc : enums) { | |
| ctx.Emit({{"type", EnumTypeName(enumdesc)}, | |
| Sub("enum_vals", [&] { WriteEnumValues(enumdesc, ctx); }) | |
| .WithSuffix(",")}, | |
| R"cc( | |
| enum $type$ : int { | |
| $enum_vals$, | |
| }; | |
| )cc"); | |
| } | |
| } | |
| } // namespace hpb_generator | |
| } // namespace protobuf | |
| } // namespace google |