From ff402cc563c2bd84568bb26ab888bb523489aabe Mon Sep 17 00:00:00 2001 From: Samuel Benzaquen Date: Mon, 20 Apr 2026 10:32:51 -0700 Subject: [PATCH] Add PROTOBUF_ASSUME annotations to specify that size() is non-negative. This allows the compiler to optimize bound checking code. PiperOrigin-RevId: 902708802 --- src/google/protobuf/repeated_field.h | 10 ++++++-- .../protobuf/repeated_field_unittest.cc | 8 +++++++ src/google/protobuf/repeated_ptr_field.h | 24 ++++++++++++------- .../protobuf/repeated_ptr_field_unittest.cc | 8 +++++++ 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/google/protobuf/repeated_field.h b/src/google/protobuf/repeated_field.h index fc3a06f455452..db4a296b09e7a 100644 --- a/src/google/protobuf/repeated_field.h +++ b/src/google/protobuf/repeated_field.h @@ -191,7 +191,11 @@ class SooRep { Arena* arena() const { return ResolveTaggedArena<&SooRep::resolver_, kResolverTaggedBits>(this); } - int size() const { return size_; } + int size() const { + int res = size_; + PROTOBUF_ASSUME(res >= 0); + return res; + } void set_size(int size) { ABSL_DCHECK(!is_soo() || size <= kSooCapacityBytes); size_ = size; @@ -559,7 +563,9 @@ class ABSL_ATTRIBUTE_WARN_UNUSED PROTOBUF_DECLSPEC_EMPTY_BASES soo_rep_.set_size(size); } int Capacity(bool is_soo) const { - return is_soo ? kSooCapacityElements : soo_rep_.capacity(); + int res = is_soo ? kSooCapacityElements : soo_rep_.capacity(); + PROTOBUF_ASSUME(res >= 0); + return res; } template diff --git a/src/google/protobuf/repeated_field_unittest.cc b/src/google/protobuf/repeated_field_unittest.cc index 35affbfca01d3..4edf0f9b9e884 100644 --- a/src/google/protobuf/repeated_field_unittest.cc +++ b/src/google/protobuf/repeated_field_unittest.cc @@ -1620,4 +1620,12 @@ TEST(RepeatedFieldIsFullTest, DISABLED_MergeFromPacked) { } // namespace protobuf } // namespace google +// Code thunks to be dumped by the debugger to inspect the generated assemtbly. +static const int& CodegenRepeatedFieldGet(const google::protobuf::RepeatedField& a, + int idx) { + return a[idx]; +} + +static volatile int odr_use = absl::HashOf(&CodegenRepeatedFieldGet); + #include "google/protobuf/port_undef.inc" diff --git a/src/google/protobuf/repeated_ptr_field.h b/src/google/protobuf/repeated_ptr_field.h index 048713f7ae931..efd7926bdeea0 100644 --- a/src/google/protobuf/repeated_ptr_field.h +++ b/src/google/protobuf/repeated_ptr_field.h @@ -224,14 +224,22 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase { } bool empty() const { return current_size_ == 0; } - int size() const { return current_size_; } + int size() const { + int res = current_size_; + PROTOBUF_ASSUME(res >= 0); + return res; + } // Returns the size of the buffer with pointers to elements. // // Note: // // * prefer `SizeAtCapacity()` to `size() == Capacity()`; // * prefer `AllocatedSizeAtCapacity()` to `allocated_size() == Capacity()`. - int Capacity() const { return using_sso() ? kSSOCapacity : rep()->capacity; } + int Capacity() const { + int res = using_sso() ? kSSOCapacity : rep()->capacity; + PROTOBUF_ASSUME(res >= 0); + return res; + } template const Value& at(int index) const { @@ -249,7 +257,7 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase { template Value* Mutable(int index) { - RuntimeAssertInBounds(index, current_size_); + RuntimeAssertInBounds(index, size()); return cast(element_at(index)); } @@ -340,7 +348,7 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase { template PROTOBUF_FUTURE_ADD_NODISCARD const Value& Get(int index) const { if constexpr (GetBoundsCheckMode() == BoundsCheckMode::kReturnDefault) { - if (ABSL_PREDICT_FALSE(index < 0 || index >= current_size_)) { + if (ABSL_PREDICT_FALSE(index < 0 || index >= size())) { // `default_instance()` is not supported for MessageLite and Message. if constexpr (TypeHandler::has_default_instance()) { LogIndexOutOfBounds(index, current_size_); @@ -350,7 +358,7 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase { } // We refactor this to a separate function instead of inlining it so we // can measure the performance impact more easily. - RuntimeAssertInBounds(index, current_size_); + RuntimeAssertInBounds(index, size()); return *cast(element_at(index)); } @@ -518,8 +526,8 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase { } void SwapElements(int index1, int index2) { - internal::RuntimeAssertInBounds(index1, current_size_); - internal::RuntimeAssertInBounds(index2, current_size_); + internal::RuntimeAssertInBounds(index1, size()); + internal::RuntimeAssertInBounds(index2, size()); using std::swap; // enable ADL with fallback swap(element_at(index1), element_at(index2)); } @@ -625,7 +633,7 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase { // arena. template Value* UnsafeArenaReleaseLast() { - internal::RuntimeAssertInBounds(0, current_size_); + internal::RuntimeAssertInBounds(0, size()); ExchangeCurrentSize(current_size_ - 1); auto* result = cast(element_at(current_size_)); if (using_sso()) { diff --git a/src/google/protobuf/repeated_ptr_field_unittest.cc b/src/google/protobuf/repeated_ptr_field_unittest.cc index f3d9ac28d56ad..52284e14b48ff 100644 --- a/src/google/protobuf/repeated_ptr_field_unittest.cc +++ b/src/google/protobuf/repeated_ptr_field_unittest.cc @@ -2029,4 +2029,12 @@ TEST_F(RepeatedPtrFieldInsertionIteratorsTest, MoveProtos) { } // namespace protobuf } // namespace google +// Code thunks to be dumped by the debugger to inspect the generated assemtbly. +static auto& CodegenRepeatedPtrFieldGet( + const google::protobuf::RepeatedPtrField& a, int idx) { + return a[idx]; +} + +static volatile int odr_use = absl::HashOf(&CodegenRepeatedPtrFieldGet); + #include "google/protobuf/port_undef.inc"