blob: fbfda2d0210a8cefe3f5f1191e8afa6beac4d6d6 [file]
// 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 <cstddef>
#include <cstdint>
#include <string>
#include <utility>
#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<const TYPE*>(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<SymbolBaseN<0>*>(value);
ptr->symbol_type_ = ENUM_VALUE;
} else {
ptr = static_cast<SymbolBaseN<1>*>(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<const EnumValueDescriptor*>(
static_cast<const SymbolBaseN<0>*>(ptr_))
: type() == ENUM_VALUE_OTHER_PARENT
? static_cast<const EnumValueDescriptor*>(
static_cast<const SymbolBaseN<1>*>(ptr_))
: nullptr;
}
#undef DEFINE_MEMBERS
// Returns the type of the underlying descriptor.
Type type() const { return static_cast<Type>(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<const void*, absl::string_view> 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 <typename T>
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 <typename T, typename U>
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<Symbol, SymbolByFullNameHash, SymbolByFullNameEq>;
} // namespace internal
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_SYMBOL_H__