// Protocol Buffers - Google's data interchange format // Copyright 2023 Google LLC. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd #include "protos/repeated_field_iterator.h" #include #include #include #include #include #include #include #include #include using ::testing::ElementsAre; namespace protos { namespace internal { template using ScalarRef = ReferenceProxy>; template using ScalarIterator = Iterator>; template using StringRef = ReferenceProxy>; template using StringIterator = Iterator>; struct IteratorTestPeer { template static ScalarRef MakeScalarRefProxy(T& ref) { return ScalarRef({&ref}); } template static ScalarIterator MakeScalarIterator(T* ptr) { return ScalarIterator({ptr}); } template static StringRef MakeStringRefProxy(upb_Array* arr, protos::Arena& arena) { return StringRef({arr, arena.ptr(), 0}); } template static StringIterator MakeStringIterator(upb_Array* arr, protos::Arena& arena) { return StringIterator({arr, arena.ptr()}); } }; namespace { TEST(ScalarReferenceTest, BasicOperationsWork) { int i = 0; ScalarRef p = IteratorTestPeer::MakeScalarRefProxy(i); ScalarRef cp = IteratorTestPeer::MakeScalarRefProxy(std::as_const(i)); EXPECT_EQ(i, 0); p = 17; EXPECT_EQ(i, 17); EXPECT_EQ(p, 17); EXPECT_EQ(cp, 17); i = 13; EXPECT_EQ(p, 13); EXPECT_EQ(cp, 13); EXPECT_FALSE((std::is_assignable::value)); // Check that implicit conversion works T -> const T ScalarRef cp2 = p; EXPECT_EQ(cp2, 13); EXPECT_FALSE((std::is_convertible>::value)); } TEST(ScalarReferenceTest, AssignmentAndSwap) { int i = 3; int j = 5; ScalarRef p = IteratorTestPeer::MakeScalarRefProxy(i); ScalarRef p2 = IteratorTestPeer::MakeScalarRefProxy(j); EXPECT_EQ(p, 3); EXPECT_EQ(p2, 5); swap(p, p2); EXPECT_EQ(p, 5); EXPECT_EQ(p2, 3); p = p2; EXPECT_EQ(p, 3); EXPECT_EQ(p2, 3); } template std::array RunCompares(const T& a, const U& b) { // Verify some basic properties here. // Equivalencies EXPECT_EQ((a == b), (b == a)); EXPECT_EQ((a != b), (b != a)); EXPECT_EQ((a < b), (b > a)); EXPECT_EQ((a > b), (b < a)); EXPECT_EQ((a <= b), (b >= a)); EXPECT_EQ((a >= b), (b <= a)); // Opposites EXPECT_NE((a == b), (a != b)); EXPECT_NE((a < b), (a >= b)); EXPECT_NE((a > b), (a <= b)); return {{ (a == b), (a != b), (a < b), (a <= b), (a > b), (a >= b), }}; } template void TestScalarIterator(T* array) { ScalarIterator it = IteratorTestPeer::MakeScalarIterator(array); // Copy auto it2 = it; EXPECT_THAT(RunCompares(it, it2), ElementsAre(true, false, false, true, false, true)); // Increment EXPECT_EQ(*++it, 11); EXPECT_EQ(*it2, 10); EXPECT_EQ(*it++, 11); EXPECT_EQ(*it2, 10); EXPECT_EQ(*it, 12); EXPECT_EQ(*it2, 10); EXPECT_THAT(RunCompares(it, it2), ElementsAre(false, true, false, false, true, true)); // Assign it2 = it; EXPECT_EQ(*it, 12); EXPECT_EQ(*it2, 12); // Decrement EXPECT_EQ(*--it, 11); EXPECT_EQ(*it--, 11); EXPECT_EQ(*it, 10); it += 5; EXPECT_EQ(*it, 15); EXPECT_EQ(it - it2, 3); EXPECT_EQ(it2 - it, -3); it -= 3; EXPECT_EQ(*it, 12); EXPECT_EQ(it[6], 18); EXPECT_EQ(it[-1], 11); } TEST(ScalarIteratorTest, BasicOperationsWork) { int array[10] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; TestScalarIterator(array); TestScalarIterator(array); } TEST(ScalarIteratorTest, Convertibility) { int array[10] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; ScalarIterator it = IteratorTestPeer::MakeScalarIterator(array); it += 4; ScalarIterator cit = it; EXPECT_EQ(*it, 14); EXPECT_EQ(*cit, 14); it += 2; EXPECT_EQ(*it, 16); EXPECT_EQ(*cit, 14); cit = it; EXPECT_EQ(*it, 16); EXPECT_EQ(*cit, 16); EXPECT_FALSE((std::is_convertible, ScalarIterator>::value)); EXPECT_FALSE((std::is_assignable, ScalarIterator>::value)); } TEST(ScalarIteratorTest, MutabilityOnlyWorksOnMutable) { int array[10] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; ScalarIterator it = IteratorTestPeer::MakeScalarIterator(array); EXPECT_EQ(array[3], 13); it[3] = 113; EXPECT_EQ(array[3], 113); ScalarIterator cit = it; EXPECT_FALSE((std::is_assignable::value)); EXPECT_FALSE((std::is_assignable::value)); } TEST(ScalarIteratorTest, IteratorReferenceInteraction) { int array[10] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; ScalarIterator it = IteratorTestPeer::MakeScalarIterator(array); EXPECT_EQ(it[4], 14); // op& from references goes back to iterator. ScalarIterator it2 = &it[4]; EXPECT_EQ(it + 4, it2); } TEST(ScalarIteratorTest, IteratorBasedAlgorithmsWork) { // We use a vector here to make testing it easier. std::vector v(10, 0); ScalarIterator it = IteratorTestPeer::MakeScalarIterator(v.data()); EXPECT_THAT(v, ElementsAre(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); std::iota(it, it + 10, 10); EXPECT_THAT(v, ElementsAre(10, 11, 12, 13, 14, 15, 16, 17, 18, 19)); EXPECT_EQ(it + 5, std::find(it, it + 10, 15)); EXPECT_EQ(145, std::accumulate(it, it + 10, 0)); std::sort(it, it + 10, [](int a, int b) { return std::tuple(a % 2, a) < std::tuple(b % 2, b); }); EXPECT_THAT(v, ElementsAre(10, 12, 14, 16, 18, 11, 13, 15, 17, 19)); } const char* CloneString(protos::Arena& arena, absl::string_view str) { char* data = (char*)upb_Arena_Malloc(arena.ptr(), str.size()); memcpy(data, str.data(), str.size()); return data; } upb_Array* MakeStringArray(protos::Arena& arena, const std::vector& input) { upb_Array* arr = upb_Array_New(arena.ptr(), kUpb_CType_String); for (absl::string_view str : input) { upb_MessageValue message_value; message_value.str_val = upb_StringView_FromDataAndSize(CloneString(arena, str), str.size()); upb_Array_Append(arr, message_value, arena.ptr()); } return arr; } TEST(StringReferenceTest, BasicOperationsWork) { protos::Arena arena; upb_Array* arr = MakeStringArray(arena, {""}); auto read = [&] { upb_MessageValue message_value = upb_Array_Get(arr, 0); return absl::string_view(message_value.str_val.data, message_value.str_val.size); }; StringRef p = IteratorTestPeer::MakeStringRefProxy(arr, arena); StringRef cp = IteratorTestPeer::MakeStringRefProxy(arr, arena); EXPECT_EQ(read(), ""); EXPECT_EQ(p, ""); p = "ABC"; EXPECT_EQ(read(), "ABC"); EXPECT_EQ(p, "ABC"); EXPECT_EQ(cp, "ABC"); const_cast(read().data())[0] = 'X'; EXPECT_EQ(read(), "XBC"); EXPECT_EQ(p, "XBC"); EXPECT_EQ(cp, "XBC"); EXPECT_FALSE((std::is_assignable::value)); // Check that implicit conversion works T -> const T StringRef cp2 = p; EXPECT_EQ(cp2, "XBC"); EXPECT_FALSE( (std::is_convertible>::value)); EXPECT_THAT(RunCompares(p, "XBC"), ElementsAre(true, false, false, true, false, true)); EXPECT_THAT(RunCompares(p, "YBC"), ElementsAre(false, true, true, true, false, false)); EXPECT_THAT(RunCompares(p, "RBC"), ElementsAre(false, true, false, false, true, true)); EXPECT_THAT(RunCompares(p, "XB"), ElementsAre(false, true, false, false, true, true)); EXPECT_THAT(RunCompares(p, "XBCD"), ElementsAre(false, true, true, true, false, false)); } TEST(StringReferenceTest, AssignmentAndSwap) { protos::Arena arena; upb_Array* arr1 = MakeStringArray(arena, {"ABC"}); upb_Array* arr2 = MakeStringArray(arena, {"DEF"}); auto p = IteratorTestPeer::MakeStringRefProxy(arr1, arena); auto p2 = IteratorTestPeer::MakeStringRefProxy(arr2, arena); EXPECT_EQ(p, "ABC"); EXPECT_EQ(p2, "DEF"); swap(p, p2); EXPECT_EQ(p, "DEF"); EXPECT_EQ(p2, "ABC"); p = p2; EXPECT_EQ(p, "ABC"); EXPECT_EQ(p2, "ABC"); } template void TestStringIterator(protos::Arena& arena, upb_Array* array) { StringIterator it = IteratorTestPeer::MakeStringIterator(array, arena); // Copy auto it2 = it; EXPECT_THAT(RunCompares(it, it2), ElementsAre(true, false, false, true, false, true)); // Increment EXPECT_EQ(*++it, "11"); EXPECT_EQ(*it2, "10"); EXPECT_EQ(*it++, "11"); EXPECT_EQ(*it2, "10"); EXPECT_EQ(*it, "12"); EXPECT_EQ(*it2, "10"); EXPECT_THAT(RunCompares(it, it2), ElementsAre(false, true, false, false, true, true)); // Assign it2 = it; EXPECT_EQ(*it, "12"); EXPECT_EQ(*it2, "12"); // Decrement EXPECT_EQ(*--it, "11"); EXPECT_EQ(*it--, "11"); EXPECT_EQ(*it, "10"); it += 5; EXPECT_EQ(*it, "15"); EXPECT_EQ(it - it2, 3); EXPECT_EQ(it2 - it, -3); it -= 3; EXPECT_EQ(*it, "12"); EXPECT_EQ(it[6], "18"); EXPECT_EQ(it[-1], "11"); } TEST(StringIteratorTest, BasicOperationsWork) { protos::Arena arena; auto* array = MakeStringArray( arena, {"10", "11", "12", "13", "14", "15", "16", "17", "18", "19"}); TestStringIterator(arena, array); TestStringIterator(arena, array); } TEST(StringIteratorTest, Convertibility) { protos::Arena arena; auto* array = MakeStringArray( arena, {"10", "11", "12", "13", "14", "15", "16", "17", "18", "19"}); StringIterator it = IteratorTestPeer::MakeStringIterator(array, arena); it += 4; StringIterator cit = it; EXPECT_EQ(*it, "14"); EXPECT_EQ(*cit, "14"); it += 2; EXPECT_EQ(*it, "16"); EXPECT_EQ(*cit, "14"); cit = it; EXPECT_EQ(*it, "16"); EXPECT_EQ(*cit, "16"); EXPECT_FALSE((std::is_convertible, StringIterator>::value)); EXPECT_FALSE( (std::is_assignable, StringIterator>::value)); } TEST(StringIteratorTest, MutabilityOnlyWorksOnMutable) { protos::Arena arena; auto* array = MakeStringArray( arena, {"10", "11", "12", "13", "14", "15", "16", "17", "18", "19"}); StringIterator it = IteratorTestPeer::MakeStringIterator(array, arena); auto read = [&] { upb_MessageValue message_value = upb_Array_Get(array, 3); return absl::string_view(message_value.str_val.data, message_value.str_val.size); }; EXPECT_EQ(read(), "13"); it[3] = "113"; EXPECT_EQ(read(), "113"); StringIterator cit = it; EXPECT_FALSE((std::is_assignable::value)); EXPECT_FALSE( (std::is_assignable::value)); } TEST(StringIteratorTest, IteratorReferenceInteraction) { protos::Arena arena; auto* array = MakeStringArray( arena, {"10", "11", "12", "13", "14", "15", "16", "17", "18", "19"}); StringIterator it = IteratorTestPeer::MakeStringIterator(array, arena); EXPECT_EQ(it[4], "14"); // op& from references goes back to iterator. StringIterator it2 = &it[4]; EXPECT_EQ(it + 4, it2); } TEST(StringIteratorTest, IteratorBasedAlgorithmsWork) { protos::Arena arena; auto* array = MakeStringArray( arena, {"10", "11", "12", "13", "14", "15", "16", "17", "18", "19"}); StringIterator it = IteratorTestPeer::MakeStringIterator(array, arena); auto read = [&] { std::vector v; for (int i = 0; i < 10; ++i) { upb_MessageValue message_value = upb_Array_Get(array, i); v.emplace_back(message_value.str_val.data, message_value.str_val.size); } return v; }; EXPECT_THAT(read(), ElementsAre("10", "11", "12", "13", "14", // "15", "16", "17", "18", "19")); std::sort(it, it + 10, [](absl::string_view a, absl::string_view b) { return std::tuple(a[1] % 2, a) < std::tuple(b[1] % 2, b); }); EXPECT_THAT(read(), ElementsAre("10", "12", "14", "16", "18", // "11", "13", "15", "17", "19")); // Now sort with the default less. std::sort(it, it + 10); EXPECT_THAT(read(), ElementsAre("10", "11", "12", "13", "14", // "15", "16", "17", "18", "19")); // Mutable algorithm std::generate(it, it + 10, [i = 0]() mutable { return std::string(i++, 'x'); }); EXPECT_THAT(read(), ElementsAre("", "x", "xx", "xxx", "xxxx", "xxxxx", "xxxxxx", "xxxxxxx", "xxxxxxxx", "xxxxxxxxx")); } } // namespace } // namespace internal } // namespace protos