Skip to content

Commit 3ae5a07

Browse files
Reduce code bloat for messages - use virtual methods for Merge/Clear/Delete.
PiperOrigin-RevId: 564768912
1 parent 2447882 commit 3ae5a07

File tree

1 file changed

+46
-31
lines changed

1 file changed

+46
-31
lines changed

src/google/protobuf/repeated_ptr_field.h

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,13 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase {
149149
template <typename Handler>
150150
using Value = typename Handler::Type;
151151

152+
// We use the same Handler for all Message types to deduplicate generated
153+
// code.
154+
template <typename Handler>
155+
using CommonHandler = typename std::conditional<
156+
std::is_base_of<MessageLite, Value<Handler>>::value,
157+
internal::GenericTypeHandler<MessageLite>, Handler>::type;
158+
152159
static constexpr int kSSOCapacity = 1;
153160

154161
protected:
@@ -230,27 +237,24 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase {
230237
void Delete(int index) {
231238
ABSL_DCHECK_GE(index, 0);
232239
ABSL_DCHECK_LT(index, current_size_);
233-
Delete<TypeHandler>(element_at(index), arena_);
240+
using H = CommonHandler<TypeHandler>;
241+
Delete<H>(element_at(index), arena_);
234242
}
235243

236244
// Must be called from destructor.
237245
template <typename TypeHandler>
238246
void Destroy() {
247+
using H = CommonHandler<TypeHandler>;
239248
if (arena_ != nullptr) return;
240-
241-
if (using_sso()) {
242-
if (tagged_rep_or_elem_ == nullptr) return;
243-
Delete<TypeHandler>(tagged_rep_or_elem_, nullptr);
244-
return;
245-
}
246-
247-
Rep* r = rep();
248-
int n = r->allocated_size;
249-
void* const* elems = r->elements;
249+
int n = allocated_size();
250+
void** elems = elements();
250251
for (int i = 0; i < n; i++) {
251-
Delete<TypeHandler>(elems[i], nullptr);
252+
Delete<H>(elems[i], nullptr);
253+
}
254+
if (!using_sso()) {
255+
internal::SizedDelete(rep(),
256+
total_size_ * sizeof(elems[0]) + kRepHeaderSize);
252257
}
253-
internal::SizedDelete(r, total_size_ * sizeof(elems[0]) + kRepHeaderSize);
254258
}
255259

256260
bool NeedsDestroy() const {
@@ -281,7 +285,8 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase {
281285
const int n = current_size_;
282286
ABSL_DCHECK_GE(n, 0);
283287
if (n > 0) {
284-
ClearNonEmpty<TypeHandler>();
288+
using H = CommonHandler<TypeHandler>;
289+
ClearNonEmpty<H>();
285290
}
286291
}
287292

@@ -295,8 +300,8 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase {
295300
// calls the object-allocate and merge handlers.
296301
ABSL_DCHECK_NE(&other, this);
297302
if (other.current_size_ == 0) return;
298-
MergeFromInternal(other,
299-
&RepeatedPtrFieldBase::MergeFromInnerLoop<TypeHandler>);
303+
using H = CommonHandler<TypeHandler>;
304+
MergeFromInternal(other, &RepeatedPtrFieldBase::MergeFromInnerLoop<H>);
300305
}
301306

302307
inline void InternalSwap(RepeatedPtrFieldBase* PROTOBUF_RESTRICT rhs) {
@@ -332,7 +337,8 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase {
332337
void RemoveLast() {
333338
ABSL_DCHECK_GT(current_size_, 0);
334339
ExchangeCurrentSize(current_size_ - 1);
335-
TypeHandler::Clear(cast<TypeHandler>(element_at(current_size_)));
340+
using H = CommonHandler<TypeHandler>;
341+
H::Clear(cast<H>(element_at(current_size_)));
336342
}
337343

338344
template <typename TypeHandler>
@@ -348,9 +354,10 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase {
348354

349355
template <typename TypeHandler>
350356
static inline Value<TypeHandler>* copy(Value<TypeHandler>* value) {
351-
auto* new_value = TypeHandler::NewFromPrototype(value, nullptr);
352-
TypeHandler::Merge(*value, new_value);
353-
return new_value;
357+
using H = CommonHandler<TypeHandler>;
358+
auto* new_value = H::NewFromPrototype(value, nullptr);
359+
H::Merge(*value, new_value);
360+
return cast<TypeHandler>(new_value);
354361
}
355362

356363
// Used for constructing iterators.
@@ -392,7 +399,7 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase {
392399
}
393400

394401
template <typename TypeHandler>
395-
size_t SpaceUsedExcludingSelfLong() const {
402+
PROTOBUF_NOINLINE size_t SpaceUsedExcludingSelfLong() const {
396403
size_t allocated_bytes =
397404
using_sso()
398405
? 0
@@ -437,7 +444,8 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase {
437444
// cleared objects awaiting reuse. We don't want to grow the array in
438445
// this case because otherwise a loop calling AddAllocated() followed by
439446
// Clear() would leak memory.
440-
Delete<TypeHandler>(element_at(current_size_), arena_);
447+
using H = CommonHandler<TypeHandler>;
448+
Delete<H>(element_at(current_size_), arena_);
441449
} else if (current_size_ < allocated_size()) {
442450
// We have some cleared objects. We don't care about their order, so we
443451
// can just move the first one to the end to make space.
@@ -568,8 +576,9 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase {
568576
my_arena->Own(value);
569577
} else if (my_arena != value_arena) {
570578
auto* new_value = TypeHandler::NewFromPrototype(value, my_arena);
571-
TypeHandler::Merge(*value, new_value);
572-
TypeHandler::Delete(value, value_arena);
579+
using H = CommonHandler<TypeHandler>;
580+
H::Merge(*value, new_value);
581+
H::Delete(value, value_arena);
573582
value = new_value;
574583
}
575584

@@ -741,9 +750,8 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase {
741750
const int n = current_size_;
742751
void* const* elems = elements();
743752
int i = 0;
744-
ABSL_DCHECK_GT(
745-
n,
746-
0); // do/while loop to avoid initial test because we know n > 0
753+
ABSL_DCHECK_GT(n, 0);
754+
// do/while loop to avoid initial test because we know n > 0
747755
do {
748756
TypeHandler::Clear(cast<TypeHandler>(elems[i++]));
749757
} while (i < n);
@@ -866,7 +874,8 @@ class GenericTypeHandler {
866874
// NewFromPrototypeHelper() is not defined inline here, as we will need to do a
867875
// virtual function dispatch anyways to go from Message* to call New/Merge. (The
868876
// additional helper is needed as a workaround for MSVC.)
869-
MessageLite* NewFromPrototypeHelper(const MessageLite* prototype, Arena* arena);
877+
PROTOBUF_EXPORT MessageLite* NewFromPrototypeHelper(
878+
const MessageLite* prototype, Arena* arena);
870879

871880
template <>
872881
inline MessageLite* GenericTypeHandler<MessageLite>::NewFromPrototype(
@@ -885,8 +894,8 @@ PROTOBUF_NOINLINE inline void GenericTypeHandler<GenericType>::Merge(
885894
to->MergeFrom(from);
886895
}
887896
template <>
888-
void GenericTypeHandler<MessageLite>::Merge(const MessageLite& from,
889-
MessageLite* to);
897+
PROTOBUF_EXPORT void GenericTypeHandler<MessageLite>::Merge(
898+
const MessageLite& from, MessageLite* to);
890899

891900
template <>
892901
inline void GenericTypeHandler<std::string>::Clear(std::string* value) {
@@ -1642,7 +1651,13 @@ inline Arena* RepeatedPtrField<Element>::GetOwningArena() const {
16421651

16431652
template <typename Element>
16441653
inline size_t RepeatedPtrField<Element>::SpaceUsedExcludingSelfLong() const {
1645-
return RepeatedPtrFieldBase::SpaceUsedExcludingSelfLong<TypeHandler>();
1654+
// `google::protobuf::Message` has a virtual method `SpaceUsedLong`, hence we can
1655+
// instantiate just one function for all protobuf messages.
1656+
// Note: std::is_base_of requires that `Element` is a concrete class.
1657+
using H = typename std::conditional<std::is_base_of<Message, Element>::value,
1658+
internal::GenericTypeHandler<Message>,
1659+
TypeHandler>::type;
1660+
return RepeatedPtrFieldBase::SpaceUsedExcludingSelfLong<H>();
16461661
}
16471662

16481663
template <typename Element>

0 commit comments

Comments
 (0)