summaryrefslogtreecommitdiffstats
path: root/contrib/libs/protoc/src/google/protobuf/compiler/cpp/enum.cc
diff options
context:
space:
mode:
authornechda <[email protected]>2024-08-29 23:50:27 +0300
committernechda <[email protected]>2024-08-30 00:05:25 +0300
commite10d6638f07a82edae3ea8197b9f5c0affcc07ea (patch)
tree571c38cec05813766a1ad290c9d51ce7ace52919 /contrib/libs/protoc/src/google/protobuf/compiler/cpp/enum.cc
parente79b38f2bbbf78d295d1901d2a79f898022d5224 (diff)
Update cpp-protobuf to 22.5
Привет!\ Этот PR переключат cpp & python библиотеки protobuf на версию 22.5 Если у вас возникли проблемы после влития этого PR: 1. Если начали падать канон тесты, то проведите их переканонизацию 2. Прочитайте <https://wiki.yandex-team.ru/users/nechda/obnovlenie-cpp-protobuf-22.5/> страничку с основными изменениями 3. Если страничка в вики не помогла, то пишите в [DEVTOOLSSUPPORT](https://st.yandex-team.ru/DEVTOOLSSUPPORT) 7fecade616c20a841b9e9af7b7998bdfc8d2807d
Diffstat (limited to 'contrib/libs/protoc/src/google/protobuf/compiler/cpp/enum.cc')
-rw-r--r--contrib/libs/protoc/src/google/protobuf/compiler/cpp/enum.cc782
1 files changed, 463 insertions, 319 deletions
diff --git a/contrib/libs/protoc/src/google/protobuf/compiler/cpp/enum.cc b/contrib/libs/protoc/src/google/protobuf/compiler/cpp/enum.cc
index 91d0b9442b7..917cc761288 100644
--- a/contrib/libs/protoc/src/google/protobuf/compiler/cpp/enum.cc
+++ b/contrib/libs/protoc/src/google/protobuf/compiler/cpp/enum.cc
@@ -32,26 +32,49 @@
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
-#include <google/protobuf/compiler/cpp/enum.h>
+#include "google/protobuf/compiler/cpp/enum.h"
+#include <algorithm>
#include <cstdint>
#include <limits>
-#include <map>
-
-#include <google/protobuf/io/printer.h>
-#include <google/protobuf/stubs/strutil.h>
-#include <google/protobuf/compiler/cpp/helpers.h>
-#include <google/protobuf/compiler/cpp/names.h>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "google/protobuf/descriptor.h"
+#include "y_absl/container/btree_map.h"
+#include "y_absl/container/btree_set.h"
+#include "y_absl/container/flat_hash_map.h"
+#include "y_absl/strings/str_cat.h"
+#include "google/protobuf/compiler/cpp/helpers.h"
+#include "google/protobuf/compiler/cpp/names.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
-
namespace {
-// The GOOGLE_ARRAYSIZE constant is the max enum value plus 1. If the max enum value
-// is kint32max, GOOGLE_ARRAYSIZE will overflow. In such cases we should omit the
-// generation of the GOOGLE_ARRAYSIZE constant.
+using Sub = ::google::protobuf::io::Printer::Sub;
+
+y_absl::flat_hash_map<y_absl::string_view, TProtoStringType> EnumVars(
+ const EnumDescriptor* enum_, const Options& options,
+ const EnumValueDescriptor* min, const EnumValueDescriptor* max) {
+ auto classname = ClassName(enum_, false);
+ return {
+ {"Enum", enum_->name()},
+ {"Enum_", ResolveKeyword(enum_->name())},
+ {"Msg_Enum", classname},
+ {"::Msg_Enum", QualifiedClassName(enum_, options)},
+ {"Msg_Enum_",
+ enum_->containing_type() == nullptr ? "" : y_absl::StrCat(classname, "_")},
+ {"kMin", y_absl::StrCat(min->number())},
+ {"kMax", y_absl::StrCat(max->number())},
+ };
+}
+
+// The ARRAYSIZE constant is the max enum value plus 1. If the max enum value
+// is kint32max, ARRAYSIZE will overflow. In such cases we should omit the
+// generation of the ARRAYSIZE constant.
bool ShouldGenerateArraySize(const EnumDescriptor* descriptor) {
arc_i32 max_value = descriptor->value(0)->number();
for (int i = 0; i < descriptor->value_count(); i++) {
@@ -61,251 +84,323 @@ bool ShouldGenerateArraySize(const EnumDescriptor* descriptor) {
}
return max_value != std::numeric_limits<arc_i32>::max();
}
-
-// Returns the number of unique numeric enum values. This is less than
-// descriptor->value_count() when there are aliased values.
-int CountUniqueValues(const EnumDescriptor* descriptor) {
- std::set<int> values;
- for (int i = 0; i < descriptor->value_count(); ++i) {
- values.insert(descriptor->value(i)->number());
+} // namespace
+EnumGenerator::ValueLimits EnumGenerator::ValueLimits::FromEnum(
+ const EnumDescriptor* descriptor) {
+ const EnumValueDescriptor* min_desc = descriptor->value(0);
+ const EnumValueDescriptor* max_desc = descriptor->value(0);
+
+ for (int i = 1; i < descriptor->value_count(); ++i) {
+ if (descriptor->value(i)->number() < min_desc->number()) {
+ min_desc = descriptor->value(i);
+ }
+ if (descriptor->value(i)->number() > max_desc->number()) {
+ max_desc = descriptor->value(i);
+ }
}
- return values.size();
-}
-} // namespace
+ return EnumGenerator::ValueLimits{min_desc, max_desc};
+}
EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
- const std::map<TProtoStringType, TProtoStringType>& vars,
const Options& options)
- : descriptor_(descriptor),
- classname_(ClassName(descriptor, false)),
+ : enum_(descriptor),
options_(options),
generate_array_size_(ShouldGenerateArraySize(descriptor)),
- variables_(vars) {
- variables_["classname"] = classname_;
- variables_["classtype"] = QualifiedClassName(descriptor_, options);
- variables_["short_name"] = descriptor_->name();
- variables_["nested_name"] = descriptor_->name();
- variables_["resolved_name"] = ResolveKeyword(descriptor_->name());
- variables_["prefix"] =
- (descriptor_->containing_type() == nullptr) ? "" : classname_ + "_";
+ has_reflection_(HasDescriptorMethods(enum_->file(), options_)),
+ limits_(ValueLimits::FromEnum(enum_)) {
+ // The conditions here for what is "sparse" are not rigorously
+ // chosen.
+ size_t values_range = static_cast<size_t>(limits_.max->number()) -
+ static_cast<size_t>(limits_.min->number());
+ size_t total_values = static_cast<size_t>(enum_->value_count());
+ should_cache_ = has_reflection_ &&
+ (values_range < 16u || values_range < total_values * 2u);
}
-EnumGenerator::~EnumGenerator() {}
-
-void EnumGenerator::GenerateDefinition(io::Printer* printer) {
- Formatter format(printer, variables_);
- format("enum ${1$$classname$$}$ : int {\n", descriptor_);
- format.Indent();
-
- const EnumValueDescriptor* min_value = descriptor_->value(0);
- const EnumValueDescriptor* max_value = descriptor_->value(0);
-
- for (int i = 0; i < descriptor_->value_count(); i++) {
- auto format_value = format;
- format_value.Set("name", EnumValueName(descriptor_->value(i)));
- // 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.
- format_value.Set("number", Int32ToString(descriptor_->value(i)->number()));
- format_value.Set("deprecation",
- DeprecatedAttribute(options_, descriptor_->value(i)));
-
- if (i > 0) format_value(",\n");
- format_value("${1$$prefix$$name$$}$ $deprecation$= $number$",
- descriptor_->value(i));
-
- if (descriptor_->value(i)->number() < min_value->number()) {
- min_value = descriptor_->value(i);
- }
- if (descriptor_->value(i)->number() > max_value->number()) {
- max_value = descriptor_->value(i);
- }
- }
-
- if (descriptor_->file()->syntax() == FileDescriptor::SYNTAX_PROTO3) {
- // For new enum semantics: generate min and max sentinel values equal to
- // INT32_MIN and INT32_MAX
- if (descriptor_->value_count() > 0) format(",\n");
- format(
- "$classname$_$prefix$INT_MIN_SENTINEL_DO_NOT_USE_ = "
- "std::numeric_limits<$int32$>::min(),\n"
- "$classname$_$prefix$INT_MAX_SENTINEL_DO_NOT_USE_ = "
- "std::numeric_limits<$int32$>::max()");
- }
-
- format.Outdent();
- format("\n};\n");
-
- format(
- "$dllexport_decl $bool $classname$_IsValid(int value);\n"
- "constexpr $classname$ ${1$$prefix$$short_name$_MIN$}$ = "
- "$prefix$$2$;\n"
- "constexpr $classname$ ${1$$prefix$$short_name$_MAX$}$ = "
- "$prefix$$3$;\n",
- descriptor_, EnumValueName(min_value), EnumValueName(max_value));
+void EnumGenerator::GenerateDefinition(io::Printer* p) {
+ auto v1 = p->WithVars(EnumVars(enum_, options_, limits_.min, limits_.max));
+
+ auto v2 = p->WithVars({
+ Sub("Msg_Enum_Enum_MIN",
+ y_absl::StrCat(p->LookupVar("Msg_Enum_"), enum_->name(), "_MIN"))
+ .AnnotatedAs(enum_),
+ Sub("Msg_Enum_Enum_MAX",
+ y_absl::StrCat(p->LookupVar("Msg_Enum_"), enum_->name(), "_MAX"))
+ .AnnotatedAs(enum_),
+ });
+ p->Emit(
+ {
+ {"values",
+ [&] {
+ for (int i = 0; i < enum_->value_count(); ++i) {
+ const auto* value = enum_->value(i);
+ p->Emit(
+ {
+ Sub("Msg_Enum_VALUE",
+ y_absl::StrCat(p->LookupVar("Msg_Enum_"),
+ EnumValueName(value)))
+ .AnnotatedAs(value),
+ {"kNumber", Int32ToString(value->number())},
+ {"DEPRECATED", value->options().deprecated()
+ ? "PROTOBUF_DEPRECATED_ENUM"
+ : ""},
+ },
+ R"cc(
+ $Msg_Enum_VALUE$$ DEPRECATED$ = $kNumber$,
+ )cc");
+ }
+ }},
+ // Only emit annotations for the $Msg_Enum$ used in the `enum`
+ // definition.
+ Sub("Msg_Enum_annotated", p->LookupVar("Msg_Enum"))
+ .AnnotatedAs(enum_),
+ {"open_enum_sentinels",
+ [&] {
+ if (enum_->is_closed()) {
+ return;
+ }
+
+ // For open enum semantics: generate min and max sentinel values
+ // equal to INT32_MIN and INT32_MAX
+ p->Emit({{"Msg_Enum_Msg_Enum_",
+ y_absl::StrCat(p->LookupVar("Msg_Enum"), "_",
+ p->LookupVar("Msg_Enum_"))}},
+ R"cc(
+ $Msg_Enum_Msg_Enum_$INT_MIN_SENTINEL_DO_NOT_USE_ =
+ std::numeric_limits<::arc_i32>::min(),
+ $Msg_Enum_Msg_Enum_$INT_MAX_SENTINEL_DO_NOT_USE_ =
+ std::numeric_limits<::arc_i32>::max(),
+ )cc");
+ }},
+ },
+ R"cc(
+ enum $Msg_Enum_annotated$ : int {
+ $values$,
+ $open_enum_sentinels$,
+ };
+
+ $dllexport_decl $bool $Msg_Enum$_IsValid(int value);
+ constexpr $Msg_Enum$ $Msg_Enum_Enum_MIN$ = static_cast<$Msg_Enum$>($kMin$);
+ constexpr $Msg_Enum$ $Msg_Enum_Enum_MAX$ = static_cast<$Msg_Enum$>($kMax$);
+ )cc");
if (generate_array_size_) {
- format(
- "constexpr int ${1$$prefix$$short_name$_ARRAYSIZE$}$ = "
- "$prefix$$short_name$_MAX + 1;\n\n",
- descriptor_);
+ p->Emit({Sub("Msg_Enum_Enum_ARRAYSIZE",
+ y_absl::StrCat(p->LookupVar("Msg_Enum_"), enum_->name(),
+ "_ARRAYSIZE"))
+ .AnnotatedAs(enum_)},
+ R"cc(
+ constexpr int $Msg_Enum_Enum_ARRAYSIZE$ = $kMax$ + 1;
+ )cc");
}
- if (HasDescriptorMethods(descriptor_->file(), options_)) {
- format(
- "$dllexport_decl $const ::$proto_ns$::EnumDescriptor* "
- "$classname$_descriptor();\n");
+ if (has_reflection_) {
+ p->Emit(R"cc(
+ $dllexport_decl $const ::$proto_ns$::EnumDescriptor*
+ $Msg_Enum$_descriptor();
+ )cc");
+ } else {
+ p->Emit(R"cc(
+ const TProtoStringType& $Msg_Enum$_Name($Msg_Enum$ value);
+ )cc");
}
- // The _Name and _Parse functions. The lite implementation is table-based, so
- // we make sure to keep the tables hidden in the .cc file.
- if (!HasDescriptorMethods(descriptor_->file(), options_)) {
- format("const TProtoStringType& $classname$_Name($classname$ value);\n");
- }
- // The _Name() function accepts the enum type itself but also any integral
- // type.
- format(
- "template<typename T>\n"
- "inline const TProtoStringType& $classname$_Name(T enum_t_value) {\n"
- " static_assert(::std::is_same<T, $classname$>::value ||\n"
- " ::std::is_integral<T>::value,\n"
- " \"Incorrect type passed to function $classname$_Name.\");\n");
- if (HasDescriptorMethods(descriptor_->file(), options_)) {
- format(
- " return ::$proto_ns$::internal::NameOfEnum(\n"
- " $classname$_descriptor(), enum_t_value);\n");
+ // There are three possible implementations of $Enum$_Name() and
+ // $Msg_Enum$_Parse(), depending on whether we are using a dense enum name
+ // cache or not, and whether or not we have reflection. Very little code is
+ // shared between the three, so it is split into three Emit() calls.
+
+ // Can't use WithVars here, since callbacks can only be passed to Emit()
+ // directly. Because this includes $Enum$, it must be a callback.
+ auto write_assert = [&] {
+ p->Emit(R"cc(
+ static_assert(std::is_same<T, $Msg_Enum$>::value ||
+ std::is_integral<T>::value,
+ "Incorrect type passed to $Enum$_Name().");
+ )cc");
+ };
+
+ if (should_cache_ || !has_reflection_) {
+ p->Emit({{"static_assert", write_assert}}, R"cc(
+ template <typename T>
+ const TProtoStringType& $Msg_Enum$_Name(T value) {
+ $static_assert$;
+ return $Msg_Enum$_Name(static_cast<$Msg_Enum$>(value));
+ }
+ )cc");
+ if (should_cache_) {
+ // Using the NameOfEnum routine can be slow, so we create a small
+ // cache of pointers to the TProtoStringType objects that reflection
+ // stores internally. This cache is a simple contiguous array of
+ // pointers, so if the enum values are sparse, it's not worth it.
+ p->Emit(R"cc(
+ template <>
+ inline const TProtoStringType& $Msg_Enum$_Name($Msg_Enum$ value) {
+ return ::$proto_ns$::internal::NameOfDenseEnum<$Msg_Enum$_descriptor,
+ $kMin$, $kMax$>(
+ static_cast<int>(value));
+ }
+ )cc");
+ } else {
+ p->Emit(R"cc(
+ const TProtoStringType& $Msg_Enum$_Name($Msg_Enum$ value);
+ )cc");
+ }
} else {
- format(
- " return $classname$_Name(static_cast<$classname$>(enum_t_value));\n");
+ p->Emit({{"static_assert", write_assert}}, R"cc(
+ template <typename T>
+ const TProtoStringType& $Msg_Enum$_Name(T value) {
+ $static_assert$;
+ return ::$proto_ns$::internal::NameOfEnum($Msg_Enum$_descriptor(), value);
+ }
+ )cc");
}
- format("}\n");
-
- if (HasDescriptorMethods(descriptor_->file(), options_)) {
- format(
- "inline bool $classname$_Parse(\n"
- " ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, $classname$* "
- "value) "
- "{\n"
- " return ::$proto_ns$::internal::ParseNamedEnum<$classname$>(\n"
- " $classname$_descriptor(), name, value);\n"
- "}\n");
+
+ if (has_reflection_) {
+ p->Emit(R"cc(
+ inline bool $Msg_Enum$_Parse(y_absl::string_view name, $Msg_Enum$* value) {
+ return ::$proto_ns$::internal::ParseNamedEnum<$Msg_Enum$>(
+ $Msg_Enum$_descriptor(), name, value);
+ }
+ )cc");
} else {
- format(
- "bool $classname$_Parse(\n"
- " ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, $classname$* "
- "value);\n");
+ p->Emit(R"cc(
+ bool $Msg_Enum$_Parse(y_absl::string_view name, $Msg_Enum$* value);
+ )cc");
}
}
-void EnumGenerator::GenerateGetEnumDescriptorSpecializations(
- io::Printer* printer) {
- Formatter format(printer, variables_);
- format(
- "template <> struct is_proto_enum< $classtype$> : ::std::true_type "
- "{};\n");
- if (HasDescriptorMethods(descriptor_->file(), options_)) {
- format(
- "template <>\n"
- "inline const EnumDescriptor* GetEnumDescriptor< $classtype$>() {\n"
- " return $classtype$_descriptor();\n"
- "}\n");
+void EnumGenerator::GenerateGetEnumDescriptorSpecializations(io::Printer* p) {
+ auto v = p->WithVars(EnumVars(enum_, options_, limits_.min, limits_.max));
+
+ p->Emit(R"cc(
+ template <>
+ struct is_proto_enum<$::Msg_Enum$> : std::true_type {};
+ )cc");
+ if (!has_reflection_) {
+ return;
}
+ p->Emit(R"cc(
+ template <>
+ inline const EnumDescriptor* GetEnumDescriptor<$::Msg_Enum$>() {
+ return $::Msg_Enum$_descriptor();
+ }
+ )cc");
}
-void EnumGenerator::GenerateSymbolImports(io::Printer* printer) const {
- Formatter format(printer, variables_);
- format("typedef $classname$ $resolved_name$;\n");
-
- for (int j = 0; j < descriptor_->value_count(); j++) {
- TProtoStringType deprecated_attr =
- DeprecatedAttribute(options_, descriptor_->value(j));
- format(
- "$1$static constexpr $resolved_name$ ${2$$3$$}$ =\n"
- " $classname$_$3$;\n",
- deprecated_attr, descriptor_->value(j),
- EnumValueName(descriptor_->value(j)));
+void EnumGenerator::GenerateSymbolImports(io::Printer* p) const {
+ auto v = p->WithVars(EnumVars(enum_, options_, limits_.min, limits_.max));
+
+ p->Emit({Sub("Enum_", p->LookupVar("Enum_")).AnnotatedAs(enum_)}, R"cc(
+ using $Enum_$ = $Msg_Enum$;
+ )cc");
+
+ for (int j = 0; j < enum_->value_count(); ++j) {
+ const auto* value = enum_->value(j);
+ p->Emit(
+ {
+ Sub("VALUE", EnumValueName(enum_->value(j))).AnnotatedAs(value),
+ {"DEPRECATED",
+ value->options().deprecated() ? "PROTOBUF_DEPRECATED_ENUM" : ""},
+ },
+ R"cc(
+ $DEPRECATED $static constexpr $Enum_$ $VALUE$ = $Msg_Enum$_$VALUE$;
+ )cc");
}
- format(
- "static inline bool $nested_name$_IsValid(int value) {\n"
- " return $classname$_IsValid(value);\n"
- "}\n"
- "static constexpr $resolved_name$ ${1$$nested_name$_MIN$}$ =\n"
- " $classname$_$nested_name$_MIN;\n"
- "static constexpr $resolved_name$ ${1$$nested_name$_MAX$}$ =\n"
- " $classname$_$nested_name$_MAX;\n",
- descriptor_);
+ p->Emit(
+ {
+ Sub("Enum_MIN", y_absl::StrCat(enum_->name(), "_MIN"))
+ .AnnotatedAs(enum_),
+ Sub("Enum_MAX", y_absl::StrCat(enum_->name(), "_MAX"))
+ .AnnotatedAs(enum_),
+ },
+ R"cc(
+ static inline bool $Enum$_IsValid(int value) {
+ return $Msg_Enum$_IsValid(value);
+ }
+ static constexpr $Enum_$ $Enum_MIN$ = $Msg_Enum$_$Enum$_MIN;
+ static constexpr $Enum_$ $Enum_MAX$ = $Msg_Enum$_$Enum$_MAX;
+ )cc");
+
if (generate_array_size_) {
- format(
- "static constexpr int ${1$$nested_name$_ARRAYSIZE$}$ =\n"
- " $classname$_$nested_name$_ARRAYSIZE;\n",
- descriptor_);
+ p->Emit(
+ {
+ Sub("Enum_ARRAYSIZE", y_absl::StrCat(enum_->name(), "_ARRAYSIZE"))
+ .AnnotatedAs(enum_),
+ },
+ R"cc(
+ static constexpr int $Enum_ARRAYSIZE$ = $Msg_Enum$_$Enum$_ARRAYSIZE;
+ )cc");
}
- if (HasDescriptorMethods(descriptor_->file(), options_)) {
- format(
- "static inline const ::$proto_ns$::EnumDescriptor*\n"
- "$nested_name$_descriptor() {\n"
- " return $classname$_descriptor();\n"
- "}\n");
+ if (has_reflection_) {
+ p->Emit(R"cc(
+ static inline const ::$proto_ns$::EnumDescriptor* $Enum$_descriptor() {
+ return $Msg_Enum$_descriptor();
+ }
+ )cc");
}
- format(
- "template<typename T>\n"
- "static inline const TProtoStringType& $nested_name$_Name(T enum_t_value) {\n"
- " static_assert(::std::is_same<T, $resolved_name$>::value ||\n"
- " ::std::is_integral<T>::value,\n"
- " \"Incorrect type passed to function $nested_name$_Name.\");\n"
- " return $classname$_Name(enum_t_value);\n"
- "}\n");
- format(
- "static inline bool "
- "$nested_name$_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name,\n"
- " $resolved_name$* value) {\n"
- " return $classname$_Parse(name, value);\n"
- "}\n");
+ p->Emit(R"cc(
+ template <typename T>
+ static inline const TProtoStringType& $Enum$_Name(T value) {
+ return $Msg_Enum$_Name(value);
+ }
+ static inline bool $Enum$_Parse(y_absl::string_view name, $Enum_$* value) {
+ return $Msg_Enum$_Parse(name, value);
+ }
+ )cc");
}
-void EnumGenerator::GenerateMethods(int idx, io::Printer* printer) {
- Formatter format(printer, variables_);
- if (HasDescriptorMethods(descriptor_->file(), options_)) {
- format(
- "const ::$proto_ns$::EnumDescriptor* $classname$_descriptor() {\n"
- " ::$proto_ns$::internal::AssignDescriptors(&$desc_table$);\n"
- " return $file_level_enum_descriptors$[$1$];\n"
- "}\n",
- idx);
- }
-
- format(
- "bool $classname$_IsValid(int value) {\n"
- " switch (value) {\n");
-
- // Multiple values may have the same number. Make sure we only cover
- // each number once by first constructing a set containing all valid
- // numbers, then printing a case statement for each element.
-
- std::set<int> numbers;
- for (int j = 0; j < descriptor_->value_count(); j++) {
- const EnumValueDescriptor* value = descriptor_->value(j);
- numbers.insert(value->number());
- }
+void EnumGenerator::GenerateMethods(int idx, io::Printer* p) {
+ auto v = p->WithVars(EnumVars(enum_, options_, limits_.min, limits_.max));
- for (std::set<int>::iterator iter = numbers.begin(); iter != numbers.end();
- ++iter) {
- format(" case $1$:\n", Int32ToString(*iter));
+ if (has_reflection_) {
+ p->Emit({{"idx", idx}}, R"cc(
+ const ::$proto_ns$::EnumDescriptor* $Msg_Enum$_descriptor() {
+ ::$proto_ns$::internal::AssignDescriptors(&$desc_table$);
+ return $file_level_enum_descriptors$[$idx$];
+ }
+ )cc");
}
- format(
- " return true;\n"
- " default:\n"
- " return false;\n"
- " }\n"
- "}\n"
- "\n");
-
- if (!HasDescriptorMethods(descriptor_->file(), options_)) {
+ p->Emit({{"cases",
+ [&] {
+ // Multiple values may have the same number. Make sure we only
+ // cover each number once by first constructing a set containing
+ // all valid numbers, then printing a case statement for each
+ // element.
+
+ std::vector<int> numbers;
+ numbers.reserve(enum_->value_count());
+ for (int i = 0; i < enum_->value_count(); ++i) {
+ numbers.push_back(enum_->value(i)->number());
+ }
+ // Sort and deduplicate `numbers`.
+ y_absl::c_sort(numbers);
+ numbers.erase(std::unique(numbers.begin(), numbers.end()),
+ numbers.end());
+
+ for (int n : numbers) {
+ p->Emit({{"n", n}}, R"cc(
+ case $n$:
+ )cc");
+ }
+ }}},
+ R"(
+ bool $Msg_Enum$_IsValid(int value) {
+ switch (value) {
+ $cases$;
+ return true;
+ default:
+ return false;
+ }
+ }
+ )");
+
+ if (!has_reflection_) {
// In lite mode (where descriptors are unavailable), we generate separate
// tables for mapping between enum names and numbers. The _entries table
// contains the bulk of the data and is sorted by name, while
@@ -319,119 +414,168 @@ void EnumGenerator::GenerateMethods(int idx, io::Printer* printer) {
// numerical value. In cases where there are multiple names for the same
// number, we treat the first name appearing in the .proto file as the
// canonical one.
- std::map<TProtoStringType, int> name_to_number;
- std::map<int, TProtoStringType> number_to_canonical_name;
- for (int i = 0; i < descriptor_->value_count(); i++) {
- const EnumValueDescriptor* value = descriptor_->value(i);
+
+ y_absl::btree_map<TProtoStringType, int> name_to_number;
+ y_absl::flat_hash_map<int, TProtoStringType> number_to_canonical_name;
+ for (int i = 0; i < enum_->value_count(); ++i) {
+ const auto* value = enum_->value(i);
name_to_number.emplace(value->name(), value->number());
+
// The same number may appear with multiple names, so we use emplace() to
// let the first name win.
number_to_canonical_name.emplace(value->number(), value->name());
}
- format(
- "static ::$proto_ns$::internal::ExplicitlyConstructed<TProtoStringType> "
- "$classname$_strings[$1$] = {};\n\n",
- CountUniqueValues(descriptor_));
-
- // We concatenate all the names for a given enum into one big string
- // literal. If instead we store an array of string literals, the linker
- // seems to put all enum strings for a given .proto file in the same
- // section, which hinders its ability to strip out unused strings.
- format("static const char $classname$_names[] =");
- for (const auto& p : name_to_number) {
- format("\n \"$1$\"", p.first);
+ // Build the offset table for the strings table.
+ struct Offset {
+ int number;
+ size_t index, byte_offset, len;
+ };
+ std::vector<Offset> offsets;
+ size_t index = 0;
+ size_t offset = 0;
+ for (const auto& e : name_to_number) {
+ offsets.push_back(Offset{e.second, index, offset, e.first.size()});
+ ++index;
+ offset += e.first.size();
}
- format(";\n\n");
-
- format(
- "static const ::$proto_ns$::internal::EnumEntry $classname$_entries[] "
- "= {\n");
- int i = 0;
- std::map<int, int> number_to_index;
- int data_index = 0;
- for (const auto& p : name_to_number) {
- format(" { {$classname$_names + $1$, $2$}, $3$ },\n", data_index,
- p.first.size(), p.second);
- if (number_to_canonical_name[p.second] == p.first) {
- number_to_index.emplace(p.second, i);
- }
- ++i;
- data_index += p.first.size();
- }
-
- format(
- "};\n"
- "\n"
- "static const int $classname$_entries_by_number[] = {\n");
- for (const auto& p : number_to_index) {
- format(" $1$, // $2$ -> $3$\n", p.second, p.first,
- number_to_canonical_name[p.first]);
- }
- format(
- "};\n"
- "\n");
-
- format(
- "const TProtoStringType& $classname$_Name(\n"
- " $classname$ value) {\n"
- " static const bool dummy =\n"
- " ::$proto_ns$::internal::InitializeEnumStrings(\n"
- " $classname$_entries,\n"
- " $classname$_entries_by_number,\n"
- " $1$, $classname$_strings);\n"
- " (void) dummy;\n"
- " int idx = ::$proto_ns$::internal::LookUpEnumName(\n"
- " $classname$_entries,\n"
- " $classname$_entries_by_number,\n"
- " $1$, value);\n"
- " return idx == -1 ? ::$proto_ns$::internal::GetEmptyString() :\n"
- " $classname$_strings[idx].get();\n"
- "}\n",
- CountUniqueValues(descriptor_));
- format(
- "bool $classname$_Parse(\n"
- " ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, $classname$* "
- "value) "
- "{\n"
- " int int_value;\n"
- " bool success = ::$proto_ns$::internal::LookUpEnumValue(\n"
- " $classname$_entries, $1$, name, &int_value);\n"
- " if (success) {\n"
- " *value = static_cast<$classname$>(int_value);\n"
- " }\n"
- " return success;\n"
- "}\n",
- descriptor_->value_count());
+ y_absl::c_sort(offsets, [](const auto& a, const auto& b) {
+ return a.byte_offset < b.byte_offset;
+ });
+
+ std::vector<Offset> offsets_by_number = offsets;
+ y_absl::c_sort(offsets_by_number, [](const auto& a, const auto& b) {
+ return a.number < b.number;
+ });
+
+ offsets_by_number.erase(
+ std::unique(
+ offsets_by_number.begin(), offsets_by_number.end(),
+ [](const auto& a, const auto& b) { return a.number == b.number; }),
+ offsets_by_number.end());
+
+ p->Emit(
+ {
+ {"num_unique", number_to_canonical_name.size()},
+ {"num_declared", enum_->value_count()},
+ {"names",
+ // We concatenate all the names for a given enum into one big
+ // string literal. If instead we store an array of string
+ // literals, the linker seems to put all enum strings for a given
+ // .proto file in the same section, which hinders its ability to
+ // strip out unused strings.
+ [&] {
+ for (const auto& e : name_to_number) {
+ p->Emit({{"name", e.first}}, R"cc(
+ "$name$"
+ )cc");
+ }
+ }},
+ {"entries",
+ [&] {
+ for (const auto& offset : offsets) {
+ p->Emit({{"number", offset.number},
+ {"offset", offset.byte_offset},
+ {"len", offset.len}},
+ R"cc(
+ {{&$Msg_Enum$_names[$offset$], $len$}, $number$},
+ )cc");
+ }
+ }},
+ {"entries_by_number",
+ [&] {
+ for (const auto& offset : offsets_by_number) {
+ p->Emit({{"number", offset.number},
+ {"index", offset.index},
+ {"name", number_to_canonical_name[offset.number]}},
+ R"cc(
+ $index$, // $number$ -> $name$
+ )cc");
+ }
+ }},
+ },
+ R"cc(
+ static ::$proto_ns$::internal::ExplicitlyConstructed<TProtoStringType>
+ $Msg_Enum$_strings[$num_unique$] = {};
+
+ static const char $Msg_Enum$_names[] = {
+ $names$,
+ };
+
+ static const ::$proto_ns$::internal::EnumEntry $Msg_Enum$_entries[] =
+ {
+ $entries$,
+ };
+
+ static const int $Msg_Enum$_entries_by_number[] = {
+ $entries_by_number$,
+ };
+
+ const TProtoStringType& $Msg_Enum$_Name($Msg_Enum$ value) {
+ static const bool kDummy =
+ ::$proto_ns$::internal::InitializeEnumStrings(
+ $Msg_Enum$_entries, $Msg_Enum$_entries_by_number,
+ $num_unique$, $Msg_Enum$_strings);
+ (void)kDummy;
+
+ int idx = ::$proto_ns$::internal::LookUpEnumName(
+ $Msg_Enum$_entries, $Msg_Enum$_entries_by_number, $num_unique$,
+ value);
+ return idx == -1 ? ::$proto_ns$::internal::GetEmptyString()
+ : $Msg_Enum$_strings[idx].get();
+ }
+
+ bool $Msg_Enum$_Parse(y_absl::string_view name, $Msg_Enum$* value) {
+ int int_value;
+ bool success = ::$proto_ns$::internal::LookUpEnumValue(
+ $Msg_Enum$_entries, $num_declared$, name, &int_value);
+ if (success) {
+ *value = static_cast<$Msg_Enum$>(int_value);
+ }
+ return success;
+ }
+ )cc");
}
- if (descriptor_->containing_type() != nullptr) {
- TProtoStringType parent = ClassName(descriptor_->containing_type(), false);
+ if (enum_->containing_type() != nullptr) {
// Before C++17, we must define the static constants which were
// declared in the header, to give the linker a place to put them.
// But MSVC++ pre-2015 and post-2017 (version 15.5+) insists that we not.
- format(
- "#if (__cplusplus < 201703) && "
- "(!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))\n");
-
- for (int i = 0; i < descriptor_->value_count(); i++) {
- format("constexpr $classname$ $1$::$2$;\n", parent,
- EnumValueName(descriptor_->value(i)));
- }
- format(
- "constexpr $classname$ $1$::$nested_name$_MIN;\n"
- "constexpr $classname$ $1$::$nested_name$_MAX;\n",
- parent);
- if (generate_array_size_) {
- format("constexpr int $1$::$nested_name$_ARRAYSIZE;\n", parent);
- }
-
- format(
- "#endif // (__cplusplus < 201703) && "
- "(!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))\n");
+ p->Emit(
+ {
+ {"Msg_", ClassName(enum_->containing_type(), false)},
+ {"constexpr_storage",
+ [&] {
+ for (int i = 0; i < enum_->value_count(); i++) {
+ p->Emit({{"VALUE", EnumValueName(enum_->value(i))}},
+ R"cc(
+ constexpr $Msg_Enum$ $Msg_$::$VALUE$;
+ )cc");
+ }
+ }},
+ {"array_size",
+ [&] {
+ if (generate_array_size_) {
+ p->Emit(R"cc(
+ constexpr int $Msg_$::$Enum$_ARRAYSIZE;
+ )cc");
+ }
+ }},
+ },
+ R"(
+ #if (__cplusplus < 201703) && \
+ (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+
+ $constexpr_storage$;
+ constexpr $Msg_Enum$ $Msg_$::$Enum$_MIN;
+ constexpr $Msg_Enum$ $Msg_$::$Enum$_MAX;
+ $array_size$;
+
+ #endif // (__cplusplus < 201703) &&
+ // (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+ )");
}
}
-
} // namespace cpp
} // namespace compiler
} // namespace protobuf