Implement iterator methods for RepeatedFieldProxy.

Note that we expose the iterator types of RepeatedField and RepeatedPtrField directly. This is fine, as both iterator types are in the `internal` namespace and thus can't be spelled in application code.

`absl::string_view` fields are still using the `RepeatedPtrIterator<std::string>` iterators, which leak the `std::string` backing of these fields. This is fixed in a follow-up cl.

PiperOrigin-RevId: 881161313
This commit is contained in:
Clayton Knittel 2026-03-09 19:42:16 -07:00 committed by Copybara-Service
parent 82bcc5ed59
commit 627c33533d
2 changed files with 385 additions and 22 deletions

View file

@ -219,6 +219,13 @@ class RepeatedFieldProxyBase {
return static_cast<size_type>(field().size());
}
[[nodiscard]] const_iterator cbegin() const { return begin(); }
[[nodiscard]] const_iterator cend() const { return end(); }
[[nodiscard]] iterator begin() const { return field().begin(); }
[[nodiscard]] iterator end() const { return field().end(); }
[[nodiscard]] reverse_iterator rbegin() const { return field().rbegin(); }
[[nodiscard]] reverse_iterator rend() const { return field().rend(); }
protected:
explicit RepeatedFieldProxyBase(ConstQualifiedRepeatedFieldType& field)
: field_(&field) {}

View file

