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