Add special handling of `RepeatedPtrField`s in `Arena`. This avoids calling the `RepeatedPtrField(Arena*)` constructor of `RepeatedPtrField`s and sets up the machinery to handle specializing how `RepeatedPtrField`s are allocated on arenas. PiperOrigin-RevId: 805959754
diff --git a/src/google/protobuf/BUILD.bazel b/src/google/protobuf/BUILD.bazel index b776c81..42388e4 100644 --- a/src/google/protobuf/BUILD.bazel +++ b/src/google/protobuf/BUILD.bazel
@@ -557,6 +557,7 @@ "@abseil-cpp//absl/container:layout", "@abseil-cpp//absl/log:absl_check", "@abseil-cpp//absl/log:absl_log", + "@abseil-cpp//absl/meta:type_traits", "@abseil-cpp//absl/numeric:bits", "@abseil-cpp//absl/synchronization", "@abseil-cpp//absl/types:span",
diff --git a/src/google/protobuf/arena.h b/src/google/protobuf/arena.h index 3ed71de..5884a9a 100644 --- a/src/google/protobuf/arena.h +++ b/src/google/protobuf/arena.h
@@ -19,6 +19,9 @@ #include <type_traits> #include <utility> #include <vector> + +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" #if defined(_MSC_VER) && !defined(_LIBCPP_STD_VER) && !_HAS_EXCEPTIONS // Work around bugs in MSVC <typeinfo> header when _HAS_EXCEPTIONS=0. #include <exception> @@ -78,6 +81,12 @@ class GenericTypeHandler; // defined in repeated_field.h template <typename T> +struct IsRepeatedPtrFieldType; // defined in repeated_ptr_field.h + +template <typename T> +struct RepeatedPtrFieldArenaRep; // defined in repeated_ptr_field.h + +template <typename T> void arena_delete_object(void* PROTOBUF_NONNULL object) { delete reinterpret_cast<T*>(object); } @@ -222,7 +231,12 @@ return static_cast<Type*>(CopyConstruct<Type>(arena, &args...)); } } - return CreateArenaCompatible<Type>(arena, std::forward<Args>(args)...); + if constexpr (internal::IsRepeatedPtrFieldType<Type>::value) { + return CreateRepeatedPtrFieldWithArena<Type>( + arena, std::forward<Args>(args)...); + } else { + return CreateArenaCompatible<Type>(arena, std::forward<Args>(args)...); + } } else { if (ABSL_PREDICT_FALSE(arena == nullptr)) { return new T(std::forward<Args>(args)...); @@ -431,11 +445,24 @@ static T* PROTOBUF_NONNULL Construct(void* PROTOBUF_NONNULL ptr, Arena* PROTOBUF_NULLABLE arena, Args&&... args) { - return new (ptr) T(arena, static_cast<Args&&>(args)...); + if constexpr (internal::IsRepeatedPtrFieldType<T>::value) { + using ArenaRepT = typename internal::RepeatedPtrFieldArenaRep<T>::Type; + auto* arena_repr = + new (ptr) ArenaRepT(arena, static_cast<Args&&>(args)...); + return arena_repr; + } else { + return new (ptr) T(arena, static_cast<Args&&>(args)...); + } } static PROTOBUF_ALWAYS_INLINE T* PROTOBUF_NONNULL New() { - return new T(nullptr); + // Repeated pointer fields no longer have an arena constructor, so + // specialize calling their default constructor. + if constexpr (internal::IsRepeatedPtrFieldType<T>::value) { + return new T(); + } else { + return new T(nullptr); + } } friend class Arena; @@ -517,6 +544,25 @@ } } + template <typename T, typename... Args> + PROTOBUF_NDEBUG_INLINE static T* PROTOBUF_NONNULL + CreateRepeatedPtrFieldWithArena(Arena* PROTOBUF_NULLABLE arena, + Args&&... args) { + static_assert(is_arena_constructable<T>::value, + "Can only construct types that are ArenaConstructable"); + static_assert(internal::IsRepeatedPtrFieldType<T>::value, + "Can only construct repeated pointer fields with " + "CreateRepeatedPtrFieldWithArena"); + if (ABSL_PREDICT_FALSE(arena == nullptr)) { + return new T(static_cast<Args&&>(args)...); + } else { + using ArenaRepT = typename internal::RepeatedPtrFieldArenaRep<T>::Type; + auto* arena_repr = + arena->DoCreateMessage<ArenaRepT>(static_cast<Args&&>(args)...); + return arena_repr; + } + } + // This specialization for no arguments is necessary, because its behavior is // slightly different. When the arena pointer is nullptr, it calls T() // instead of T(nullptr).
diff --git a/src/google/protobuf/repeated_ptr_field.h b/src/google/protobuf/repeated_ptr_field.h index 5bf3aaf..952751f 100644 --- a/src/google/protobuf/repeated_ptr_field.h +++ b/src/google/protobuf/repeated_ptr_field.h
@@ -586,6 +586,8 @@ using InternalArenaConstructable_ = void; using DestructorSkippable_ = void; + friend google::protobuf::Arena; + template <typename T> friend class Arena::InternalHelper; @@ -953,6 +955,49 @@ }; +template <typename T> +struct IsRepeatedPtrFieldType { + static constexpr bool value = false; +}; + +template <> +struct IsRepeatedPtrFieldType<RepeatedPtrFieldBase> { + static constexpr bool value = true; +}; + +template <typename Element> +struct IsRepeatedPtrFieldType<RepeatedPtrField<Element>> { + static constexpr bool value = true; +}; + +// This class maps RepeatedPtrField(Base)? types to the types that we will use +// to represent them when allocated on an arena. This is necessary because +// `RepeatedPtrField`s do not own an arena pointer, but can be allocated +// directly on an arena. In this case, we will use a wrapper class that holds +// both the arena pointer and the repeated field, and points the repeated field +// to the arena pointer. +// +// Additionally, split repeated pointer fields will use this representation when +// allocated, regardless of whether they are on an arena or not. +template <typename T> +struct RepeatedPtrFieldArenaRep {}; + +template <> +struct RepeatedPtrFieldArenaRep<RepeatedPtrFieldBase> { + // TODO - With removed arena pointers, we will need a class that + // holds both the arena pointer and the repeated field, and points the + // repeated to the arena pointer. + using Type = RepeatedPtrFieldBase; +}; + +template <typename Element> +struct RepeatedPtrFieldArenaRep<RepeatedPtrField<Element>> { + // TODO - With removed arena pointers, we will need a class that + // holds both the arena pointer and the repeated field, and points the + // repeated to the arena pointer. + using Type = RepeatedPtrField<Element>; +}; + } // namespace internal // RepeatedPtrField is like RepeatedField, but used for repeated strings or