| // 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 "upb/message/map.h" |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <string> |
| |
| #include <gtest/gtest.h> |
| #include "upb/base/descriptor_constants.h" |
| #include "upb/base/string_view.h" |
| #include "upb/mem/arena.h" |
| #include "upb/mem/arena.hpp" |
| #include "upb/message/array.h" |
| #include "upb/message/internal/map.h" |
| |
| TEST(MapTest, DeleteRegression) { |
| upb::Arena arena; |
| upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_String); |
| ASSERT_NE(map, nullptr); |
| |
| upb_MessageValue key; |
| key.int32_val = 0; |
| |
| upb_MessageValue insert_value; |
| insert_value.str_val = upb_StringView_FromString("abcde"); |
| |
| upb_MapInsertStatus st = upb_Map_Insert(map, key, insert_value, arena.ptr()); |
| EXPECT_EQ(kUpb_MapInsertStatus_Inserted, st); |
| |
| upb_MessageValue delete_value; |
| bool removed = upb_Map_Delete(map, key, &delete_value); |
| EXPECT_TRUE(removed); |
| |
| EXPECT_TRUE( |
| upb_StringView_IsEqual(insert_value.str_val, delete_value.str_val)); |
| } |
| |
| TEST(MapTest, DeleteMissingKeyStringValue) { |
| upb::Arena arena; |
| upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_String); |
| ASSERT_NE(map, nullptr); |
| |
| upb_MessageValue key; |
| key.int32_val = 42; |
| |
| upb_MessageValue delete_value; |
| bool removed = upb_Map_Delete(map, key, &delete_value); |
| EXPECT_FALSE(removed); |
| } |
| |
| TEST(MapTest, ReserveEmptyMap) { |
| upb::Arena arena; |
| upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_Int32); |
| ASSERT_NE(map, nullptr); |
| EXPECT_EQ(_upb_Map_Capacity(map), 0); |
| |
| EXPECT_TRUE(upb_Map_Reserve(map, 20, arena.ptr())); |
| EXPECT_EQ(_upb_Map_Capacity(map), 32); |
| |
| for (int32_t i = 1; i <= 20; i++) { |
| upb_MessageValue k, v; |
| k.int32_val = i; |
| v.int32_val = i * 10; |
| EXPECT_EQ(kUpb_MapInsertStatus_Inserted, |
| upb_Map_Insert(map, k, v, arena.ptr())); |
| EXPECT_EQ(_upb_Map_Capacity(map), 32); |
| } |
| EXPECT_EQ(upb_Map_Size(map), 20); |
| for (int32_t i = 1; i <= 20; i++) { |
| upb_MessageValue k; |
| k.int32_val = i; |
| upb_MessageValue got; |
| EXPECT_TRUE(upb_Map_Get(map, k, &got)); |
| EXPECT_EQ(got.int32_val, i * 10); |
| } |
| } |
| |
| TEST(MapTest, ReserveStringMap) { |
| upb::Arena arena; |
| upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_String, kUpb_CType_Int32); |
| ASSERT_NE(map, nullptr); |
| EXPECT_EQ(_upb_Map_Capacity(map), 0); |
| |
| EXPECT_TRUE(upb_Map_Reserve(map, 50, arena.ptr())); |
| EXPECT_EQ(_upb_Map_Capacity(map), 64); |
| |
| for (int i = 0; i < 50; i++) { |
| std::string s = "key-" + std::to_string(i); |
| upb_MessageValue k, v; |
| k.str_val = upb_StringView_FromDataAndSize(s.data(), s.size()); |
| v.int32_val = i; |
| EXPECT_EQ(kUpb_MapInsertStatus_Inserted, |
| upb_Map_Insert(map, k, v, arena.ptr())); |
| EXPECT_EQ(_upb_Map_Capacity(map), 64); |
| } |
| EXPECT_EQ(upb_Map_Size(map), 50); |
| } |
| |
| TEST(MapTest, ReserveGrowExistingMap) { |
| upb::Arena arena; |
| upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_Int32); |
| ASSERT_NE(map, nullptr); |
| for (int32_t i = 1; i <= 3; i++) { |
| upb_MessageValue k, v; |
| k.int32_val = i; |
| v.int32_val = i * 10; |
| EXPECT_EQ(kUpb_MapInsertStatus_Inserted, |
| upb_Map_Insert(map, k, v, arena.ptr())); |
| } |
| EXPECT_EQ(_upb_Map_Capacity(map), 8); |
| |
| EXPECT_TRUE(upb_Map_Reserve(map, 50, arena.ptr())); |
| EXPECT_GE(_upb_Map_Capacity(map), 64); |
| EXPECT_EQ(upb_Map_Size(map), 3); |
| for (int32_t i = 1; i <= 3; i++) { |
| upb_MessageValue k; |
| k.int32_val = i; |
| upb_MessageValue got; |
| EXPECT_TRUE(upb_Map_Get(map, k, &got)); |
| EXPECT_EQ(got.int32_val, i * 10); |
| } |
| } |
| |
| namespace { |
| |
| // Walks `map` with the upb_MapIterator_* API used by the PHP, Ruby and elisp |
| // bindings. Returns the number of entries visited. Stops early (returning |
| // upb_Map_Size() + 1) if iteration fails to terminate. |
| size_t CountEntriesWithMapIterator(const upb_Map* map) { |
| size_t iter = kUpb_Map_Begin; |
| size_t n = 0; |
| while (upb_MapIterator_Next(map, &iter)) { |
| if (++n > upb_Map_Size(map)) break; |
| } |
| return n; |
| } |
| |
| // Walks `map` with upb_Map_Next(). Returns the number of entries visited. |
| // Stops early (returning upb_Map_Size() + 1) if iteration fails to terminate. |
| size_t CountEntriesWithMapNext(const upb_Map* map) { |
| size_t iter = kUpb_Map_Begin; |
| upb_MessageValue k, v; |
| size_t n = 0; |
| while (upb_Map_Next(map, &k, &v, &iter)) { |
| if (++n > upb_Map_Size(map)) break; |
| } |
| return n; |
| } |
| |
| void InsertInt32(upb_Map* map, int32_t key, int32_t val, upb_Arena* arena) { |
| upb_MessageValue k, v; |
| k.int32_val = key; |
| v.int32_val = val; |
| ASSERT_EQ(kUpb_MapInsertStatus_Inserted, upb_Map_Insert(map, k, v, arena)); |
| } |
| |
| void InsertString(upb_Map* map, const char* key, int32_t val, |
| upb_Arena* arena) { |
| upb_MessageValue k, v; |
| k.str_val = upb_StringView_FromString(key); |
| v.int32_val = val; |
| ASSERT_EQ(kUpb_MapInsertStatus_Inserted, upb_Map_Insert(map, k, v, arena)); |
| } |
| |
| } // namespace |
| |
| TEST(MapIteratorTest, NeverPopulatedIntMap) { |
| upb::Arena arena; |
| upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_Int32); |
| ASSERT_NE(map, nullptr); |
| EXPECT_EQ(CountEntriesWithMapIterator(map), 0); |
| EXPECT_EQ(CountEntriesWithMapNext(map), 0); |
| } |
| |
| TEST(MapIteratorTest, NeverPopulatedStringMap) { |
| upb::Arena arena; |
| upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_String, kUpb_CType_Int32); |
| ASSERT_NE(map, nullptr); |
| EXPECT_EQ(CountEntriesWithMapIterator(map), 0); |
| EXPECT_EQ(CountEntriesWithMapNext(map), 0); |
| } |
| |
| TEST(MapIteratorTest, ClearedMap) { |
| upb::Arena arena; |
| upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_Int32); |
| ASSERT_NE(map, nullptr); |
| for (int32_t i = 1; i <= 5; i++) InsertInt32(map, i, i, arena.ptr()); |
| upb_Map_Clear(map); |
| EXPECT_EQ(CountEntriesWithMapIterator(map), 0); |
| EXPECT_EQ(CountEntriesWithMapNext(map), 0); |
| } |
| |
| TEST(MapIteratorTest, AllEntriesDeleted) { |
| upb::Arena arena; |
| upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_String, kUpb_CType_Int32); |
| ASSERT_NE(map, nullptr); |
| InsertString(map, "a", 1, arena.ptr()); |
| InsertString(map, "b", 2, arena.ptr()); |
| upb_MessageValue k; |
| k.str_val = upb_StringView_FromString("a"); |
| EXPECT_TRUE(upb_Map_Delete(map, k, nullptr)); |
| k.str_val = upb_StringView_FromString("b"); |
| EXPECT_TRUE(upb_Map_Delete(map, k, nullptr)); |
| EXPECT_EQ(CountEntriesWithMapIterator(map), 0); |
| EXPECT_EQ(CountEntriesWithMapNext(map), 0); |
| } |
| |
| TEST(MapIteratorTest, PopulatedIntMapTerminates) { |
| upb::Arena arena; |
| upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_Int32); |
| ASSERT_NE(map, nullptr); |
| for (int32_t i = 1; i <= 20; i++) InsertInt32(map, i, i, arena.ptr()); |
| EXPECT_EQ(CountEntriesWithMapIterator(map), 20); |
| EXPECT_EQ(CountEntriesWithMapNext(map), 20); |
| } |
| |
| TEST(MapIteratorTest, PopulatedStringMapTerminates) { |
| upb::Arena arena; |
| upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_String, kUpb_CType_Int32); |
| ASSERT_NE(map, nullptr); |
| InsertString(map, "x", 1, arena.ptr()); |
| InsertString(map, "y", 2, arena.ptr()); |
| InsertString(map, "z", 3, arena.ptr()); |
| EXPECT_EQ(CountEntriesWithMapIterator(map), 3); |
| EXPECT_EQ(CountEntriesWithMapNext(map), 3); |
| } |
| |
| TEST(MapIteratorTest, ZeroKeyOnly) { |
| upb::Arena arena; |
| upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_Int32); |
| ASSERT_NE(map, nullptr); |
| InsertInt32(map, 0, 42, arena.ptr()); |
| EXPECT_EQ(CountEntriesWithMapIterator(map), 1); |
| EXPECT_EQ(CountEntriesWithMapNext(map), 1); |
| } |
| |
| TEST(MapIteratorTest, ZeroKeyAndOtherKeys) { |
| upb::Arena arena; |
| upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_Int32); |
| ASSERT_NE(map, nullptr); |
| for (int32_t i = 0; i < 10; i++) InsertInt32(map, i, i, arena.ptr()); |
| EXPECT_EQ(CountEntriesWithMapIterator(map), 10); |
| EXPECT_EQ(CountEntriesWithMapNext(map), 10); |
| } |