#ifndef GOOGLE_PROTOBUF_FIELD_WITH_ARENA_H__ #define GOOGLE_PROTOBUF_FIELD_WITH_ARENA_H__ #include #include #include "absl/log/absl_check.h" #include "google/protobuf/arena.h" #include "google/protobuf/internal_metadata_locator.h" #include "google/protobuf/internal_visibility.h" #include "google/protobuf/metadata_lite.h" // Must be included last. #include "google/protobuf/port_def.inc" namespace google { namespace protobuf { namespace internal { // Checks whether `T` has a constructor that takes an internal metadata offset // and an arena pointer. Some constructors won't take the arena pointer if they // don't need it. // // We check for both `InternalVisibility` constructors and normal constructors, // as `std::is_constructible` can only see public constructors. template inline constexpr bool kOffsetConstructorTakesArenaPointer = std::is_constructible_v || std::is_constructible_v; // A container that holds a `T` and an arena pointer, where `T` has an // `InternalMetadataResolver` member. This is used for both directly // arena-allocated `T`'s and split `T`'s. Both cases need to return the correct // thing when a user calls `GetArena()` on `T`, or when internal code needs the // arena to do memory allocation. // // This class is used to store `InternalMetadata` alongside `T` with an // `InternalMetadataResolver`, since `InternalMetadataResolver`s can only point // to an existing arena pointer "nearby" in memory. // // Note that `FieldWithArena` is destructor-skippable if and only if `T` is // destructor-skippable. template class FieldWithArena : public ContainerDestructorSkippableBase { public: using InternalArenaConstructable_ = void; constexpr FieldWithArena() : field_() {} template explicit FieldWithArena(Arena* arena, Args&&... args) : _internal_metadata_(arena) { StaticallyVerifyLayout(); // Construct `T` after setting `_internal_metadata_` so that `T` can safely // call ResolveArena(). if constexpr (kOffsetConstructorTakesArenaPointer) { new (&field_) T(BuildOffset(), arena, std::forward(args)...); } else { new (&field_) T(BuildOffset(), std::forward(args)...); } } ~FieldWithArena() { // The destructor of `FieldWithArena` for destructor-skippable types must // only be called if the field is not allocated on an arena. if constexpr (Arena::is_destructor_skippable::value) { ABSL_DCHECK_EQ(GetArena(), nullptr); } field_.~T(); } const T& field() const { return field_; } T& field() { return field_; } // Returns the arena that the field is allocated on. This is cheaper than // calling `field().GetArena()`. Arena* GetArena() const { return _internal_metadata_.arena(); } private: friend InternalMetadataOffset; static constexpr InternalMetadataOffset BuildOffset(); // A method to statically verify the offset of `field_storage_`. We need to // define this in a member function out of line because `FieldWithArena` needs // to be fully defined to call `offsetof`, but `field_storage_` is private. static constexpr void StaticallyVerifyLayout(); // Generated code sometimes doesn't have a complete type for `T` (for example, // split repeated message fields). When the constructor of `FieldWithArena` is // invoked, both the constructor and destructor of all members are pulled in. // By placing the field in a union, we manually invoke its destructor and can // prevent it from being pulled in except in the `Destroy()` method. union { T field_; }; // Note that the name of this field must be `_internal_metadata_`, as // `InternalMetadataOffset` expects a field with this name. const InternalMetadata _internal_metadata_; }; template constexpr InternalMetadataOffset FieldWithArena::BuildOffset() { return InternalMetadataOffset::Build(); } template constexpr void FieldWithArena::StaticallyVerifyLayout() { static_assert( offsetof(FieldWithArena, field_) == 0, "field_ must be at offset 0 in FieldWithArena. There are multiple places " "throughout the code (e.g. reflection, VerifyHasBitConsistency) which " "assume that you can find the wrapped field by interpreting a pointer as " "the wrapped field type, and aren't aware of this wrapper class. By " "placing `field_` at offset 0 in this struct, this assumption holds."); } } // namespace internal } // namespace protobuf } // namespace google #include "google/protobuf/port_undef.inc" #endif // GOOGLE_PROTOBUF_FIELD_WITH_ARENA_H__