Move Rep out of RepeatedField and rename to HeapRep.
Motivation: refactoring in preparation for small object optimization in RepeatedField.
PiperOrigin-RevId: 651866147
diff --git a/src/google/protobuf/repeated_field.h b/src/google/protobuf/repeated_field.h
index a8eb940..31271de 100644
--- a/src/google/protobuf/repeated_field.h
+++ b/src/google/protobuf/repeated_field.h
@@ -95,6 +95,23 @@
using DestructorSkippable_ = void;
};
+template <size_t kMinSize>
+struct HeapRep {
+ // Avoid 'implicitly deleted dtor' warnings on certain compilers.
+ ~HeapRep() = delete;
+
+ void* elements() { return this + 1; }
+
+ // Align to 8 as sanitizers are picky on the alignment of containers to start
+ // at 8 byte offsets even when compiling for 32 bit platforms.
+ union {
+ alignas(8) Arena* arena;
+ // We pad the header to be at least `sizeof(Element)` so that we have
+ // power-of-two sized allocations, which enables Arena optimizations.
+ char padding[kMinSize];
+ };
+};
+
} // namespace internal
// RepeatedField is used to represent repeated fields of a primitive type (in
@@ -296,7 +313,7 @@
// function non-const.
inline Arena* GetArena() {
return Capacity() == 0 ? static_cast<Arena*>(arena_or_elements_)
- : rep()->arena;
+ : heap_rep()->arena;
}
// For internal use only.
@@ -306,28 +323,17 @@
private:
using InternalArenaConstructable_ = void;
+ // We use std::max in order to share template instantiations between
+ // different element types.
+ using HeapRep = internal::HeapRep<std::max<size_t>(sizeof(Element), 8)>;
template <typename T>
friend class Arena::InternalHelper;
friend class Arena;
- // Pad the rep to being max(Arena*, Element) with a minimum align
- // of 8 as sanitizers are picky on the alignment of containers to
- // start at 8 byte offsets even when compiling for 32 bit platforms.
- struct Rep {
- union {
- alignas(8) Arena* arena;
- Element unused;
- };
- Element* elements() { return reinterpret_cast<Element*>(this + 1); }
-
- // Avoid 'implicitly deleted dtor' warnings on certain compilers.
- ~Rep() = delete;
- };
-
static constexpr int kInitialSize = 0;
- static PROTOBUF_CONSTEXPR const size_t kRepHeaderSize = sizeof(Rep);
+ static PROTOBUF_CONSTEXPR const size_t kRepHeaderSize = sizeof(HeapRep);
RepeatedField(Arena* arena, const RepeatedField& rhs);
RepeatedField(Arena* arena, RepeatedField&& rhs);
@@ -425,28 +431,28 @@
return static_cast<Element*>(arena_or_elements_);
}
- // Returns a pointer to the Rep struct.
- // pre-condition: the Rep must have been allocated, ie elements() is safe.
- Rep* rep() const {
- return reinterpret_cast<Rep*>(reinterpret_cast<char*>(elements()) -
- kRepHeaderSize);
+ // Returns a pointer to the HeapRep struct.
+ // pre-condition: the HeapRep must have been allocated, ie elements() is safe.
+ HeapRep* heap_rep() const {
+ return reinterpret_cast<HeapRep*>(reinterpret_cast<char*>(elements()) -
+ kRepHeaderSize);
}
// Internal helper to delete all elements and deallocate the storage.
template <bool in_destructor = false>
void InternalDeallocate() {
const size_t bytes = Capacity() * sizeof(Element) + kRepHeaderSize;
- if (rep()->arena == nullptr) {
- internal::SizedDelete(rep(), bytes);
+ if (heap_rep()->arena == nullptr) {
+ internal::SizedDelete(heap_rep(), bytes);
} else if (!in_destructor) {
// If we are in the destructor, we might be being destroyed as part of
// the arena teardown. We can't try and return blocks to the arena then.
- rep()->arena->ReturnArrayMemory(rep(), bytes);
+ heap_rep()->arena->ReturnArrayMemory(heap_rep(), bytes);
}
}
// A note on the representation here (see also comment below for
- // RepeatedPtrFieldBase's struct Rep):
+ // RepeatedPtrFieldBase's struct HeapRep):
//
// We maintain the same sizeof(RepeatedField) as before we added arena support
// so that we do not degrade performance by bloating memory usage. Directly
@@ -458,8 +464,9 @@
int size_;
int capacity_;
// If capacity_ == 0 this points to an Arena otherwise it points to the
- // elements member of a Rep struct. Using this invariant allows the storage of
- // the arena pointer without an extra allocation in the constructor.
+ // elements member of a HeapRep struct. Using this invariant allows the
+ // storage of the arena pointer without an extra allocation in the
+ // constructor.
void* arena_or_elements_;
};
@@ -938,7 +945,7 @@
PROTOBUF_NOINLINE void RepeatedField<Element>::GrowNoAnnotate(int current_size,
int new_size) {
ABSL_DCHECK_GT(new_size, Capacity());
- Rep* new_rep;
+ HeapRep* new_rep;
Arena* arena = GetArena();
new_size = internal::CalculateReserveSize<Element, kRepHeaderSize>(Capacity(),
@@ -959,15 +966,16 @@
std::min((res.n - kRepHeaderSize) / sizeof(Element),
static_cast<size_t>(std::numeric_limits<int>::max()));
new_size = static_cast<int>(num_available);
- new_rep = static_cast<Rep*>(res.p);
+ new_rep = static_cast<HeapRep*>(res.p);
} else {
- new_rep = reinterpret_cast<Rep*>(Arena::CreateArray<char>(arena, bytes));
+ new_rep =
+ reinterpret_cast<HeapRep*>(Arena::CreateArray<char>(arena, bytes));
}
new_rep->arena = arena;
if (Capacity() > 0) {
if (current_size > 0) {
- Element* pnew = new_rep->elements();
+ Element* pnew = static_cast<Element*>(new_rep->elements());
Element* pold = elements();
// TODO: add absl::is_trivially_relocatable<Element>
if (std::is_trivial<Element>::value) {
@@ -983,7 +991,7 @@
}
set_capacity(new_size);
- arena_or_elements_ = new_rep->elements();
+ arena_or_elements_ = static_cast<Element*>(new_rep->elements());
}
// Ideally we would be able to use:
diff --git a/src/google/protobuf/repeated_field_unittest.cc b/src/google/protobuf/repeated_field_unittest.cc
index 144e17c..4b4d328 100644
--- a/src/google/protobuf/repeated_field_unittest.cc
+++ b/src/google/protobuf/repeated_field_unittest.cc
@@ -274,6 +274,7 @@
CheckAllocationSizes<RepeatedField<bool>>(false);
CheckAllocationSizes<RepeatedField<uint32_t>>(false);
CheckAllocationSizes<RepeatedField<uint64_t>>(false);
+ CheckAllocationSizes<RepeatedField<absl::Cord>>(false);
}
TEST(RepeatedField, NaturalGrowthOnArenasReuseBlocks) {