mirror of
https://github.com/protocolbuffers/protobuf
synced 2026-08-26 02:23:14 -04:00
Implement 32-bit offset-based pointers for descriptor types.
Almost all the memory for the descriptors of a .proto file are allocated in a single flat allocation that is limited in size to 2GiB. As such, we can change crosslinking pointers to be 32-bit offset pointers. This significantly reduces the size of the descriptor objects. PiperOrigin-RevId: 958511623
This commit is contained in:
parent
51dedb854d
commit
4e7b3d8142
7 changed files with 553 additions and 179 deletions
|
|
@ -111,6 +111,7 @@ google/protobuf/metadata.h
|
|||
google/protobuf/metadata_lite.h
|
||||
google/protobuf/micro_string.h
|
||||
google/protobuf/naming_style.h
|
||||
google/protobuf/offset_ptr.h
|
||||
google/protobuf/option_interpreter.h
|
||||
google/protobuf/os_macros_restore.inc
|
||||
google/protobuf/os_macros_undef.inc
|
||||
|
|
|
|||
|
|
@ -726,6 +726,7 @@ cc_library(
|
|||
"inlined_string_field.cc",
|
||||
"map.cc",
|
||||
"message_lite.cc",
|
||||
"offset_ptr.cc",
|
||||
"parse_context.cc",
|
||||
"raw_ptr.cc",
|
||||
"repeated_field.cc",
|
||||
|
|
@ -759,6 +760,7 @@ cc_library(
|
|||
"map_type_handler.h",
|
||||
"message_lite.h",
|
||||
"metadata_lite.h",
|
||||
"offset_ptr.h",
|
||||
"parse_context.h",
|
||||
"raw_ptr.h",
|
||||
"repeated_field.h",
|
||||
|
|
@ -988,6 +990,17 @@ cc_test(
|
|||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "offset_ptr_test",
|
||||
srcs = ["offset_ptr_test.cc"],
|
||||
deps = [
|
||||
":cc_test_protos",
|
||||
":protobuf_lite",
|
||||
"@googletest//:gtest",
|
||||
"@googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
# This target exposes the headers for the protobuf runtime, and additionally
|
||||
# depends on the C++ well-known types and some other miscellaneous utilities.
|
||||
# The purpose is to preserve compatibility with projects that do not yet comply
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@
|
|||
#include "google/protobuf/message.h"
|
||||
#include "google/protobuf/message_lite.h"
|
||||
#include "google/protobuf/naming_style.h"
|
||||
#include "google/protobuf/offset_ptr.h"
|
||||
#include "google/protobuf/option_interpreter.h"
|
||||
#include "google/protobuf/parse_context.h"
|
||||
#include "google/protobuf/port.h"
|
||||
|
|
@ -421,7 +422,7 @@ class FlatAllocatorImpl {
|
|||
|
||||
// TODO: Remove the NULL terminators to save memory and simplify
|
||||
// the code.
|
||||
absl::optional<internal::DescriptorNames> CreateDescriptorNames(
|
||||
absl::optional<internal::DescriptorNames::Input> CreateDescriptorNames(
|
||||
std::initializer_list<absl::string_view> bytes,
|
||||
std::initializer_list<size_t> sizes) {
|
||||
for (size_t size : sizes) {
|
||||
|
|
@ -439,7 +440,7 @@ class FlatAllocatorImpl {
|
|||
memcpy(out, b.data(), b.size());
|
||||
out += b.size();
|
||||
}
|
||||
auto res = internal::DescriptorNames(out);
|
||||
auto res = internal::DescriptorNames::Input{out};
|
||||
for (size_t size : sizes) {
|
||||
uint16_t size16 = static_cast<uint16_t>(size);
|
||||
memcpy(out, &size16, sizeof(size16));
|
||||
|
|
@ -452,7 +453,7 @@ class FlatAllocatorImpl {
|
|||
PlanArray<char>(internal::DescriptorNames::AllocationSizeForSimpleNames(
|
||||
full_name_size));
|
||||
}
|
||||
absl::optional<internal::DescriptorNames> AllocateEntityNames(
|
||||
absl::optional<internal::DescriptorNames::Input> AllocateEntityNames(
|
||||
absl::string_view scope, absl::string_view name) {
|
||||
static constexpr absl::string_view kNullChar("\0", 1);
|
||||
if (scope.empty()) {
|
||||
|
|
@ -465,7 +466,7 @@ class FlatAllocatorImpl {
|
|||
}
|
||||
}
|
||||
|
||||
internal::DescriptorNames AllocatePlaceholderNames(
|
||||
internal::DescriptorNames::Input AllocatePlaceholderNames(
|
||||
absl::string_view full_name, size_t name_size) {
|
||||
static constexpr absl::string_view kNullChar("\0", 1);
|
||||
auto out = CreateDescriptorNames({full_name, kNullChar},
|
||||
|
|
@ -533,7 +534,7 @@ class FlatAllocatorImpl {
|
|||
PlanArray<char>(total_bytes);
|
||||
}
|
||||
|
||||
absl::optional<internal::DescriptorNames> AllocateFieldNames(
|
||||
absl::optional<internal::DescriptorNames::Input> AllocateFieldNames(
|
||||
const absl::string_view name, const absl::string_view scope,
|
||||
const std::string* opt_json_name) {
|
||||
ABSL_CHECK(has_allocated());
|
||||
|
|
@ -856,38 +857,11 @@ struct ParentNameQueryBase {
|
|||
}
|
||||
};
|
||||
|
||||
// A 32-bit "offset" based pointer used for hash tables below.
|
||||
// The base pointer is the base of the FlatAllocation, which is the same for
|
||||
// the objects in a single FileDescriptorTables (except the ones allocated later
|
||||
// on demand, like unknown_enum_values_by_number_).
|
||||
// This halves the size of the tables in 64-bit builds.
|
||||
template <typename T>
|
||||
struct OffsetT {
|
||||
OffsetT(T* ptr, absl::string_view flat_buffer) {
|
||||
ptrdiff_t diff = reinterpret_cast<const char*>(ptr) -
|
||||
reinterpret_cast<const char*>(flat_buffer.data());
|
||||
// Verify the pointer is actually in bounds of the buffer.
|
||||
ABSL_DCHECK(static_cast<const void*>(ptr) >= flat_buffer.data() &&
|
||||
static_cast<const void*>(ptr) <
|
||||
flat_buffer.data() + flat_buffer.size());
|
||||
ABSL_DCHECK_GE(diff, 0);
|
||||
ABSL_DCHECK_LE(diff, std::numeric_limits<uint32_t>::max());
|
||||
value = static_cast<uint32_t>(diff);
|
||||
}
|
||||
|
||||
T* Resolve(const void* base_ptr) const {
|
||||
return const_cast<T*>(reinterpret_cast<const T*>(
|
||||
reinterpret_cast<const char*>(base_ptr) + value));
|
||||
}
|
||||
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
const T& ResolveSymbol(const T& v, const void*) {
|
||||
return v;
|
||||
}
|
||||
Symbol ResolveSymbol(OffsetT<const internal::SymbolBase> v,
|
||||
Symbol ResolveSymbol(internal::BasePointer<const internal::SymbolBase, false> v,
|
||||
const void* base_ptr) {
|
||||
return Symbol(v.Resolve(base_ptr));
|
||||
}
|
||||
|
|
@ -916,7 +890,8 @@ struct SymbolByParentHash {
|
|||
|
||||
const void* base_ptr;
|
||||
|
||||
size_t operator()(OffsetT<const internal::SymbolBase> v) const {
|
||||
size_t operator()(
|
||||
internal::BasePointer<const internal::SymbolBase, false> v) const {
|
||||
return (*this)(Symbol(v.Resolve(base_ptr)));
|
||||
}
|
||||
|
||||
|
|
@ -930,8 +905,9 @@ struct SymbolByParentEq {
|
|||
|
||||
const void* base_ptr;
|
||||
|
||||
bool operator()(OffsetT<const internal::SymbolBase> offset,
|
||||
const ParentNameFieldQuery& query) const {
|
||||
bool operator()(
|
||||
internal::BasePointer<const internal::SymbolBase, false> offset,
|
||||
const ParentNameFieldQuery& query) const {
|
||||
Symbol symbol = ResolveSymbol(offset, base_ptr);
|
||||
const FieldDescriptor* field = symbol.field_descriptor();
|
||||
return field != nullptr && !field->is_extension() &&
|
||||
|
|
@ -945,9 +921,9 @@ struct SymbolByParentEq {
|
|||
ResolveSymbol(b, base_ptr).parent_name_key();
|
||||
}
|
||||
};
|
||||
using SymbolsByParentSet =
|
||||
absl::flat_hash_set<OffsetT<const internal::SymbolBase>, SymbolByParentHash,
|
||||
SymbolByParentEq>;
|
||||
using SymbolsByParentSet = absl::flat_hash_set<
|
||||
internal::BasePointer<const internal::SymbolBase, false>,
|
||||
SymbolByParentHash, SymbolByParentEq>;
|
||||
|
||||
template <typename Projection>
|
||||
struct ProjectedHash {
|
||||
|
|
@ -1059,8 +1035,8 @@ using EnumValuesByNumberSet =
|
|||
ParentNumberEq>;
|
||||
|
||||
template <typename T>
|
||||
std::pair<const void*, int> ObjectToParentNumber(OffsetT<T> offset,
|
||||
const void* base_ptr) {
|
||||
std::pair<const void*, int> ObjectToParentNumber(
|
||||
internal::BasePointer<T, false> offset, const void* base_ptr) {
|
||||
return ObjectToParentNumber(offset.Resolve(base_ptr));
|
||||
}
|
||||
|
||||
|
|
@ -1088,11 +1064,11 @@ struct ParentNumberEqOffsetPtr {
|
|||
};
|
||||
|
||||
using EnumValuesByNumberSetOffsetPtr =
|
||||
absl::flat_hash_set<OffsetT<const EnumValueDescriptor>,
|
||||
absl::flat_hash_set<internal::BasePointer<const EnumValueDescriptor, false>,
|
||||
ParentNumberHashOffsetPtr, ParentNumberEqOffsetPtr>;
|
||||
|
||||
using FieldsByNumberSet =
|
||||
absl::flat_hash_set<OffsetT<const FieldDescriptor>,
|
||||
absl::flat_hash_set<internal::BasePointer<const FieldDescriptor, false>,
|
||||
ParentNumberHashOffsetPtr, ParentNumberEqOffsetPtr>;
|
||||
|
||||
// This is a map rather than a hash-map, since we use it to iterate
|
||||
|
|
@ -1313,7 +1289,8 @@ class FileDescriptorTables {
|
|||
const FileDescriptorTables* tables);
|
||||
void FieldsByCamelcaseNamesLazyInitInternal() const;
|
||||
|
||||
Symbol Resolve(OffsetT<const internal::SymbolBase> v) const {
|
||||
Symbol Resolve(
|
||||
internal::BasePointer<const internal::SymbolBase, false> v) const {
|
||||
return Symbol(v.Resolve(flat_buffer_.data()));
|
||||
}
|
||||
|
||||
|
|
@ -1931,7 +1908,7 @@ FileDescriptorTables::FindEnumValueByNumberCreatingIfUnknown(
|
|||
absl::StrCat(parent->full_name(), ".", enum_value_name));
|
||||
result->number_ = number;
|
||||
result->type_ = parent;
|
||||
result->options_ = &EnumValueOptions::default_instance();
|
||||
result->options_ = nullptr;
|
||||
unknown_enum_values_by_number_.insert(result);
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1973,7 +1950,8 @@ bool FileDescriptorTables::AddAliasUnderParent(const void* parent,
|
|||
ABSL_DCHECK_EQ(name, symbol.parent_name_key().second);
|
||||
ABSL_DCHECK_EQ(parent, symbol.parent_name_key().first);
|
||||
return symbols_by_parent_
|
||||
.insert(OffsetT<const internal::SymbolBase>(symbol.ptr(), flat_buffer_))
|
||||
.insert(internal::BasePointer<const internal::SymbolBase, false>(
|
||||
symbol.ptr(), flat_buffer_.data()))
|
||||
.second;
|
||||
}
|
||||
|
||||
|
|
@ -2002,7 +1980,8 @@ bool FileDescriptorTables::AddFieldByNumber(FieldDescriptor* field) {
|
|||
}
|
||||
|
||||
return fields_by_number_
|
||||
.insert(OffsetT<const FieldDescriptor>(field, flat_buffer_))
|
||||
.insert(internal::BasePointer<const FieldDescriptor, false>(
|
||||
field, flat_buffer_.data()))
|
||||
.second;
|
||||
}
|
||||
|
||||
|
|
@ -2014,7 +1993,8 @@ bool FileDescriptorTables::AddEnumValueByNumber(EnumValueDescriptor* value) {
|
|||
static_cast<int64_t>(base) + value->type()->sequential_value_limit_)
|
||||
return true;
|
||||
return enum_values_by_number_
|
||||
.insert(OffsetT<const EnumValueDescriptor>(value, flat_buffer_))
|
||||
.insert(internal::BasePointer<const EnumValueDescriptor, false>(
|
||||
value, flat_buffer_.data()))
|
||||
.second;
|
||||
}
|
||||
|
||||
|
|
@ -2864,6 +2844,18 @@ bool DescriptorPool::TryFindExtensionInFallbackDatabase(
|
|||
|
||||
// ===================================================================
|
||||
|
||||
#define PROTOBUF_DEFINE_OPTIONS_ACCESSOR(CLASS, TYPE) \
|
||||
const TYPE& CLASS::options() const { return *options_; }
|
||||
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(Descriptor, MessageOptions)
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(FieldDescriptor, FieldOptions)
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(OneofDescriptor, OneofOptions)
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(EnumDescriptor, EnumOptions)
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(EnumValueDescriptor, EnumValueOptions)
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(ServiceDescriptor, ServiceOptions)
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(MethodDescriptor, MethodOptions)
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(FileDescriptor, FileOptions)
|
||||
|
||||
bool FieldDescriptor::is_map_message_type() const {
|
||||
return message_type()->options().map_entry();
|
||||
}
|
||||
|
|
@ -3436,9 +3428,10 @@ std::string FileDescriptor::DebugStringWithOptions(
|
|||
comment_printer.AddPreComment(&contents);
|
||||
|
||||
absl::flat_hash_set<int> public_dependencies(
|
||||
public_dependencies_, public_dependencies_ + public_dependency_count_);
|
||||
public_dependencies_.get(),
|
||||
public_dependencies_ + public_dependency_count_);
|
||||
absl::flat_hash_set<int> weak_dependencies(
|
||||
weak_dependencies_, weak_dependencies_ + weak_dependency_count_);
|
||||
weak_dependencies_.get(), weak_dependencies_ + weak_dependency_count_);
|
||||
|
||||
for (int i = 0; i < dependency_count(); i++) {
|
||||
if (public_dependencies.contains(i)) {
|
||||
|
|
@ -4755,10 +4748,10 @@ Symbol DescriptorPool::NewPlaceholderWithMutexHeld(
|
|||
EnumDescriptor* placeholder_enum = &placeholder_file->enum_types_[0];
|
||||
memset(static_cast<void*>(placeholder_enum), 0, sizeof(*placeholder_enum));
|
||||
|
||||
placeholder_enum->all_names_ = alloc.AllocatePlaceholderNames(
|
||||
placeholder_full_name, placeholder_name.size());
|
||||
placeholder_enum->all_names_.SetPayload(alloc.AllocatePlaceholderNames(
|
||||
placeholder_full_name, placeholder_name.size()));
|
||||
placeholder_enum->file_ = placeholder_file;
|
||||
placeholder_enum->options_ = &EnumOptions::default_instance();
|
||||
placeholder_enum->options_ = nullptr;
|
||||
placeholder_enum->proto_features_ = &FeatureSet::default_instance();
|
||||
placeholder_enum->merged_features_ = &FeatureSet::default_instance();
|
||||
placeholder_enum->is_placeholder_ = true;
|
||||
|
|
@ -4783,7 +4776,7 @@ Symbol DescriptorPool::NewPlaceholderWithMutexHeld(
|
|||
|
||||
placeholder_value->number_ = 0;
|
||||
placeholder_value->type_ = placeholder_enum;
|
||||
placeholder_value->options_ = &EnumValueOptions::default_instance();
|
||||
placeholder_value->options_ = nullptr;
|
||||
|
||||
return Symbol(placeholder_enum);
|
||||
} else {
|
||||
|
|
@ -4794,10 +4787,10 @@ Symbol DescriptorPool::NewPlaceholderWithMutexHeld(
|
|||
memset(static_cast<void*>(placeholder_message), 0,
|
||||
sizeof(*placeholder_message));
|
||||
|
||||
placeholder_message->all_names_ = alloc.AllocatePlaceholderNames(
|
||||
placeholder_full_name, placeholder_name.size());
|
||||
placeholder_message->all_names_.SetPayload(alloc.AllocatePlaceholderNames(
|
||||
placeholder_full_name, placeholder_name.size()));
|
||||
placeholder_message->file_ = placeholder_file;
|
||||
placeholder_message->options_ = &MessageOptions::default_instance();
|
||||
placeholder_message->options_ = nullptr;
|
||||
placeholder_message->proto_features_ = &FeatureSet::default_instance();
|
||||
placeholder_message->merged_features_ = &FeatureSet::default_instance();
|
||||
placeholder_message->is_placeholder_ = true;
|
||||
|
|
@ -4844,7 +4837,7 @@ FileDescriptor* DescriptorPool::NewPlaceholderFileWithMutexHeld(
|
|||
placeholder->name_ = alloc.AllocateStrings(name);
|
||||
placeholder->package_ = &internal::GetEmptyString();
|
||||
placeholder->pool_ = this;
|
||||
placeholder->options_ = &FileOptions::default_instance();
|
||||
placeholder->options_ = nullptr;
|
||||
placeholder->proto_features_ = &FeatureSet::default_instance();
|
||||
placeholder->merged_features_ = &FeatureSet::default_instance();
|
||||
placeholder->tables_ = &FileDescriptorTables::GetEmptyInstance();
|
||||
|
|
@ -5207,20 +5200,20 @@ void internal::DescriptorBuilder::PostProcessFieldFeatures(
|
|||
if (field.options_->has_ctype()) {
|
||||
field.legacy_proto_ctype_ = field.options_->ctype();
|
||||
const_cast<FieldOptions*>( // NOLINT(google3-runtime-proto-const-cast)
|
||||
field.options_)
|
||||
field.options_.get())
|
||||
->clear_ctype();
|
||||
}
|
||||
}
|
||||
|
||||
// A common pattern: We want to convert a repeated field in the descriptor
|
||||
// to an array of values, calling some method to build each value.
|
||||
#define BUILD_ARRAY(INPUT, OUTPUT, NAME, METHOD, PARENT) \
|
||||
OUTPUT->NAME##_count_ = INPUT.NAME##_size(); \
|
||||
OUTPUT->NAME##s_ = alloc.AllocateArray< \
|
||||
typename std::remove_pointer<decltype(OUTPUT->NAME##s_)>::type>( \
|
||||
INPUT.NAME##_size()); \
|
||||
for (int i = 0; i < INPUT.NAME##_size(); i++) { \
|
||||
METHOD(INPUT.NAME(i), PARENT, OUTPUT->NAME##s_ + i, alloc); \
|
||||
#define BUILD_ARRAY(INPUT, OUTPUT, NAME, METHOD, PARENT) \
|
||||
OUTPUT->NAME##_count_ = INPUT.NAME##_size(); \
|
||||
OUTPUT->NAME##s_ = \
|
||||
alloc.AllocateArray<decltype(OUTPUT->NAME##s_)::value_type>( \
|
||||
INPUT.NAME##_size()); \
|
||||
for (int i = 0; i < INPUT.NAME##_size(); i++) { \
|
||||
METHOD(INPUT.NAME(i), PARENT, OUTPUT->NAME##s_ + i, alloc); \
|
||||
}
|
||||
|
||||
PROTOBUF_NOINLINE void internal::DescriptorBuilder::AddRecursiveImportError(
|
||||
|
|
@ -5840,9 +5833,8 @@ FileDescriptor* internal::DescriptorBuilder::BuildFileImpl(
|
|||
auto cleanup = DisableTracking();
|
||||
internal::VisitDescriptors(
|
||||
*result, proto, [&](const auto& descriptor, const auto& proto) {
|
||||
using OptionsT =
|
||||
typename std::remove_const<typename std::remove_pointer<
|
||||
decltype(descriptor.options_)>::type>::type;
|
||||
using OptionsT = std::remove_const_t<
|
||||
std::decay_t<decltype(*descriptor.options_)>>;
|
||||
using DescriptorT =
|
||||
typename std::remove_const<typename std::remove_reference<
|
||||
decltype(descriptor)>::type>::type;
|
||||
|
|
@ -5850,7 +5842,7 @@ FileDescriptor* internal::DescriptorBuilder::BuildFileImpl(
|
|||
ResolveFeatures(
|
||||
proto, const_cast<DescriptorT*>(&descriptor),
|
||||
const_cast< // NOLINT(google3-runtime-proto-const-cast)
|
||||
OptionsT*>(descriptor.options_),
|
||||
OptionsT*>(+descriptor.options_),
|
||||
alloc);
|
||||
});
|
||||
}
|
||||
|
|
@ -6002,7 +5994,8 @@ void internal::DescriptorBuilder::BuildMessage(const DescriptorProto& proto,
|
|||
internal::FlatAllocator& alloc) {
|
||||
const absl::string_view scope =
|
||||
(parent == nullptr) ? file_->package() : parent->full_name();
|
||||
result->all_names_ = AllocateNameStrings(scope, proto.name(), proto, alloc);
|
||||
result->all_names_.SetPayload(
|
||||
AllocateNameStrings(scope, proto.name(), proto, alloc));
|
||||
ValidateSymbolName(proto.name(), result->full_name(), proto);
|
||||
|
||||
result->file_ = file_;
|
||||
|
|
@ -6046,7 +6039,6 @@ void internal::DescriptorBuilder::BuildMessage(const DescriptorProto& proto,
|
|||
if (recursion_depth_ <= 0) {
|
||||
AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::OTHER,
|
||||
"Reached maximum recursion limit for nested messages.");
|
||||
result->nested_types_ = nullptr;
|
||||
result->nested_type_count_ = 0;
|
||||
return;
|
||||
}
|
||||
|
|
@ -6287,12 +6279,13 @@ void internal::DescriptorBuilder::BuildFieldOrExtension(
|
|||
if (auto names = alloc.AllocateFieldNames(
|
||||
proto.name(), scope,
|
||||
proto.has_json_name() ? &proto.json_name() : nullptr)) {
|
||||
result->all_names_ = *names;
|
||||
result->all_names_.SetPayload(*names);
|
||||
} else {
|
||||
AddError(
|
||||
scope.empty() ? proto.name() : absl::StrCat(scope, ".", proto.name()),
|
||||
proto, DescriptorPool::ErrorCollector::NAME, "Name too long.");
|
||||
result->all_names_ = alloc.AllocateEntityNames("", "unknown").value();
|
||||
result->all_names_.SetPayload(
|
||||
alloc.AllocateEntityNames("", "unknown").value());
|
||||
}
|
||||
|
||||
ValidateSymbolName(proto.name(), result->full_name(), proto);
|
||||
|
|
@ -6637,15 +6630,14 @@ void internal::DescriptorBuilder::BuildOneof(const OneofDescriptorProto& proto,
|
|||
Descriptor* parent,
|
||||
OneofDescriptor* result,
|
||||
internal::FlatAllocator& alloc) {
|
||||
result->all_names_ =
|
||||
AllocateNameStrings(parent->full_name(), proto.name(), proto, alloc);
|
||||
result->all_names_.SetPayload(
|
||||
AllocateNameStrings(parent->full_name(), proto.name(), proto, alloc));
|
||||
ValidateSymbolName(proto.name(), result->full_name(), proto);
|
||||
|
||||
result->containing_type_ = parent;
|
||||
|
||||
// We need to fill these in later.
|
||||
result->field_count_ = 0;
|
||||
result->fields_ = nullptr;
|
||||
|
||||
// Copy options.
|
||||
AllocateOptions(proto, result, OneofDescriptorProto::kOptionsFieldNumber,
|
||||
|
|
@ -6813,7 +6805,8 @@ void internal::DescriptorBuilder::BuildEnum(const EnumDescriptorProto& proto,
|
|||
const absl::string_view scope =
|
||||
(parent == nullptr) ? file_->package() : parent->full_name();
|
||||
|
||||
result->all_names_ = AllocateNameStrings(scope, proto.name(), proto, alloc);
|
||||
result->all_names_.SetPayload(
|
||||
AllocateNameStrings(scope, proto.name(), proto, alloc));
|
||||
ValidateSymbolName(proto.name(), result->full_name(), proto);
|
||||
result->file_ = file_;
|
||||
result->containing_type_ = parent;
|
||||
|
|
@ -6995,8 +6988,8 @@ void internal::DescriptorBuilder::BuildEnumValue(
|
|||
void internal::DescriptorBuilder::BuildService(
|
||||
const ServiceDescriptorProto& proto, const void* /* dummy */,
|
||||
ServiceDescriptor* result, internal::FlatAllocator& alloc) {
|
||||
result->all_names_ =
|
||||
AllocateNameStrings(file_->package(), proto.name(), proto, alloc);
|
||||
result->all_names_.SetPayload(
|
||||
AllocateNameStrings(file_->package(), proto.name(), proto, alloc));
|
||||
result->file_ = file_;
|
||||
ValidateSymbolName(proto.name(), result->full_name(), proto);
|
||||
|
||||
|
|
@ -7014,8 +7007,8 @@ void internal::DescriptorBuilder::BuildMethod(
|
|||
const MethodDescriptorProto& proto, const ServiceDescriptor* parent,
|
||||
MethodDescriptor* result, internal::FlatAllocator& alloc) {
|
||||
result->service_ = parent;
|
||||
result->all_names_ =
|
||||
AllocateNameStrings(parent->full_name(), proto.name(), proto, alloc);
|
||||
result->all_names_.SetPayload(
|
||||
AllocateNameStrings(parent->full_name(), proto.name(), proto, alloc));
|
||||
|
||||
ValidateSymbolName(proto.name(), result->full_name(), proto);
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@
|
|||
#include "absl/synchronization/mutex.h"
|
||||
#include "google/protobuf/descriptor_lite.h" // IWYU pragma: export
|
||||
#include "google/protobuf/extension_set.h"
|
||||
#include "google/protobuf/offset_ptr.h"
|
||||
#include "google/protobuf/port.h"
|
||||
|
||||
// Must be included last.
|
||||
|
|
@ -267,7 +268,18 @@ class PROTOBUF_FUTURE_ADD_EARLY_WARN_UNUSED DescriptorNames {
|
|||
public:
|
||||
// Uninitialized, to support `= default` of descriptor types.
|
||||
DescriptorNames() = default;
|
||||
explicit DescriptorNames(const char* payload) : payload_(payload) {}
|
||||
|
||||
// We can't construct temporaries because of the offset_ptr, so avoid this.
|
||||
#ifndef SWIG
|
||||
DescriptorNames(const DescriptorNames&) = delete;
|
||||
DescriptorNames& operator=(const DescriptorNames&) = delete;
|
||||
#endif
|
||||
|
||||
struct Input {
|
||||
const char* value;
|
||||
};
|
||||
|
||||
void SetPayload(Input value) { payload_ = value.value; }
|
||||
|
||||
// The full name is just before `payload_`, and the name is the suffix of it.
|
||||
// We don't need a special offset for them.
|
||||
|
|
@ -309,7 +321,7 @@ class PROTOBUF_FUTURE_ADD_EARLY_WARN_UNUSED DescriptorNames {
|
|||
return absl::string_view(payload_ - offset, size);
|
||||
}
|
||||
|
||||
const char* payload_;
|
||||
internal::NonnullOffsetPtr<const char> payload_;
|
||||
};
|
||||
|
||||
class FlatAllocator;
|
||||
|
|
@ -822,25 +834,20 @@ class PROTOBUF_EXPORT Descriptor : private internal::SymbolBase {
|
|||
// sequentially numbered fields in a message.
|
||||
uint16_t sequential_field_limit_;
|
||||
|
||||
int field_count_;
|
||||
|
||||
internal::DescriptorNames all_names_;
|
||||
const FileDescriptor* file_;
|
||||
const Descriptor* containing_type_;
|
||||
const MessageOptions* options_;
|
||||
const FeatureSet* proto_features_;
|
||||
const FeatureSet* merged_features_;
|
||||
|
||||
// These arrays are separated from their sizes to minimize padding on 64-bit.
|
||||
FieldDescriptor* fields_;
|
||||
OneofDescriptor* oneof_decls_;
|
||||
Descriptor* nested_types_;
|
||||
EnumDescriptor* enum_types_;
|
||||
ExtensionRange* extension_ranges_;
|
||||
FieldDescriptor* extensions_;
|
||||
ReservedRange* reserved_ranges_;
|
||||
const std::string** reserved_names_;
|
||||
internal::NonnullOffsetPtr<const FileDescriptor> file_;
|
||||
internal::NullableOffsetPtr<const Descriptor> containing_type_;
|
||||
internal::OffsetProtoPtr<const MessageOptions> options_;
|
||||
internal::NonnullOffsetPtr<FieldDescriptor> fields_;
|
||||
internal::NonnullOffsetPtr<OneofDescriptor> oneof_decls_;
|
||||
internal::NonnullOffsetPtr<Descriptor> nested_types_;
|
||||
internal::NonnullOffsetPtr<EnumDescriptor> enum_types_;
|
||||
internal::NonnullOffsetPtr<ExtensionRange> extension_ranges_;
|
||||
internal::NonnullOffsetPtr<FieldDescriptor> extensions_;
|
||||
internal::NonnullOffsetPtr<ReservedRange> reserved_ranges_;
|
||||
internal::NonnullOffsetPtr<const std::string*> reserved_names_;
|
||||
|
||||
int field_count_;
|
||||
int oneof_decl_count_;
|
||||
int real_oneof_decl_count_;
|
||||
int nested_type_count_;
|
||||
|
|
@ -850,6 +857,9 @@ class PROTOBUF_EXPORT Descriptor : private internal::SymbolBase {
|
|||
int reserved_range_count_;
|
||||
int reserved_name_count_;
|
||||
|
||||
const FeatureSet* proto_features_;
|
||||
const FeatureSet* merged_features_;
|
||||
|
||||
// IMPORTANT: If you add a new field, make sure to search for all instances
|
||||
// of Allocate<Descriptor>() and AllocateArray<Descriptor>() in descriptor.cc
|
||||
// and update them to initialize the field.
|
||||
|
|
@ -867,7 +877,8 @@ class PROTOBUF_EXPORT Descriptor : private internal::SymbolBase {
|
|||
friend class SymbolChecker;
|
||||
};
|
||||
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(Descriptor, 160);
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(Descriptor, 112);
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(Descriptor::ExtensionRange, 40);
|
||||
|
||||
// Describes a single field of a message. To get the descriptor for a given
|
||||
// field, first get the Descriptor for the message in which it is defined,
|
||||
|
|
@ -1273,6 +1284,9 @@ class PROTOBUF_EXPORT FieldDescriptor : private internal::SymbolBase,
|
|||
|
||||
CppRepeatedType CalculateCppRepeatedType() const;
|
||||
|
||||
static void TypeOnceInit(const FieldDescriptor* to_init);
|
||||
void InternalTypeOnceInit() const;
|
||||
|
||||
bool has_default_value_ : 1;
|
||||
bool proto3_optional_ : 1;
|
||||
// Whether the user has specified the json_name field option in the .proto
|
||||
|
|
@ -1308,25 +1322,32 @@ class PROTOBUF_EXPORT FieldDescriptor : private internal::SymbolBase,
|
|||
// and its indices above.
|
||||
int number_;
|
||||
internal::DescriptorNames all_names_;
|
||||
const FileDescriptor* file_;
|
||||
internal::NonnullOffsetPtr<const FileDescriptor> file_;
|
||||
|
||||
union {
|
||||
internal::NonnullOffsetPtr<const OneofDescriptor> containing_oneof;
|
||||
internal::NullableOffsetPtr<const Descriptor> extension_scope;
|
||||
} scope_;
|
||||
|
||||
internal::OffsetProtoPtr<const FieldOptions> options_;
|
||||
|
||||
// The once_flag is followed by a NUL terminated string for the type name and
|
||||
// enum default value (or empty string if no default enum).
|
||||
// Allocated separately, so no OffsetPtr.
|
||||
absl::once_flag* type_once_;
|
||||
static void TypeOnceInit(const FieldDescriptor* to_init);
|
||||
void InternalTypeOnceInit() const;
|
||||
|
||||
// Extensions' containing type can be from a different file, so no OffsetPtr.
|
||||
const Descriptor* containing_type_;
|
||||
|
||||
union {
|
||||
const OneofDescriptor* containing_oneof;
|
||||
const Descriptor* extension_scope;
|
||||
} scope_;
|
||||
union {
|
||||
// Types can be from different files so no OffsetPtr.
|
||||
mutable const Descriptor* message_type;
|
||||
mutable const EnumDescriptor* enum_type;
|
||||
} type_descriptor_;
|
||||
const FieldOptions* options_;
|
||||
|
||||
const FeatureSet* proto_features_;
|
||||
const FeatureSet* merged_features_;
|
||||
|
||||
// IMPORTANT: If you add a new field, make sure to search for all instances
|
||||
// of Allocate<FieldDescriptor>() and AllocateArray<FieldDescriptor>() in
|
||||
// descriptor.cc and update them to initialize the field.
|
||||
|
|
@ -1362,7 +1383,7 @@ class PROTOBUF_EXPORT FieldDescriptor : private internal::SymbolBase,
|
|||
friend class OneofDescriptor;
|
||||
};
|
||||
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(FieldDescriptor, 88);
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(FieldDescriptor, 72);
|
||||
|
||||
// Describes a oneof defined in a message type.
|
||||
class PROTOBUF_EXPORT OneofDescriptor : private internal::SymbolBase {
|
||||
|
|
@ -1451,11 +1472,12 @@ class PROTOBUF_EXPORT OneofDescriptor : private internal::SymbolBase {
|
|||
int field_count_;
|
||||
|
||||
internal::DescriptorNames all_names_;
|
||||
const Descriptor* containing_type_;
|
||||
const OneofOptions* options_;
|
||||
internal::NonnullOffsetPtr<const Descriptor> containing_type_;
|
||||
internal::OffsetProtoPtr<const OneofOptions> options_;
|
||||
internal::NonnullOffsetPtr<const FieldDescriptor> fields_;
|
||||
|
||||
const FeatureSet* proto_features_;
|
||||
const FeatureSet* merged_features_;
|
||||
const FieldDescriptor* fields_;
|
||||
|
||||
// IMPORTANT: If you add a new field, make sure to search for all instances
|
||||
// of Allocate<OneofDescriptor>() and AllocateArray<OneofDescriptor>()
|
||||
|
|
@ -1469,7 +1491,7 @@ class PROTOBUF_EXPORT OneofDescriptor : private internal::SymbolBase {
|
|||
friend class Reflection;
|
||||
};
|
||||
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(OneofDescriptor, 56);
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(OneofDescriptor, 40);
|
||||
|
||||
// Describes an enum type defined in a .proto file. To get the EnumDescriptor
|
||||
// for a generated enum type, call TypeName_descriptor(). Use DescriptorPool
|
||||
|
|
@ -1663,20 +1685,20 @@ class PROTOBUF_EXPORT EnumDescriptor : private internal::SymbolBase {
|
|||
// sequentially numbered labels in an enum.
|
||||
int16_t sequential_value_limit_;
|
||||
|
||||
int value_count_;
|
||||
|
||||
internal::DescriptorNames all_names_;
|
||||
const FileDescriptor* file_;
|
||||
const Descriptor* containing_type_;
|
||||
const EnumOptions* options_;
|
||||
const FeatureSet* proto_features_;
|
||||
const FeatureSet* merged_features_;
|
||||
EnumValueDescriptor* values_;
|
||||
internal::NonnullOffsetPtr<const FileDescriptor> file_;
|
||||
internal::NullableOffsetPtr<const Descriptor> containing_type_;
|
||||
internal::OffsetProtoPtr<const EnumOptions> options_;
|
||||
internal::NonnullOffsetPtr<EnumValueDescriptor> values_;
|
||||
internal::NonnullOffsetPtr<EnumDescriptor::ReservedRange> reserved_ranges_;
|
||||
internal::NonnullOffsetPtr<const std::string*> reserved_names_;
|
||||
|
||||
int value_count_;
|
||||
int reserved_range_count_;
|
||||
int reserved_name_count_;
|
||||
EnumDescriptor::ReservedRange* reserved_ranges_;
|
||||
const std::string** reserved_names_;
|
||||
|
||||
const FeatureSet* proto_features_;
|
||||
const FeatureSet* merged_features_;
|
||||
|
||||
// IMPORTANT: If you add a new field, make sure to search for all instances
|
||||
// of Allocate<EnumDescriptor>() and AllocateArray<EnumDescriptor>() in
|
||||
|
|
@ -1694,7 +1716,7 @@ class PROTOBUF_EXPORT EnumDescriptor : private internal::SymbolBase {
|
|||
friend class Reflection;
|
||||
};
|
||||
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(EnumDescriptor, 88);
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(EnumDescriptor, 64);
|
||||
|
||||
// Describes an individual enum constant of a particular type. To get the
|
||||
// EnumValueDescriptor for a given enum value, first get the EnumDescriptor
|
||||
|
|
@ -1789,9 +1811,11 @@ class PROTOBUF_EXPORT EnumValueDescriptor : private internal::SymbolBaseN<0>,
|
|||
// We keep the old-style std::string payload to support `NameOfEnumAsString`
|
||||
// Once we start migrating Enum_Name functions to string_view we can switch
|
||||
// this too.
|
||||
const std::string* all_names_;
|
||||
internal::NonnullOffsetPtr<const std::string> all_names_;
|
||||
internal::OffsetProtoPtr<const EnumValueOptions> options_;
|
||||
// Type can be from a different allocation when creating enums via
|
||||
// `FindEnumValueByNumberCreatingIfUnknown`.
|
||||
const EnumDescriptor* type_;
|
||||
const EnumValueOptions* options_;
|
||||
const FeatureSet* proto_features_;
|
||||
const FeatureSet* merged_features_;
|
||||
// IMPORTANT: If you add a new field, make sure to search for all instances
|
||||
|
|
@ -1807,7 +1831,7 @@ class PROTOBUF_EXPORT EnumValueDescriptor : private internal::SymbolBaseN<0>,
|
|||
friend class Reflection;
|
||||
};
|
||||
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(EnumValueDescriptor, 48);
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(EnumValueDescriptor, 40);
|
||||
|
||||
// Describes an RPC service. Use DescriptorPool to construct your own
|
||||
// descriptors.
|
||||
|
|
@ -1897,12 +1921,14 @@ class PROTOBUF_EXPORT ServiceDescriptor : private internal::SymbolBase {
|
|||
void GetLocationPath(std::vector<int>* output) const;
|
||||
|
||||
internal::DescriptorNames all_names_;
|
||||
const FileDescriptor* file_;
|
||||
const ServiceOptions* options_;
|
||||
internal::NonnullOffsetPtr<const FileDescriptor> file_;
|
||||
internal::OffsetProtoPtr<const ServiceOptions> options_;
|
||||
internal::NonnullOffsetPtr<MethodDescriptor> methods_;
|
||||
int method_count_;
|
||||
|
||||
const FeatureSet* proto_features_;
|
||||
const FeatureSet* merged_features_;
|
||||
MethodDescriptor* methods_;
|
||||
int method_count_;
|
||||
|
||||
// IMPORTANT: If you add a new field, make sure to search for all instances
|
||||
// of Allocate<ServiceDescriptor>() and AllocateArray<ServiceDescriptor>() in
|
||||
// descriptor.cc and update them to initialize the field.
|
||||
|
|
@ -1914,7 +1940,7 @@ class PROTOBUF_EXPORT ServiceDescriptor : private internal::SymbolBase {
|
|||
friend class MethodDescriptor;
|
||||
};
|
||||
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(ServiceDescriptor, 64);
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(ServiceDescriptor, 40);
|
||||
|
||||
// Describes an individual service method. To obtain a MethodDescriptor given
|
||||
// a service, first get its ServiceDescriptor, then call
|
||||
|
|
@ -2007,10 +2033,13 @@ class PROTOBUF_EXPORT MethodDescriptor : private internal::SymbolBase {
|
|||
bool client_streaming_;
|
||||
bool server_streaming_;
|
||||
internal::DescriptorNames all_names_;
|
||||
const ServiceDescriptor* service_;
|
||||
internal::NonnullOffsetPtr<const ServiceDescriptor> service_;
|
||||
|
||||
internal::OffsetProtoPtr<const MethodOptions> options_;
|
||||
|
||||
mutable internal::LazyDescriptor input_type_;
|
||||
mutable internal::LazyDescriptor output_type_;
|
||||
const MethodOptions* options_;
|
||||
|
||||
const FeatureSet* proto_features_;
|
||||
const FeatureSet* merged_features_;
|
||||
// IMPORTANT: If you add a new field, make sure to search for all instances
|
||||
|
|
@ -2023,7 +2052,7 @@ class PROTOBUF_EXPORT MethodDescriptor : private internal::SymbolBase {
|
|||
friend class ServiceDescriptor;
|
||||
};
|
||||
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(MethodDescriptor, 80);
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(MethodDescriptor, 64);
|
||||
|
||||
// Describes a whole .proto file. To get the FileDescriptor for a compiled-in
|
||||
// file, get the descriptor for something defined in that file and call
|
||||
|
|
@ -2199,19 +2228,6 @@ class PROTOBUF_EXPORT FileDescriptor : private internal::SymbolBase {
|
|||
friend class FileDescriptorLegacy;
|
||||
typedef FileOptions OptionsType;
|
||||
|
||||
bool is_placeholder_;
|
||||
// Indicates the FileDescriptor is completed building. Used to verify
|
||||
// that type accessor functions that can possibly build a dependent file
|
||||
// aren't called during the process of building the file.
|
||||
bool finished_building_;
|
||||
// This one is here to fill the padding.
|
||||
int extension_count_;
|
||||
|
||||
const std::string* name_;
|
||||
const std::string* package_;
|
||||
const DescriptorPool* pool_;
|
||||
Edition edition_;
|
||||
|
||||
// Returns edition of this file. For legacy proto2/proto3 files, special
|
||||
// EDITION_PROTO2 and EDITION_PROTO3 values are used.
|
||||
Edition edition() const;
|
||||
|
|
@ -2223,12 +2239,16 @@ class PROTOBUF_EXPORT FileDescriptor : private internal::SymbolBase {
|
|||
const FeatureSet& features() const { return *merged_features_; }
|
||||
friend class internal::InternalFeatureHelper;
|
||||
|
||||
// dependencies_once_ contain a once_flag followed by N NUL terminated
|
||||
// strings. Dependencies that do not need to be loaded will be empty. ie just
|
||||
// {'\0'}
|
||||
absl::once_flag* dependencies_once_;
|
||||
static void DependenciesOnceInit(const FileDescriptor* to_init);
|
||||
void InternalDependenciesOnceInit() const;
|
||||
bool is_placeholder_;
|
||||
// Indicates the FileDescriptor is completed building. Used to verify
|
||||
// that type accessor functions that can possibly build a dependent file
|
||||
// aren't called during the process of building the file.
|
||||
bool finished_building_;
|
||||
// This one is here to fill the padding.
|
||||
int extension_count_;
|
||||
|
||||
internal::NonnullOffsetPtr<const std::string> name_;
|
||||
Edition edition_;
|
||||
|
||||
// These are arranged to minimize padding on 64-bit.
|
||||
int dependency_count_;
|
||||
|
|
@ -2239,21 +2259,34 @@ class PROTOBUF_EXPORT FileDescriptor : private internal::SymbolBase {
|
|||
int enum_type_count_;
|
||||
int service_count_;
|
||||
|
||||
mutable const FileDescriptor** dependencies_;
|
||||
int* public_dependencies_;
|
||||
int* weak_dependencies_;
|
||||
absl::string_view* option_dependencies_;
|
||||
internal::NonnullOffsetPtr<const FileDescriptor*> dependencies_;
|
||||
internal::NonnullOffsetPtr<int> public_dependencies_;
|
||||
internal::NonnullOffsetPtr<int> weak_dependencies_;
|
||||
internal::NonnullOffsetPtr<absl::string_view> option_dependencies_;
|
||||
|
||||
internal::NonnullOffsetPtr<Descriptor> message_types_;
|
||||
internal::NonnullOffsetPtr<EnumDescriptor> enum_types_;
|
||||
internal::NonnullOffsetPtr<ServiceDescriptor> services_;
|
||||
internal::NonnullOffsetPtr<FieldDescriptor> extensions_;
|
||||
internal::OffsetProtoPtr<const FileOptions> options_;
|
||||
|
||||
internal::OffsetProtoPtr<const SourceCodeInfo> source_code_info_;
|
||||
|
||||
const std::string* package_;
|
||||
|
||||
// dependencies_once_ contain a once_flag followed by N NUL terminated
|
||||
// strings. Dependencies that do not need to be loaded will be empty. ie just
|
||||
// {'\0'}
|
||||
absl::once_flag* dependencies_once_;
|
||||
static void DependenciesOnceInit(const FileDescriptor* to_init);
|
||||
void InternalDependenciesOnceInit() const;
|
||||
|
||||
const DescriptorPool* pool_;
|
||||
|
||||
Descriptor* message_types_;
|
||||
EnumDescriptor* enum_types_;
|
||||
ServiceDescriptor* services_;
|
||||
FieldDescriptor* extensions_;
|
||||
const FileOptions* options_;
|
||||
const FeatureSet* proto_features_;
|
||||
const FeatureSet* merged_features_;
|
||||
|
||||
const FileDescriptorTables* tables_;
|
||||
const SourceCodeInfo* source_code_info_;
|
||||
|
||||
// IMPORTANT: If you add a new field, make sure to search for all instances
|
||||
// of Allocate<FileDescriptor>() and AllocateArray<FileDescriptor>() in
|
||||
|
|
@ -2272,7 +2305,7 @@ class PROTOBUF_EXPORT FileDescriptor : private internal::SymbolBase {
|
|||
friend class ServiceDescriptor;
|
||||
};
|
||||
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(FileDescriptor, 184);
|
||||
PROTOBUF_INTERNAL_CHECK_CLASS_SIZE(FileDescriptor, 136);
|
||||
|
||||
#ifndef SWIG
|
||||
enum class ExtDeclEnforcementLevel : uint8_t {
|
||||
|
|
@ -2903,9 +2936,6 @@ class PROTOBUF_EXPORT DescriptorPool {
|
|||
return FIELD##s_ + index; \
|
||||
}
|
||||
|
||||
#define PROTOBUF_DEFINE_OPTIONS_ACCESSOR(CLASS, TYPE) \
|
||||
inline const TYPE& CLASS::options() const { return *options_; }
|
||||
|
||||
PROTOBUF_DEFINE_NAME_ACCESSOR(Descriptor)
|
||||
PROTOBUF_DEFINE_ACCESSOR(Descriptor, file, const FileDescriptor*)
|
||||
PROTOBUF_DEFINE_ACCESSOR(Descriptor, containing_type, const Descriptor*)
|
||||
|
|
@ -2936,7 +2966,6 @@ PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, reserved_range,
|
|||
const Descriptor::ReservedRange*)
|
||||
PROTOBUF_DEFINE_ACCESSOR(Descriptor, reserved_name_count, int)
|
||||
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(Descriptor, MessageOptions)
|
||||
PROTOBUF_DEFINE_ACCESSOR(Descriptor, is_placeholder, bool)
|
||||
|
||||
PROTOBUF_DEFINE_NAME_ACCESSOR(FieldDescriptor)
|
||||
|
|
@ -2944,7 +2973,6 @@ PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, file, const FileDescriptor*)
|
|||
PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, number, int)
|
||||
PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, is_extension, bool)
|
||||
PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, containing_type, const Descriptor*)
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(FieldDescriptor, FieldOptions)
|
||||
PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, has_default_value, bool)
|
||||
PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, has_json_name, bool)
|
||||
PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_int32_t, int32_t)
|
||||
|
|
@ -2960,7 +2988,6 @@ PROTOBUF_DEFINE_NAME_ACCESSOR(OneofDescriptor)
|
|||
PROTOBUF_DEFINE_ACCESSOR(OneofDescriptor, containing_type, const Descriptor*)
|
||||
PROTOBUF_DEFINE_ACCESSOR(OneofDescriptor, field_count, int)
|
||||
PROTOBUF_DEFINE_ARRAY_ACCESSOR(OneofDescriptor, field, const FieldDescriptor*)
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(OneofDescriptor, OneofOptions)
|
||||
|
||||
PROTOBUF_DEFINE_NAME_ACCESSOR(EnumDescriptor)
|
||||
PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, file, const FileDescriptor*)
|
||||
|
|
@ -2968,7 +2995,6 @@ PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, containing_type, const Descriptor*)
|
|||
PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, value_count, int)
|
||||
PROTOBUF_DEFINE_ARRAY_ACCESSOR(EnumDescriptor, value,
|
||||
const EnumValueDescriptor*)
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(EnumDescriptor, EnumOptions)
|
||||
PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, is_placeholder, bool)
|
||||
PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, reserved_range_count, int)
|
||||
PROTOBUF_DEFINE_ARRAY_ACCESSOR(EnumDescriptor, reserved_range,
|
||||
|
|
@ -2983,18 +3009,15 @@ inline absl::string_view EnumValueDescriptor::full_name() const {
|
|||
}
|
||||
PROTOBUF_DEFINE_ACCESSOR(EnumValueDescriptor, number, int)
|
||||
PROTOBUF_DEFINE_ACCESSOR(EnumValueDescriptor, type, const EnumDescriptor*)
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(EnumValueDescriptor, EnumValueOptions)
|
||||
|
||||
PROTOBUF_DEFINE_NAME_ACCESSOR(ServiceDescriptor)
|
||||
PROTOBUF_DEFINE_ACCESSOR(ServiceDescriptor, file, const FileDescriptor*)
|
||||
PROTOBUF_DEFINE_ACCESSOR(ServiceDescriptor, method_count, int)
|
||||
PROTOBUF_DEFINE_ARRAY_ACCESSOR(ServiceDescriptor, method,
|
||||
const MethodDescriptor*)
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(ServiceDescriptor, ServiceOptions)
|
||||
|
||||
PROTOBUF_DEFINE_NAME_ACCESSOR(MethodDescriptor)
|
||||
PROTOBUF_DEFINE_ACCESSOR(MethodDescriptor, service, const ServiceDescriptor*)
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(MethodDescriptor, MethodOptions)
|
||||
PROTOBUF_DEFINE_ACCESSOR(MethodDescriptor, client_streaming, bool)
|
||||
PROTOBUF_DEFINE_ACCESSOR(MethodDescriptor, server_streaming, bool)
|
||||
|
||||
|
|
@ -3009,7 +3032,6 @@ PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, message_type_count, int)
|
|||
PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, enum_type_count, int)
|
||||
PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, service_count, int)
|
||||
PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, extension_count, int)
|
||||
PROTOBUF_DEFINE_OPTIONS_ACCESSOR(FileDescriptor, FileOptions)
|
||||
PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, is_placeholder, bool)
|
||||
|
||||
PROTOBUF_DEFINE_ARRAY_ACCESSOR(FileDescriptor, message_type, const Descriptor*)
|
||||
|
|
@ -3085,7 +3107,7 @@ inline absl::string_view FieldDescriptor::json_name() const {
|
|||
|
||||
inline const OneofDescriptor* FieldDescriptor::containing_oneof() const {
|
||||
if (is_oneof_) {
|
||||
auto* res = scope_.containing_oneof;
|
||||
const OneofDescriptor* res = scope_.containing_oneof;
|
||||
PROTOBUF_ASSUME(res != nullptr);
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
36
src/google/protobuf/offset_ptr.cc
Normal file
36
src/google/protobuf/offset_ptr.cc
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 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
|
||||
|
||||
#include "google/protobuf/offset_ptr.h"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "absl/log/absl_log.h"
|
||||
|
||||
namespace google {
|
||||
namespace protobuf {
|
||||
namespace internal {
|
||||
|
||||
void BasePointerInvalidSelfReference() noexcept {
|
||||
ABSL_LOG(FATAL) << "Nullable base pointer can't self reference.";
|
||||
}
|
||||
|
||||
void BasePointerNonnullFailure() noexcept {
|
||||
ABSL_LOG(FATAL)
|
||||
<< "Non-nullable base pointer constructed with a null pointer.";
|
||||
}
|
||||
|
||||
void BasePointerOverflow(const void* ptr, const void* base) noexcept {
|
||||
ptrdiff_t diff =
|
||||
reinterpret_cast<const char*>(ptr) - reinterpret_cast<const char*>(base);
|
||||
ABSL_LOG(FATAL) << "Pointer out of scope for offset pointer: ptr=" << ptr
|
||||
<< " base=" << base << " diff=" << diff;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
177
src/google/protobuf/offset_ptr.h
Normal file
177
src/google/protobuf/offset_ptr.h
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 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_OFFSET_PTR_H__
|
||||
#define GOOGLE_PROTOBUF_OFFSET_PTR_H__
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "absl/base/optimization.h"
|
||||
|
||||
// Must be included last.
|
||||
#include "google/protobuf/port_def.inc"
|
||||
|
||||
namespace google {
|
||||
namespace protobuf {
|
||||
namespace internal {
|
||||
|
||||
[[noreturn]] PROTOBUF_PRESERVE_ALL PROTOBUF_EXPORT void BasePointerOverflow(
|
||||
const void* ptr, const void* base) noexcept;
|
||||
[[noreturn]] PROTOBUF_PRESERVE_ALL PROTOBUF_EXPORT void
|
||||
BasePointerInvalidSelfReference() noexcept;
|
||||
[[noreturn]] PROTOBUF_PRESERVE_ALL PROTOBUF_EXPORT void
|
||||
BasePointerNonnullFailure() noexcept;
|
||||
|
||||
// Offset based pointer-like class.
|
||||
// It encodes its data relative to a `base` pointer.
|
||||
// The caller must provide this pointer and it must be the same base pointer
|
||||
// pass to `Resolve`.
|
||||
// The offset is encoded in 32-bit and the caller must guarantee that.
|
||||
//
|
||||
// Is kAllowNull is false, then nullptr is not a valid input and will terminate
|
||||
// the program. However, such mode is faster. The caller should choose the
|
||||
// appropriate setting for the pointer in question.
|
||||
template <typename T, bool kAllowNull>
|
||||
class BasePointer {
|
||||
// We must use 0 as the null pointer because some of these are initialized via
|
||||
// memset.
|
||||
static constexpr int32_t kNullOffset = 0;
|
||||
|
||||
public:
|
||||
// Uninitialized.
|
||||
BasePointer() = default;
|
||||
|
||||
// Trivial copy/assign.
|
||||
BasePointer(const BasePointer&) = default;
|
||||
BasePointer& operator=(const BasePointer&) = default;
|
||||
|
||||
BasePointer(T* ptr, const void* base) {
|
||||
if constexpr (kAllowNull) {
|
||||
if (ptr == nullptr) {
|
||||
offset_ = kNullOffset;
|
||||
return;
|
||||
}
|
||||
if (ABSL_PREDICT_FALSE(ptr == base)) {
|
||||
BasePointerInvalidSelfReference();
|
||||
}
|
||||
} else {
|
||||
if (ABSL_PREDICT_FALSE(ptr == nullptr)) {
|
||||
BasePointerNonnullFailure();
|
||||
}
|
||||
}
|
||||
|
||||
ptrdiff_t diff = reinterpret_cast<const char*>(ptr) -
|
||||
reinterpret_cast<const char*>(base);
|
||||
if (ABSL_PREDICT_FALSE(static_cast<int32_t>(diff) != diff)) {
|
||||
BasePointerOverflow(ptr, base);
|
||||
}
|
||||
|
||||
offset_ = static_cast<int32_t>(diff);
|
||||
}
|
||||
|
||||
// `base` must be the same `base` pointer as the one passed to the
|
||||
// constructor.
|
||||
T* Resolve(const void* base) const {
|
||||
if constexpr (kAllowNull) {
|
||||
if (offset_ == kNullOffset) return nullptr;
|
||||
}
|
||||
T* out = const_cast<T*>(reinterpret_cast<const T*>(
|
||||
reinterpret_cast<const char*>(base) + offset_));
|
||||
PROTOBUF_ASSUME(out != nullptr);
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t offset_;
|
||||
};
|
||||
|
||||
// Offset based pointer class.
|
||||
// It uses its own address as the base pointer, which simplifies its use but
|
||||
// restricts the input pointer to be in the same slab of memory as the
|
||||
// `OffsetPtr` instance itself.
|
||||
// kAllowNull follows the semantics of BasePointer.
|
||||
template <typename T, bool kAllowNull>
|
||||
class OffsetPtr {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
OffsetPtr() = default;
|
||||
|
||||
// Bit copy is wrong because we are relative to `this`.
|
||||
OffsetPtr(const OffsetPtr&) = delete;
|
||||
OffsetPtr& operator=(const OffsetPtr&) = delete;
|
||||
|
||||
// We can't have a conversion constructor because it would allow for
|
||||
// temporaries to be made, which breaks the invariant of maximum distance.
|
||||
|
||||
T* get() const { return ptr_.Resolve(this); }
|
||||
operator T*() const { return get(); } // NOLINT
|
||||
T* operator->() const { return get(); }
|
||||
|
||||
OffsetPtr& operator=(T* value) {
|
||||
ptr_ = BasePointer<T, kAllowNull>(value, this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OffsetPtr& operator=(std::nullptr_t) {
|
||||
static_assert(kAllowNull, "Can't accept null.");
|
||||
return *this = static_cast<T*>(nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
BasePointer<T, kAllowNull> ptr_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using NullableOffsetPtr = OffsetPtr<T, true>;
|
||||
|
||||
template <typename T>
|
||||
using NonnullOffsetPtr = OffsetPtr<T, false>;
|
||||
|
||||
// Same as OffsetPtr, with a special case &T::default_instance.
|
||||
// The pointer can be set to `&T::default_instance()` even though it is outside
|
||||
// the range. It is handled specially.
|
||||
// Null inputs are equivalent to the default instance.
|
||||
template <typename T>
|
||||
class OffsetProtoPtr {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
OffsetProtoPtr() = default;
|
||||
|
||||
// Bit copy is wrong because we are relative to `this`.
|
||||
OffsetProtoPtr(const OffsetProtoPtr&) = delete;
|
||||
OffsetProtoPtr& operator=(const OffsetProtoPtr&) = delete;
|
||||
|
||||
// We can't have a conversion constructor because it would allow for
|
||||
// temporaries to be made, which breaks the invariant of maximum distance.
|
||||
|
||||
T* get() const {
|
||||
T* value = ptr_.Resolve(this);
|
||||
return value == nullptr ? &T::default_instance() : value;
|
||||
}
|
||||
operator T*() const { return get(); } // NOLINT
|
||||
T* operator->() const { return get(); }
|
||||
|
||||
OffsetProtoPtr& operator=(T* value) {
|
||||
ptr_ = BasePointer<T, true>(
|
||||
value == &T::default_instance() ? nullptr : value, this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
BasePointer<T, true> ptr_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
#include "google/protobuf/port_undef.inc"
|
||||
|
||||
#endif // GOOGLE_PROTOBUF_OFFSET_PTR_H__
|
||||
132
src/google/protobuf/offset_ptr_test.cc
Normal file
132
src/google/protobuf/offset_ptr_test.cc
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 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
|
||||
|
||||
#include "google/protobuf/offset_ptr.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "google/protobuf/unittest.pb.h"
|
||||
|
||||
namespace google {
|
||||
namespace protobuf {
|
||||
namespace internal {
|
||||
namespace {
|
||||
|
||||
int array[10];
|
||||
|
||||
TEST(BasePtrTest, Basic) {
|
||||
BasePointer<int, false> b(&array[5], &array);
|
||||
EXPECT_EQ(&array[5], b.Resolve(&array));
|
||||
}
|
||||
|
||||
TEST(BasePtrTest, Copyable) {
|
||||
EXPECT_TRUE(
|
||||
(std::is_trivially_copy_constructible_v<BasePointer<int, false>>));
|
||||
EXPECT_TRUE((std::is_trivially_copy_assignable_v<BasePointer<int, false>>));
|
||||
EXPECT_TRUE((std::is_trivially_destructible_v<BasePointer<int, false>>));
|
||||
|
||||
BasePointer<int, false> b(&array[7], &array);
|
||||
EXPECT_EQ(&array[7], b.Resolve(&array));
|
||||
|
||||
auto b2 = b;
|
||||
EXPECT_EQ(&array[7], b2.Resolve(&array));
|
||||
}
|
||||
|
||||
TEST(BasePtrTest, BaseCanBeOnEitherSide) {
|
||||
EXPECT_EQ(&array[7],
|
||||
(BasePointer<int, false>(&array[7], &array[3])).Resolve(&array[3]));
|
||||
EXPECT_EQ(&array[4],
|
||||
(BasePointer<int, false>(&array[4], &array[7])).Resolve(&array[7]));
|
||||
}
|
||||
|
||||
TEST(BasePtrTest, BaseCanBeSameAsPointer) {
|
||||
int var = 0;
|
||||
|
||||
EXPECT_EQ(&var, (BasePointer<int, false>(&var, &var)).Resolve(&var));
|
||||
}
|
||||
|
||||
TEST(BasePtrTest, NullIsAllowed) {
|
||||
bool dummy = false;
|
||||
BasePointer<int, true> b(nullptr, &dummy);
|
||||
EXPECT_EQ(nullptr, b.Resolve(&dummy));
|
||||
|
||||
int p = 0;
|
||||
EXPECT_DEATH((BasePointer<int, true>(&p, &p)),
|
||||
"Nullable base pointer can't self reference");
|
||||
|
||||
EXPECT_DEATH((BasePointer<int, false>(nullptr, &dummy)),
|
||||
"Non-nullable base pointer constructed with a null pointer");
|
||||
}
|
||||
|
||||
TEST(BasePtrTest, OutOfScopeFails) {
|
||||
// For this one we manufacture pointers to make sure they trigger out of scope
|
||||
// failures.
|
||||
if (sizeof(void*) == 4) {
|
||||
GTEST_SKIP() << "We need 64-bit to have out-of-scope pointers.";
|
||||
}
|
||||
|
||||
void* base =
|
||||
reinterpret_cast<void*>(static_cast<uintptr_t>(0x123457890abcdef));
|
||||
|
||||
const auto make_ptr = [&](ptrdiff_t diff) {
|
||||
return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base) + diff);
|
||||
};
|
||||
|
||||
using T = BasePointer<void, false>;
|
||||
|
||||
// INT_MAX is fine.
|
||||
EXPECT_EQ(T(make_ptr(std::numeric_limits<int>::max()), base).Resolve(base),
|
||||
make_ptr(std::numeric_limits<int>::max()));
|
||||
|
||||
// INT_MAX+1 is too much.
|
||||
EXPECT_DEATH(
|
||||
T(make_ptr(ptrdiff_t{std::numeric_limits<int>::max()} + 1), base),
|
||||
"Pointer out of scope for offset pointer");
|
||||
|
||||
// INT_MIN is fine.
|
||||
EXPECT_EQ(T(make_ptr(std::numeric_limits<int>::min()), base).Resolve(base),
|
||||
make_ptr(std::numeric_limits<int>::min()));
|
||||
|
||||
// INT_MIN-1 is too much.
|
||||
EXPECT_DEATH(
|
||||
T(make_ptr(ptrdiff_t{std::numeric_limits<int>::min()} - 1), base),
|
||||
"Pointer out of scope for offset pointer");
|
||||
}
|
||||
|
||||
TEST(OffsetPtrTest, Basic) {
|
||||
struct Object {
|
||||
int array[10];
|
||||
OffsetPtr<int, false> ptr;
|
||||
} object{};
|
||||
|
||||
object.ptr = object.array + 3;
|
||||
EXPECT_EQ(object.ptr, object.array + 3);
|
||||
}
|
||||
|
||||
TEST(OffsetProtoPtr, Basic) {
|
||||
using P = proto2_unittest::TestAllTypes;
|
||||
OffsetProtoPtr<const P> p;
|
||||
|
||||
p = nullptr;
|
||||
EXPECT_EQ(&P::default_instance(), p);
|
||||
|
||||
P msg;
|
||||
p = &msg;
|
||||
EXPECT_EQ(&msg, p);
|
||||
|
||||
p = &P::default_instance();
|
||||
EXPECT_EQ(&P::default_instance(), p);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace internal
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
Loading…
Add table
Add a link
Reference in a new issue