// Protocol Buffers - Google's data interchange format // Copyright 2026 Google Inc. 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 #ifndef GOOGLE_PROTOBUF_SYMBOL_H__ #define GOOGLE_PROTOBUF_SYMBOL_H__ #include #include #include #include #include "absl/base/optimization.h" #include "absl/container/flat_hash_set.h" #include "absl/hash/hash.h" #include "absl/log/absl_check.h" #include "absl/strings/string_view.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/descriptor.pb.h" namespace google { namespace protobuf { namespace internal { // Symbol is a variant-like wrapper class that holds a pointer to any type of // Protobuf entity during the descriptor building process. It allows the // DescriptorBuilder and DescriptorPool to store and look up different // descriptor types (e.g., Message, Field, Enum, Service) uniformly in their // internal hash tables. It relies on the `SymbolBase` classes to // store the type enum. class Symbol { public: enum Type { NULL_SYMBOL, MESSAGE, FIELD, ONEOF, ENUM, ENUM_VALUE, ENUM_VALUE_OTHER_PARENT, SERVICE, METHOD, FULL_PACKAGE, SUB_PACKAGE, }; Symbol() { static constexpr SymbolBase null_symbol{}; static_assert(null_symbol.symbol_type_ == NULL_SYMBOL, ""); // Initialize with a sentinel to make sure `ptr_` is never null. ptr_ = &null_symbol; } explicit Symbol(const SymbolBase* ptr) : ptr_(ptr) {} // Every object we store derives from SymbolBase, where we store the // symbol type enum. // Storing in the object can be done without using more space in most cases, // while storing it in the Symbol type would require 8 bytes. #define DEFINE_MEMBERS(TYPE, TYPE_CONSTANT, FIELD) \ explicit Symbol(TYPE* value) : ptr_(value) { \ value->symbol_type_ = TYPE_CONSTANT; \ } \ const TYPE* FIELD() const { \ return type() == TYPE_CONSTANT ? static_cast(ptr_) : nullptr; \ } DEFINE_MEMBERS(Descriptor, MESSAGE, descriptor) DEFINE_MEMBERS(FieldDescriptor, FIELD, field_descriptor) DEFINE_MEMBERS(OneofDescriptor, ONEOF, oneof_descriptor) DEFINE_MEMBERS(EnumDescriptor, ENUM, enum_descriptor) DEFINE_MEMBERS(ServiceDescriptor, SERVICE, service_descriptor) DEFINE_MEMBERS(MethodDescriptor, METHOD, method_descriptor) DEFINE_MEMBERS(FileDescriptor, FULL_PACKAGE, file_descriptor) // We use a special node for subpackage FileDescriptor. // It is potentially added to the table with multiple different names, so we // need a separate place to put the name. struct Subpackage : SymbolBase { int name_size; const FileDescriptor* file; }; DEFINE_MEMBERS(Subpackage, SUB_PACKAGE, sub_package_file_descriptor) // Enum values have two different parents. // We use two different identities for the same object to determine the two // different insertions in the map. static Symbol EnumValue(EnumValueDescriptor* value, int n) { Symbol s; SymbolBase* ptr; if (n == 0) { ptr = static_cast*>(value); ptr->symbol_type_ = ENUM_VALUE; } else { ptr = static_cast*>(value); ptr->symbol_type_ = ENUM_VALUE_OTHER_PARENT; } s.ptr_ = ptr; return s; } const EnumValueDescriptor* enum_value_descriptor() const { return type() == ENUM_VALUE ? static_cast( static_cast*>(ptr_)) : type() == ENUM_VALUE_OTHER_PARENT ? static_cast( static_cast*>(ptr_)) : nullptr; } #undef DEFINE_MEMBERS // Returns the type of the underlying descriptor. Type type() const { return static_cast(ptr_->symbol_type_); } // Returns true if this Symbol does not point to any valid descriptor. bool IsNull() const { return type() == NULL_SYMBOL; } // Returns true if this Symbol represents a type definition (Message or Enum). bool IsType() const { return type() == MESSAGE || type() == ENUM; } // Returns true if this Symbol represents an entity that can contain other // entities (Message, Enum, Package, or Service). bool IsAggregate() const { return IsType() || IsPackage() || type() == SERVICE; } // Returns true if this Symbol represents a package (full or sub-package). bool IsPackage() const { return type() == FULL_PACKAGE || type() == SUB_PACKAGE; } // Returns the FileDescriptor where this symbol is defined, or nullptr if // it is not associated with a specific file (e.g., NULL_SYMBOL). const FileDescriptor* GetFile() const; // Returns the fully qualified name of the symbol (e.g., "foo.bar.MyMessage"). absl::string_view full_name() const; // Returns a unique key used for hashing symbols by their parent entity // and their local name. std::pair parent_name_key() const; // Returns the resolved FeatureSet applied to this symbol. const FeatureSet& features() const; // Returns true if this symbol is a placeholder (an unresolved dependency). bool is_placeholder() const; // Returns the explicit visibility keyword applied to this symbol, if any. SymbolVisibility visibility_keyword() const; // Returns true if this symbol is defined within another type (e.g., a nested // message or enum). bool IsNestedDefinition() const; // Calculates and returns the effective visibility of this symbol, taking // into account its explicit keyword, its features, and its nesting level. SymbolVisibility GetEffectiveVisibility() const; // Determines whether this symbol can be referenced from the given file, // respecting protobuf visibility rules. bool IsVisibleFrom(FileDescriptor* other) const; // Returns a detailed error message explaining why this symbol is not // visible from the given file. std::string GetVisibilityError(FileDescriptor* other, absl::string_view usage = "") const; const SymbolBase* ptr() const { return ptr_; } private: const SymbolBase* ptr_; }; // A DescriptorPool contains a bunch of hash-maps to implement the // various Find*By*() methods. Since hashtable lookups are O(1), it's // most efficient to construct a fixed set of large hash-maps used by // all objects in the pool rather than construct one or more small // hash-maps for each object. // // The keys to these hash-maps are (parent, name) or (parent, number) pairs. // A query object used to find a Symbol given its fully qualified name. struct FullNameQuery { absl::string_view query; absl::string_view full_name() const { return query; } }; // Hash functor for Symbol, based on its fully qualified name. struct SymbolByFullNameHash { using is_transparent = void; template size_t operator()(const T& s) const { return absl::HashOf(s.full_name()); } }; // Equality functor for Symbol, based on its fully qualified name. struct SymbolByFullNameEq { using is_transparent = void; template bool operator()(const T& a, const U& b) const { return a.full_name() == b.full_name(); } }; // A set of Symbols, hashed and compared by their fully qualified name. using SymbolsByNameSet = absl::flat_hash_set; } // namespace internal } // namespace protobuf } // namespace google #endif // GOOGLE_PROTOBUF_SYMBOL_H__