|  | // 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/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 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 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 |