| // Protocol Buffers - Google's data interchange format | |
| // Copyright 2022 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 | |
| // | |
| // Defines ClassData, the structure that contains type information about | |
| // messages. Used to implement reflection, parsing, dynamic casting, and other | |
| // runtime features. | |
| // | |
| // This file intentionally does not depend on protobuf_lite. | |
| #ifndef GOOGLE_PROTOBUF_CLASS_DATA_H__ | |
| #define GOOGLE_PROTOBUF_CLASS_DATA_H__ | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <string> | |
| #include <variant> | |
| #include "absl/log/absl_check.h" | |
| #include "absl/strings/string_view.h" | |
| #include "google/protobuf/port.h" | |
| // Must be included last. | |
| #include "google/protobuf/port_def.inc" | |
| namespace google { | |
| namespace protobuf { | |
| class Arena; | |
| class Descriptor; | |
| class MessageLite; | |
| class Reflection; | |
| namespace io { | |
| class EpsCopyOutputStream; | |
| } // namespace io | |
| namespace internal { | |
| struct DescriptorTable; | |
| class LazyField; | |
| struct TcParseTableBase; | |
| struct DescriptorMethods; | |
| class SerialArena; | |
| inline constexpr size_t kClassDataAlignment = 64; | |
| class MessageCreator { | |
| public: | |
| using Func = void* PROTOBUF_NONNULL (*PROTOBUF_NULLABLE)( | |
| const void* PROTOBUF_NONNULL prototype, void* PROTOBUF_NONNULL mem, | |
| Arena* PROTOBUF_NULLABLE arena); | |
| // Use -1/0/1 to be able to use <0, ==0, >0 | |
| enum Tag : int8_t { | |
| kFunc = -1, | |
| kZeroInit = 0, | |
| kMemcpy = 1, | |
| }; | |
| constexpr MessageCreator() | |
| : allocation_size_(), tag_(), alignment_(), func_(nullptr) {} | |
| static constexpr MessageCreator ZeroInit(uint32_t allocation_size, | |
| uint8_t alignment) { | |
| MessageCreator out; | |
| out.allocation_size_ = allocation_size; | |
| out.tag_ = kZeroInit; | |
| out.alignment_ = alignment; | |
| return out; | |
| } | |
| static constexpr MessageCreator CopyInit(uint32_t allocation_size, | |
| uint8_t alignment) { | |
| MessageCreator out; | |
| out.allocation_size_ = allocation_size; | |
| out.tag_ = kMemcpy; | |
| out.alignment_ = alignment; | |
| return out; | |
| } | |
| constexpr MessageCreator(Func func, uint32_t allocation_size, | |
| uint8_t alignment) | |
| : allocation_size_(allocation_size), | |
| tag_(kFunc), | |
| alignment_(alignment), | |
| func_(func) {} | |
| // Template for testing. | |
| template <typename MessageLite> | |
| MessageLite* PROTOBUF_NONNULL PlacementNew( | |
| const MessageLite* PROTOBUF_NONNULL prototype_for_func, | |
| const MessageLite* PROTOBUF_NONNULL prototype_for_copy, | |
| void* PROTOBUF_NONNULL mem, Arena* PROTOBUF_NULLABLE arena) const; | |
| // Defined in arena.h | |
| void* PROTOBUF_NONNULL AllocateMessage(Arena* PROTOBUF_NULLABLE arena) const; | |
| void* PROTOBUF_NONNULL | |
| AllocateMessage(SerialArena* PROTOBUF_NULLABLE arena) const; | |
| Tag tag() const { return tag_; } | |
| uint32_t allocation_size() const { return allocation_size_; } | |
| uint8_t alignment() const { return alignment_; } | |
| private: | |
| uint32_t allocation_size_; | |
| Tag tag_; | |
| uint8_t alignment_; | |
| Func func_; | |
| }; | |
| // ClassData* can and should be placed on read-only section to maximize sharing. | |
| // However, !LITE has mutable fields for lazy initialization of reflection | |
| // related data. We move these fields to a separate struct to keep the main | |
| // ClassData read-only. Extra indirection should be tolerable considering that | |
| // reflection isn't performance critical. | |
| struct PROTOBUF_EXPORT ReflectionData { | |
| constexpr ReflectionData( | |
| const DescriptorMethods* PROTOBUF_NONNULL descriptor_methods, | |
| const internal::DescriptorTable* PROTOBUF_NULLABLE descriptor_table, | |
| void (*PROTOBUF_NULLABLE get_metadata_tracker)()) | |
| : reflection(nullptr), | |
| descriptor(nullptr), | |
| descriptor_table(descriptor_table), | |
| descriptor_methods(descriptor_methods), | |
| get_metadata_tracker(get_metadata_tracker) {} | |
| // Accesses are protected by the once_flag in `descriptor_table`. When the | |
| // table is null these are populated from the beginning and need to | |
| // protection. | |
| const Reflection* PROTOBUF_NULLABLE reflection; | |
| const Descriptor* PROTOBUF_NULLABLE descriptor; | |
| // Codegen types will provide a DescriptorTable to do lazy | |
| // registration/initialization of the reflection objects. | |
| // Other types, like DynamicMessage, keep the table as null but eagerly | |
| // populate `reflection`/`descriptor` fields. | |
| const internal::DescriptorTable* PROTOBUF_NULLABLE descriptor_table; | |
| const DescriptorMethods* PROTOBUF_NONNULL descriptor_methods; | |
| // When an access tracker is installed, this function notifies the tracker | |
| // that GetMetadata was called. | |
| void (*PROTOBUF_NULLABLE get_metadata_tracker)(); | |
| }; | |
| // Note: The order of arguments in the functions is chosen so that it has | |
| // the same ABI as the member function that calls them. Eg the `this` | |
| // pointer becomes the first argument in the free function. | |
| // | |
| // Future work: | |
| // We could save more data by omitting any optional pointer that would | |
| // otherwise be null. We can have some metadata in ClassData telling us if we | |
| // have them and their offset. | |
| struct PROTOBUF_EXPORT ClassData { | |
| using IsInitializedFunc = bool (*PROTOBUF_NULLABLE)(const MessageLite&); | |
| using MergeToFromFunc = void (*PROTOBUF_NONNULL)(MessageLite& to, | |
| const MessageLite& from_msg); | |
| using DestroyMessageFunc = void (*PROTOBUF_NONNULL)(MessageLite& msg); | |
| using ClearFunc = void (*PROTOBUF_NONNULL)(MessageLite& msg); | |
| using ByteSizeLongFunc = size_t (*PROTOBUF_NONNULL)(const MessageLite&); | |
| using SerializeFunc = uint8_t* PROTOBUF_NULLABLE (*PROTOBUF_NONNULL)( | |
| const MessageLite& msg, uint8_t* PROTOBUF_NULLABLE ptr, | |
| io::EpsCopyOutputStream* PROTOBUF_NONNULL stream); | |
| IsInitializedFunc is_initialized; | |
| MergeToFromFunc merge_to_from; | |
| internal::MessageCreator message_creator; | |
| #if defined(PROTOBUF_CUSTOM_VTABLE) | |
| DestroyMessageFunc destroy_message; | |
| ClearFunc clear; | |
| ByteSizeLongFunc byte_size_long; | |
| SerializeFunc serialize; | |
| #endif // PROTOBUF_CUSTOM_VTABLE | |
| // Offset of the CachedSize member. | |
| uint32_t cached_size_offset; | |
| // LITE objects (ie !descriptor_methods) collocate their name as a | |
| // char[] just beyond the ClassData. | |
| bool is_lite; | |
| bool is_dynamic = false; | |
| // In normal mode we have the small constructor to avoid the cost in | |
| // codegen. | |
| #if !defined(PROTOBUF_CUSTOM_VTABLE) | |
| constexpr ClassData( | |
| IsInitializedFunc is_initialized, MergeToFromFunc merge_to_from, | |
| internal::MessageCreator message_creator, uint32_t cached_size_offset, | |
| std::variant<ReflectionData*, const char*> reflection_or_name) | |
| : is_initialized(is_initialized), | |
| merge_to_from(merge_to_from), | |
| message_creator(message_creator), | |
| cached_size_offset(cached_size_offset), | |
| is_lite(std::holds_alternative<const char*>(reflection_or_name)), | |
| aux_data(GetAuxFromVariant(reflection_or_name)) {} | |
| #endif // !PROTOBUF_CUSTOM_VTABLE | |
| // But we always provide the full constructor even in normal mode to make | |
| // helper code simpler. | |
| constexpr ClassData( | |
| IsInitializedFunc is_initialized, MergeToFromFunc merge_to_from, | |
| internal::MessageCreator message_creator, | |
| [[maybe_unused]] DestroyMessageFunc destroy_message, | |
| [[maybe_unused]] ClearFunc clear, | |
| [[maybe_unused]] ByteSizeLongFunc byte_size_long, | |
| [[maybe_unused]] SerializeFunc serialize, uint32_t cached_size_offset, | |
| std::variant<ReflectionData*, const char*> reflection_or_name) | |
| : is_initialized(is_initialized), | |
| merge_to_from(merge_to_from), | |
| message_creator(message_creator), | |
| #if defined(PROTOBUF_CUSTOM_VTABLE) | |
| destroy_message(destroy_message), | |
| clear(clear), | |
| byte_size_long(byte_size_long), | |
| serialize(serialize), | |
| #endif // PROTOBUF_CUSTOM_VTABLE | |
| cached_size_offset(cached_size_offset), | |
| is_lite(std::holds_alternative<const char*>(reflection_or_name)), | |
| aux_data(GetAuxFromVariant(reflection_or_name)) { | |
| } | |
| const TcParseTableBase* PROTOBUF_NONNULL GetTcParseTable() const; | |
| const MessageLite* PROTOBUF_NONNULL default_instance() const; | |
| // Defined in message_lite.h. | |
| MessageLite* PROTOBUF_NONNULL New(Arena* PROTOBUF_NULLABLE arena) const; | |
| // Defined in message_lite.h. | |
| MessageLite* PROTOBUF_NONNULL New(SerialArena* PROTOBUF_NULLABLE arena) const; | |
| MessageLite* PROTOBUF_NONNULL New(std::nullptr_t) const { | |
| return New(static_cast<Arena*>(nullptr)); | |
| } | |
| // Defined in message_lite.h. | |
| MessageLite* PROTOBUF_NONNULL PlacementNew( | |
| void* PROTOBUF_NONNULL mem, Arena* PROTOBUF_NULLABLE arena) const; | |
| uint32_t allocation_size() const { return message_creator.allocation_size(); } | |
| uint8_t alignment() const { return message_creator.alignment(); } | |
| template <typename MessageLite = google::protobuf::MessageLite> | |
| PROTOBUF_ALWAYS_INLINE void MergeToFrom(MessageLite& to, | |
| const MessageLite& from) const { | |
| ABSL_DCHECK(this == to.GetClassData()) | |
| << "Incorrect class data passed to MergeToFrom: expected " | |
| << to.GetTypeName() << ", got " | |
| << TypeDependent<MessageLite>(default_instance())->GetTypeName(); | |
| ABSL_DCHECK(this == from.GetClassData()) | |
| << "Invalid call to MergeFrom between types " << to.GetTypeName() | |
| << " and " << from.GetTypeName(); | |
| this->merge_to_from(to, from); | |
| } | |
| std::string DebugName() const; | |
| // Accessors for reflection related data (!LITE only). | |
| const Reflection* PROTOBUF_NULLABLE reflection() const { | |
| return reflection_data()->reflection; | |
| } | |
| const Descriptor* PROTOBUF_NULLABLE descriptor() const { | |
| return reflection_data()->descriptor; | |
| } | |
| void set_reflection(const Reflection* PROTOBUF_NULLABLE reflection) const { | |
| reflection_data()->reflection = reflection; | |
| } | |
| void set_descriptor(const Descriptor* PROTOBUF_NULLABLE descriptor) const { | |
| reflection_data()->descriptor = descriptor; | |
| } | |
| const DescriptorTable* PROTOBUF_NULLABLE descriptor_table() const { | |
| return reflection_data()->descriptor_table; | |
| } | |
| const DescriptorMethods* PROTOBUF_NONNULL descriptor_methods() const { | |
| return reflection_data()->descriptor_methods; | |
| } | |
| bool has_get_metadata_tracker() const { | |
| return reflection_data()->get_metadata_tracker != nullptr; | |
| } | |
| void get_metadata_tracker() const { | |
| reflection_data()->get_metadata_tracker(); | |
| } | |
| ReflectionData* PROTOBUF_NONNULL reflection_data() const { | |
| ABSL_DCHECK(!is_lite); | |
| return aux_data.reflection_data; | |
| } | |
| // Accessors for type name (LITE only). | |
| const char* PROTOBUF_NONNULL type_name() const { | |
| ABSL_DCHECK(is_lite); | |
| return aux_data.type_name; | |
| } | |
| union ReflectionDataOrTypeName { | |
| explicit constexpr ReflectionDataOrTypeName( | |
| ReflectionData* PROTOBUF_NONNULL reflection_data) | |
| : reflection_data(reflection_data) {} | |
| explicit constexpr ReflectionDataOrTypeName( | |
| const char* PROTOBUF_NONNULL type_name) | |
| : type_name(type_name) {} | |
| ReflectionData* PROTOBUF_NONNULL reflection_data; | |
| const char* PROTOBUF_NONNULL type_name; | |
| } aux_data; | |
| static constexpr ReflectionDataOrTypeName GetAuxFromVariant( | |
| std::variant<ReflectionData*, const char*> reflection_or_name) { | |
| if (std::holds_alternative<const char*>(reflection_or_name)) { | |
| return ReflectionDataOrTypeName( | |
| std::get<const char*>(reflection_or_name)); | |
| } else { | |
| return ReflectionDataOrTypeName( | |
| std::get<ReflectionData*>(reflection_or_name)); | |
| } | |
| } | |
| }; | |
| // We use a secondary vtable for descriptor based methods. This way ClassData | |
| // does not grow with the number of descriptor methods. This avoids extra | |
| // costs in MessageLite. | |
| struct PROTOBUF_EXPORT DescriptorMethods { | |
| using GetTypeNameFunc = absl::string_view (*PROTOBUF_NONNULL)( | |
| const ClassData* PROTOBUF_NONNULL data); | |
| using InitializationErrorMessageFunc = | |
| std::string (*PROTOBUF_NONNULL)(const MessageLite&); | |
| using GetTcTableFunc = const internal::TcParseTableBase* PROTOBUF_NONNULL ( | |
| *PROTOBUF_NONNULL)(const ClassData* PROTOBUF_NONNULL data); | |
| using SpaceUsedLongFunc = size_t (*PROTOBUF_NONNULL)(const MessageLite&); | |
| using DebugStringFunc = std::string (*PROTOBUF_NONNULL)(const MessageLite&); | |
| using VerifyLazyFieldConsistencyFunc = | |
| void (*PROTOBUF_NONNULL)(const LazyField&); | |
| GetTypeNameFunc get_type_name; | |
| InitializationErrorMessageFunc initialization_error_string; | |
| GetTcTableFunc get_tc_table; | |
| SpaceUsedLongFunc space_used_long; | |
| DebugStringFunc debug_string; | |
| VerifyLazyFieldConsistencyFunc verify_lazy_field_consistency; | |
| }; | |
| } // namespace internal | |
| } // namespace protobuf | |
| } // namespace google | |
| #include "google/protobuf/port_undef.inc" | |
| #endif // GOOGLE_PROTOBUF_CLASS_DATA_H__ |