@ -4,6 +4,7 @@
#include <cstdint>
#include <cstring>
#include <functional>
#include <limits>
#include <string>
#include <type_traits>
#include <utility>
@ -28,6 +29,8 @@ namespace {
using ::proto2_unittest::RepeatedFieldProxyTestSimpleMessage;
using ::testing::ElementsAre;
using ::testing::IsEmpty;
using ::testing::Not;
static constexpr absl::string_view kLongString =
"long string that will be heap allocated";
@ -138,14 +141,156 @@ class RepeatedFieldProxyTest : public testing::TestWithParam<bool> {
Arena arena_;
};
TEST_P(RepeatedFieldProxyTest, RepeatedInt32) {
auto field = MakeRepeatedFieldContainer<int32_t>();
RepeatedFieldProxy<int32_t> proxy = field.MakeProxy();
proxy.push_back(1);
proxy.push_back(2);
proxy.push_back(3);
EXPECT_THAT(proxy, ElementsAre(1, 2, 3));
EXPECT_THAT(*field, ElementsAre(1, 2, 3));
proxy.set(1, 4);
EXPECT_THAT(proxy, ElementsAre(1, 4, 3));
EXPECT_THAT(*field, ElementsAre(1, 4, 3));
}
TEST_P(RepeatedFieldProxyTest, ConstRepeatedInt32) {
auto field = MakeRepeatedFieldContainer<int32_t>();
field->Add(1);
field->Add(2);
field->Add(3);
{
RepeatedFieldProxy<const int32_t> proxy = field.MakeConstProxy();
EXPECT_THAT(proxy, ElementsAre(1, 2, 3));
}
{
RepeatedFieldProxy<const int32_t> proxy = field.MakeProxy();
EXPECT_THAT(proxy, ElementsAre(1, 2, 3));
}
}
TEST_P(RepeatedFieldProxyTest, RepeatedUint32) {
auto field = MakeRepeatedFieldContainer<uint32_t>();
RepeatedFieldProxy<uint32_t> proxy = field.MakeProxy();
proxy.push_back(1);
EXPECT_THAT(proxy, ElementsAre(1));
EXPECT_THAT(*field, ElementsAre(1));
}
TEST_P(RepeatedFieldProxyTest, RepeatedInt64) {
auto field = MakeRepeatedFieldContainer<int64_t>();
RepeatedFieldProxy<int64_t> proxy = field.MakeProxy();
proxy.push_back(std::numeric_limits<int64_t>::min());
EXPECT_THAT(proxy, ElementsAre(std::numeric_limits<int64_t>::min()));
EXPECT_THAT(*field, ElementsAre(std::numeric_limits<int64_t>::min()));
}
TEST_P(RepeatedFieldProxyTest, RepeatedUint64) {
auto field = MakeRepeatedFieldContainer<uint64_t>();
RepeatedFieldProxy<uint64_t> proxy = field.MakeProxy();
proxy.push_back(std::numeric_limits<uint64_t>::max());
EXPECT_THAT(proxy, ElementsAre(std::numeric_limits<uint64_t>::max()));
EXPECT_THAT(*field, ElementsAre(std::numeric_limits<uint64_t>::max()));
}
TEST_P(RepeatedFieldProxyTest, RepeatedFloat) {
auto field = MakeRepeatedFieldContainer<float>();
RepeatedFieldProxy<float> proxy = field.MakeProxy();
proxy.push_back(1.5);
EXPECT_THAT(proxy, ElementsAre(1.5));
EXPECT_THAT(*field, ElementsAre(1.5));
}
TEST_P(RepeatedFieldProxyTest, RepeatedDouble) {
auto field = MakeRepeatedFieldContainer<double>();
RepeatedFieldProxy<double> proxy = field.MakeProxy();
proxy.push_back(std::numeric_limits<double>::max());
EXPECT_THAT(proxy, ElementsAre(std::numeric_limits<double>::max()));
EXPECT_THAT(*field, ElementsAre(std::numeric_limits<double>::max()));
}
TEST_P(RepeatedFieldProxyTest, RepeatedString) {
auto field = MakeRepeatedFieldContainer<std::string>();
RepeatedFieldProxy<std::string> proxy = field.MakeProxy();
proxy.push_back("one");
proxy.push_back("two");
proxy.push_back("three");
EXPECT_THAT(proxy, ElementsAre("one", "two", "three"));
EXPECT_THAT(*field, ElementsAre("one", "two", "three"));
proxy[1] = "four";
EXPECT_THAT(proxy, ElementsAre("one", "four", "three"));
EXPECT_THAT(*field, ElementsAre("one", "four", "three"));
}
TEST_P(RepeatedFieldProxyTest, ConstRepeatedString) {
auto field = MakeRepeatedFieldContainer<std::string>();
field->Add("one");
field->Add("two");
field->Add("three");
{
RepeatedFieldProxy<const std::string> proxy = field.MakeConstProxy();
EXPECT_THAT(proxy, ElementsAre("one", "two", "three"));
}
{
RepeatedFieldProxy<const std::string> proxy = field.MakeProxy();
EXPECT_THAT(proxy, ElementsAre("one", "two", "three"));
}
}
TEST_P(RepeatedFieldProxyTest, RepeatedMessage) {
auto field =
MakeRepeatedFieldContainer<RepeatedFieldProxyTestSimpleMessage>();
RepeatedFieldProxy<RepeatedFieldProxyTestSimpleMessage> proxy =
field.MakeProxy();
proxy.emplace_back().set_value(1);
RepeatedFieldProxyTestSimpleMessage msg;
msg.set_value(2);
proxy.push_back(std::move(msg));
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb")));
}
TEST_P(RepeatedFieldProxyTest, ConstRepeatedMessage) {
auto field =
MakeRepeatedFieldContainer<RepeatedFieldProxyTestSimpleMessage>();
field->Add()->set_value(1);
field->Add()->set_value(2);
{
RepeatedFieldProxy<const RepeatedFieldProxyTestSimpleMessage> proxy =
field.MakeConstProxy();
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb")));
}
{
RepeatedFieldProxy<const RepeatedFieldProxyTestSimpleMessage> proxy =
field.MakeProxy();
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb")));
}
}
TEST_P(RepeatedFieldProxyTest, Empty) {
auto field =
MakeRepeatedFieldContainer<RepeatedFieldProxyTestSimpleMessage>();
RepeatedFieldProxy<RepeatedFieldProxyTestSimpleMessage> proxy =
field.MakeProxy();
EXPECT_TRUE(proxy.empty());
EXPECT_THAT(proxy, IsEmpty());
proxy.emplace_back();
EXPECT_FALSE(proxy.empty());
EXPECT_THAT(proxy, Not(IsEmpty()));
}
TEST_P(RepeatedFieldProxyTest, ConstEmpty) {
@ -252,9 +397,7 @@ TEST_P(RepeatedFieldProxyTest, MutateElementPrimitive) {
auto proxy = field.MakeProxy();
proxy.set(0, 4);
EXPECT_EQ(proxy[0], 4);
EXPECT_EQ(proxy[1], 2);
EXPECT_EQ(proxy[2], 3);
EXPECT_THAT(proxy, ElementsAre(4, 2, 3));
}
}
@ -272,10 +415,8 @@ void TestMutateStringElement(google::protobuf::RepeatedFieldProxy<StringType> pr
proxy[2] = c_str;
proxy[3] = StrAs<absl::string_view>("8");
EXPECT_THAT(proxy[0], StringEq("5"));
EXPECT_THAT(proxy[1], StringEq("6"));
EXPECT_THAT(proxy[2], StringEq("7"));
EXPECT_THAT(proxy[3], StringEq("8"));
EXPECT_THAT(proxy, ElementsAre(StringEq("5"), StringEq("6"), StringEq("7"),
StringEq("8")));
}
auto long_string = std::string(kLongString);
@ -297,10 +438,8 @@ void TestMutateStringElement(google::protobuf::RepeatedFieldProxy<StringType> pr
proxy.set(2, c_str2);
proxy.set(3, absl::string_view("12"));
EXPECT_THAT(proxy[0], StringEq("9"));
EXPECT_THAT(proxy[1], StringEq("10"));
EXPECT_THAT(proxy[2], StringEq("11"));
EXPECT_THAT(proxy[3], StringEq("12"));
EXPECT_THAT(proxy, ElementsAre(StringEq("9"), StringEq("10"), StringEq("11"),
StringEq("12")));
std::string str13 = "13", str14 = "14";
proxy.set(0, std::ref(str13));
@ -308,10 +447,8 @@ void TestMutateStringElement(google::protobuf::RepeatedFieldProxy<StringType> pr
proxy.set(2, std::ref("15"));
proxy.set(3, std::cref("16"));
EXPECT_THAT(proxy[0], StringEq("13"));
EXPECT_THAT(proxy[1], StringEq("14"));
EXPECT_THAT(proxy[2], StringEq("15"));
EXPECT_THAT(proxy[3], StringEq("16"));
EXPECT_THAT(proxy, ElementsAre(StringEq("13"), StringEq("14"), StringEq("15"),
StringEq("16")));
auto cord = absl::Cord(kLongString);
proxy.set(0, std::move(cord));
@ -355,9 +492,12 @@ TEST_P(RepeatedFieldProxyTest, MutateElementMessage) {
auto proxy = field.MakeProxy();
proxy[2].set_value(4);
EXPECT_THAT(proxy[0], EqualsProto(R"pb(value: 1)pb"));
EXPECT_THAT(proxy[1], EqualsProto(R"pb(value: 2)pb"));
EXPECT_THAT(proxy[2], EqualsProto(R"pb(value: 4)pb"));
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 4)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 4)pb")));
RepeatedFieldProxyTestSimpleMessage msg;
msg.set_value(5);
@ -380,10 +520,14 @@ TEST_P(RepeatedFieldProxyTest, MutateElementMessage) {
}
}
EXPECT_THAT(proxy[0], EqualsProto(R"pb(value: 5)pb"));
EXPECT_THAT(proxy[1], EqualsProto(R"pb(value: 6,
nested { value: 7 })pb"));
EXPECT_THAT(proxy[2], EqualsProto(R"pb(value: 4)pb"));
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 5)pb"),
EqualsProto(R"pb(value: 6,
nested { value: 7 })pb"),
EqualsProto(R"pb(value: 4)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 5)pb"),
EqualsProto(R"pb(value: 6,
nested { value: 7 })pb"),
EqualsProto(R"pb(value: 4)pb")));
}
TEST_P(RepeatedFieldProxyTest, PushBackInt) {
@ -393,6 +537,7 @@ TEST_P(RepeatedFieldProxyTest, PushBackInt) {
proxy.push_back(2);
proxy.push_back(3);
EXPECT_THAT(proxy, ElementsAre(1, 2, 3));
EXPECT_THAT(*field, ElementsAre(1, 2, 3));
}
@ -410,6 +555,9 @@ TEST_P(RepeatedFieldProxyTest, PushBackMessage) {
msg3.set_value(3);
proxy.push_back(msg3);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(value: 1)pb"),
EqualsProto(R"pb(value: 2)pb"),
EqualsProto(R"pb(value: 3)pb")));
@ -424,6 +572,7 @@ TEST_P(RepeatedFieldProxyTest, PushBackMessageLvalueCopies) {
proxy.push_back(*msg1);
EXPECT_NE(proxy[0].mutable_nested(), nested);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
if (!UseArena()) {
@ -440,6 +589,7 @@ TEST_P(RepeatedFieldProxyTest, PushBackMessageRvalueDoesNotCopy) {
proxy.push_back(std::move(*msg1));
EXPECT_EQ(proxy[0].mutable_nested(), nested);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
if (!UseArena()) {
@ -521,6 +671,7 @@ TEST_P(RepeatedFieldProxyTest, EmplaceBackInt) {
proxy.emplace_back(2);
proxy.emplace_back(3);
EXPECT_THAT(proxy, ElementsAre(1, 2, 3));
EXPECT_THAT(*field, ElementsAre(1, 2, 3));
}
@ -533,6 +684,7 @@ TEST_P(RepeatedFieldProxyTest, EmplaceBackMessageLvalueCopies) {
proxy.emplace_back(*msg1);
EXPECT_NE(proxy[0].mutable_nested(), nested);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
if (!UseArena()) {
@ -549,6 +701,7 @@ TEST_P(RepeatedFieldProxyTest, EmplaceBackMessageRvalueDoesNotCopy) {
proxy.emplace_back(std::move(*msg1));
EXPECT_EQ(proxy[0].mutable_nested(), nested);
EXPECT_THAT(proxy, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
EXPECT_THAT(*field, ElementsAre(EqualsProto(R"pb(nested: {})pb")));
if (!UseArena()) {
@ -602,10 +755,15 @@ void TestEmplaceBackVanillaString(
}
if constexpr (std::is_same_v<StringType, std::string>) {
EXPECT_THAT(proxy, ElementsAre("", "1", "1", "2", "3", "4", kLongString,
kLongString, "aaaaaaaaaa", "5", "60", "3456",
"123"));
EXPECT_THAT(*field, ElementsAre("", "1", "1", "2", "3", "4", kLongString,
kLongString, "aaaaaaaaaa", "5", "60",
"3456", "123"));
} else {
EXPECT_THAT(proxy, ElementsAre("", "1", "1", "2", "3", "4", kLongString,
kLongString));
EXPECT_THAT(*field, ElementsAre("", "1", "1", "2", "3", "4", kLongString,
kLongString));
}
@ -639,9 +797,207 @@ TEST_P(RepeatedFieldProxyTest, EmplaceBackCord) {
absl::Cord cord4 = absl::Cord(kLongString);
proxy.emplace_back(cord4);
EXPECT_THAT(proxy, ElementsAre("1", "2", kLongString, kLongString));
EXPECT_THAT(*field, ElementsAre("1", "2", kLongString, kLongString));
}
TEST_P(RepeatedFieldProxyTest, IntIterators) {
auto field = MakeRepeatedFieldContainer<uint32_t>();
RepeatedFieldProxy<uint32_t> proxy = field.MakeProxy();
proxy.push_back(1);
proxy.push_back(2);
proxy.push_back(3);
auto it = proxy.begin();
EXPECT_EQ(*it, 1);
EXPECT_EQ(*(++it), 2);
EXPECT_EQ(*(++it), 3);
EXPECT_EQ(++it, proxy.end());
// Post-increment
it = proxy.begin();
EXPECT_EQ(*(it++), 1);
// Pre-decrement
it = proxy.end();
EXPECT_EQ(*(--it), 3);
// Post-decrement
EXPECT_EQ(*(it--), 3);
EXPECT_EQ(*it, 2);
// Equality
EXPECT_TRUE(proxy.begin() == proxy.begin());
EXPECT_TRUE(proxy.end() == proxy.end());
EXPECT_FALSE(proxy.begin() == proxy.end());
// Inequality
EXPECT_FALSE(proxy.begin() != proxy.begin());
EXPECT_FALSE(proxy.end() != proxy.end());
EXPECT_TRUE(proxy.begin() != proxy.end());
// Random access
it = proxy.begin();
EXPECT_EQ(*(it + 0), 1);
EXPECT_EQ(*(it + 1), 2);
EXPECT_EQ(*(it + 2), 3);
// Add assign
it += 2;
EXPECT_EQ(*(it - 1), 2);
// Subtract assign
it -= 1;
EXPECT_EQ(*it, 2);
// Indexing
EXPECT_EQ(it[1], 3);
// Reverse iterators
auto rit = proxy.rbegin();
EXPECT_EQ(*rit, 3);
EXPECT_EQ(*(++rit), 2);
EXPECT_EQ(*(++rit), 1);
EXPECT_EQ(++rit, proxy.rend());
}
template <typename StringType>
void TestStringIterators(google::protobuf::RepeatedFieldProxy<StringType> proxy) {
proxy.push_back("1");
proxy.push_back("2");
proxy.push_back("3");
auto it = proxy.begin();
EXPECT_THAT(*it, StringEq("1"));
EXPECT_THAT(*(++it), StringEq("2"));
EXPECT_THAT(*(++it), StringEq("3"));
EXPECT_EQ(++it, proxy.end());
// Post-increment
it = proxy.begin();
EXPECT_THAT(*(it++), StringEq("1"));
// Pre-decrement
it = proxy.end();
EXPECT_THAT(*(--it), StringEq("3"));
// Post-decrement
EXPECT_THAT(*(it--), StringEq("3"));
EXPECT_THAT(*it, StringEq("2"));
// Equality
EXPECT_TRUE(proxy.begin() == proxy.begin());
EXPECT_TRUE(proxy.end() == proxy.end());
EXPECT_FALSE(proxy.begin() == proxy.end());
// Inequality
EXPECT_FALSE(proxy.begin() != proxy.begin());
EXPECT_FALSE(proxy.end() != proxy.end());
EXPECT_TRUE(proxy.begin() != proxy.end());
// Random access
it = proxy.begin();
EXPECT_THAT(*(it + 0), StringEq("1"));
EXPECT_THAT(*(it + 1), StringEq("2"));
EXPECT_THAT(*(it + 2), StringEq("3"));
// Add assign
it += 2;
EXPECT_THAT(*(it - 1), StringEq("2"));
// Subtract assign
it -= 1;
EXPECT_THAT(*it, StringEq("2"));
// Indexing
EXPECT_THAT(it[1], StringEq("3"));
// Reverse iterators
auto rit = proxy.rbegin();
EXPECT_THAT(*rit, StringEq("3"));
EXPECT_THAT(*(++rit), StringEq("2"));
EXPECT_THAT(*(++rit), StringEq("1"));
EXPECT_EQ(++rit, proxy.rend());
// Const iterators
auto cit = proxy.cbegin();
EXPECT_THAT(*cit, StringEq("1"));
EXPECT_THAT(*(++cit), StringEq("2"));
EXPECT_THAT(*(++cit), StringEq("3"));
EXPECT_EQ(++cit, proxy.cend());
}
TEST_P(RepeatedFieldProxyTest, StdStringIterators) {
auto field = MakeRepeatedFieldContainer<std::string>();
TestStringIterators(field.MakeProxy());
}
TEST_P(RepeatedFieldProxyTest, StringViewIterators) {
auto field = MakeRepeatedFieldContainer<absl::string_view>();
TestStringIterators(field.MakeProxy());
}
TEST_P(RepeatedFieldProxyTest, CordIterators) {
auto field = MakeRepeatedFieldContainer<absl::Cord>();
TestStringIterators(field.MakeProxy());
}
TEST_P(RepeatedFieldProxyTest, IteratorMutation) {
auto field = MakeRepeatedFieldContainer<uint32_t>();
RepeatedFieldProxy<uint32_t> proxy = field.MakeProxy();
proxy.push_back(1);
proxy.push_back(2);
proxy.push_back(3);
auto it = proxy.begin();
*it = 4;
*(++it) = 5;
EXPECT_THAT(proxy, ElementsAre(4, 5, 3));
EXPECT_THAT(*field, ElementsAre(4, 5, 3));
auto rit = proxy.rbegin();
*rit = 6;
*(++rit) = 7;
EXPECT_THAT(proxy, ElementsAre(4, 7, 6));
EXPECT_THAT(*field, ElementsAre(4, 7, 6));
}
TEST_P(RepeatedFieldProxyTest, ConstIterators) {
auto field = MakeRepeatedFieldContainer<uint32_t>();
field->Add(1);
field->Add(2);
field->Add(3);
RepeatedFieldProxy<const uint32_t> proxy = field.MakeConstProxy();
auto it = proxy.cbegin();
EXPECT_EQ(*it, 1);
EXPECT_EQ(*(++it), 2);
EXPECT_EQ(*(++it), 3);
EXPECT_EQ(++it, proxy.cend());
auto rit = proxy.rbegin();
EXPECT_EQ(*rit, 3);
EXPECT_EQ(*(++rit), 2);
EXPECT_EQ(*(++rit), 1);
EXPECT_EQ(++rit, proxy.rend());
}
TEST_P(RepeatedFieldProxyTest, Rebind) {
auto field1 = MakeRepeatedFieldContainer<int32_t>();
field1->Add(1);
auto field2 = MakeRepeatedFieldContainer<int32_t>();
field2->Add(2);
RepeatedFieldProxy<int32_t> proxy = field1.MakeProxy();
proxy = field2.MakeProxy();
// Proxy should be rebound to field2 without having modified either field.
EXPECT_THAT(proxy, ElementsAre(2));
EXPECT_THAT(*field1, ElementsAre(1));
EXPECT_THAT(*field2, ElementsAre(2));
}
INSTANTIATE_TEST_SUITE_P(RepeatedFieldProxyTest, RepeatedFieldProxyTest,
testing::Bool(),
[](const testing::TestParamInfo<bool>& info) {