|  | // 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 "protos_generator/gen_enums.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <limits> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "google/protobuf/descriptor.pb.h" | 
|  | #include "google/protobuf/descriptor.h" | 
|  | #include "protos_generator/gen_utils.h" | 
|  | #include "protos_generator/names.h" | 
|  |  | 
|  | namespace protos_generator { | 
|  |  | 
|  | namespace protobuf = ::google::protobuf; | 
|  |  | 
|  | // 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 protobuf::EnumDescriptor* enum_descriptor) { | 
|  | auto containing_type = enum_descriptor->containing_type(); | 
|  | if (containing_type == nullptr) { | 
|  | // 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 (containing_type->file()->package().empty()) { | 
|  | return ToCIdent(absl::StrCat(containing_type->name(), "_", | 
|  | kNoPackageNamePrefix, | 
|  | enum_descriptor->name())); | 
|  | } else { | 
|  | return ToCIdent( | 
|  | absl::StrCat(containing_type->name(), "_", enum_descriptor->name())); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | std::string EnumValueSymbolInNameSpace( | 
|  | const protobuf::EnumDescriptor* desc, | 
|  | const protobuf::EnumValueDescriptor* value) { | 
|  | auto containing_type = desc->containing_type(); | 
|  | if (containing_type != nullptr) { | 
|  | return ToCIdent(absl::StrCat(containing_type->name(), "_", 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 protobuf::EnumDescriptor* desc, Output& output) { | 
|  | std::vector<const 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::sort(values.begin(), values.end(), | 
|  | [](const protobuf::EnumValueDescriptor* a, | 
|  | const protobuf::EnumValueDescriptor* b) { | 
|  | return a->number() < b->number(); | 
|  | }); | 
|  |  | 
|  | for (size_t i = 0; i < values.size(); i++) { | 
|  | auto value = values[i]; | 
|  | output("  $0", EnumValueSymbolInNameSpace(desc, value)); | 
|  | output(" = $0", EnumInt32ToString(value->number())); | 
|  | if (i != values.size() - 1) { | 
|  | output(","); | 
|  | } | 
|  | output("\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | void WriteEnumDeclarations( | 
|  | const std::vector<const protobuf::EnumDescriptor*>& enums, Output& output) { | 
|  | for (auto enumdesc : enums) { | 
|  | output("enum $0 : int {\n", EnumTypeName(enumdesc)); | 
|  | WriteEnumValues(enumdesc, output); | 
|  | output("};\n\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | void WriteHeaderEnumForwardDecls( | 
|  | std::vector<const protobuf::EnumDescriptor*>& enums, Output& output) { | 
|  | for (const auto* enumdesc : enums) { | 
|  | output("enum $0 : int;\n", EnumTypeName(enumdesc)); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace protos_generator |