| // 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 | |
| // Tests for upb_table. | |
| #include <limits.h> | |
| #include <stdio.h> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <map> | |
| #include <set> | |
| #include <string> | |
| #include <vector> | |
| #include <gtest/gtest.h> | |
| #include "absl/container/flat_hash_map.h" | |
| #include "upb/base/string_view.h" | |
| #include "upb/hash/common.h" | |
| #include "upb/hash/ext_table.h" | |
| #include "upb/hash/int_table.h" | |
| #include "upb/hash/str_table.h" | |
| #include "upb/mem/arena.h" | |
| #include "upb/mem/arena.hpp" | |
| #include "upb/message/test.upb_minitable.h" | |
| #include "upb/mini_table/extension.h" | |
| #include "upb/mini_table/message.h" | |
| // Must be last. | |
| #include "upb/port/def.inc" | |
| using std::vector; | |
| TEST(Table, StringTable) { | |
| vector<std::string> keys; | |
| keys.push_back("google.protobuf.FileDescriptorSet"); | |
| keys.push_back("google.protobuf.FileDescriptorProto"); | |
| keys.push_back("google.protobuf.DescriptorProto"); | |
| keys.push_back("google.protobuf.DescriptorProto.ExtensionRange"); | |
| keys.push_back("google.protobuf.FieldDescriptorProto"); | |
| keys.push_back("google.protobuf.EnumDescriptorProto"); | |
| keys.push_back("google.protobuf.EnumValueDescriptorProto"); | |
| keys.push_back("google.protobuf.ServiceDescriptorProto"); | |
| keys.push_back("google.protobuf.MethodDescriptorProto"); | |
| keys.push_back("google.protobuf.FileOptions"); | |
| keys.push_back("google.protobuf.MessageOptions"); | |
| keys.push_back("google.protobuf.FieldOptions"); | |
| keys.push_back("google.protobuf.EnumOptions"); | |
| keys.push_back("google.protobuf.EnumValueOptions"); | |
| keys.push_back("google.protobuf.ServiceOptions"); | |
| keys.push_back("google.protobuf.MethodOptions"); | |
| keys.push_back("google.protobuf.UninterpretedOption"); | |
| keys.push_back("google.protobuf.UninterpretedOption.NamePart"); | |
| /* Initialize structures. */ | |
| upb::Arena arena; | |
| upb_strtable t; | |
| EXPECT_TRUE(upb_strtable_init(&t, keys.size(), arena.ptr())); | |
| std::map<std::string, int32_t> m; | |
| std::set<std::string> all; | |
| for (const auto& key : keys) { | |
| all.insert(key); | |
| upb_value val = {uint64_t(key[0])}; | |
| EXPECT_TRUE( | |
| upb_strtable_insert(&t, key.data(), key.size(), val, arena.ptr())); | |
| m[key] = key[0]; | |
| } | |
| /* Test correctness. */ | |
| for (const auto& key : keys) { | |
| upb_value val; | |
| bool ok = upb_strtable_lookup2(&t, key.data(), key.size(), &val); | |
| EXPECT_TRUE(ok); | |
| EXPECT_EQ(val.val, uint64_t(key[0])); | |
| EXPECT_EQ(m[key], key[0]); | |
| } | |
| intptr_t iter = UPB_STRTABLE_BEGIN; | |
| upb_StringView key; | |
| upb_value val; | |
| while (upb_strtable_next2(&t, &key, &val, &iter)) { | |
| std::set<std::string>::iterator i = all.find(key.data); | |
| EXPECT_NE(i, all.end()); | |
| all.erase(i); | |
| } | |
| EXPECT_TRUE(all.empty()); | |
| // Test iteration with resizes. | |
| for (int i = 0; i < 10; i++) { | |
| intptr_t iter = UPB_STRTABLE_BEGIN; | |
| while (upb_strtable_next2(&t, &key, &val, &iter)) { | |
| // Even if we invalidate the iterator it should only return real elements. | |
| EXPECT_EQ(val.val, m[key.data]); | |
| // Force a resize even though the size isn't changing. | |
| // Also forces the table size to grow so some new buckets end up empty. | |
| bool ok = upb_strtable_resize(&t, 5 + i, arena.ptr()); | |
| EXPECT_TRUE(ok); | |
| } | |
| } | |
| } | |
| class IntTableTest : public testing::TestWithParam<int> { | |
| void SetUp() override { | |
| if (GetParam() > 0) { | |
| for (int i = 0; i < GetParam(); i++) { | |
| keys_.push_back(i + 1); | |
| } | |
| } else { | |
| for (int32_t i = 0; i < 64; i++) { | |
| if (i < 32) | |
| keys_.push_back(i + 1); | |
| else | |
| keys_.push_back(10101 + i); | |
| } | |
| } | |
| } | |
| protected: | |
| std::vector<int32_t> keys_; | |
| }; | |
| TEST_P(IntTableTest, TestIntTable) { | |
| /* Initialize structures. */ | |
| upb::Arena arena; | |
| upb_inttable t; | |
| EXPECT_TRUE(upb_inttable_init(&t, arena.ptr())); | |
| uint32_t largest_key = 0; | |
| std::map<uint32_t, uint32_t> m; | |
| absl::flat_hash_map<uint32_t, uint32_t> hm; | |
| for (const auto& key : keys_) { | |
| largest_key = UPB_MAX((int32_t)largest_key, key); | |
| upb_value val = upb_value_uint32(key * 2); | |
| bool ok = upb_inttable_insert(&t, key, val, arena.ptr()); | |
| EXPECT_TRUE(ok); | |
| m[key] = key * 2; | |
| hm[key] = key * 2; | |
| } | |
| EXPECT_EQ(upb_inttable_count(&t), keys_.size()); | |
| /* Test correctness. */ | |
| int count = 0; | |
| for (uint32_t i = 0; i <= largest_key; i++) { | |
| upb_value val; | |
| bool ok = upb_inttable_lookup(&t, i, &val); | |
| if (ok) { /* Assume map implementation is correct. */ | |
| EXPECT_EQ(val.val, i * 2); | |
| EXPECT_EQ(m[i], i * 2); | |
| EXPECT_EQ(hm[i], i * 2); | |
| count++; | |
| } | |
| } | |
| EXPECT_EQ(count, keys_.size()); | |
| EXPECT_EQ(count, upb_inttable_count(&t)); | |
| // Test replace. | |
| count = 0; | |
| for (uint32_t i = 0; i <= largest_key; i++) { | |
| upb_value val = upb_value_uint32(i * 3); | |
| bool ok = upb_inttable_replace(&t, i, val); | |
| if (ok) { /* Assume map implementation is correct. */ | |
| m[i] = i * 3; | |
| hm[i] = i * 3; | |
| count++; | |
| } | |
| } | |
| EXPECT_EQ(count, keys_.size()); | |
| EXPECT_EQ(count, upb_inttable_count(&t)); | |
| for (const auto& key : keys_) { | |
| upb_value val; | |
| bool ok = upb_inttable_remove(&t, key, &val); | |
| EXPECT_TRUE(ok); | |
| EXPECT_EQ(val.val, (uint32_t)key * 3); | |
| count--; | |
| EXPECT_EQ(count, upb_inttable_count(&t)); | |
| } | |
| EXPECT_EQ(0, upb_inttable_count(&t)); | |
| upb_inttable_clear(&t); | |
| } | |
| TEST(TableTest, ExtTable) { | |
| upb_Arena* a = upb_Arena_New(); | |
| upb_exttable table; | |
| ASSERT_TRUE(upb_exttable_init(&table, 4, a)); | |
| const upb_MiniTable* mt1 = &upb_0test__TestExtensions_msg_init; | |
| const upb_MiniTable* mt2 = &upb_0test__TestMessageSet_msg_init; | |
| const upb_MiniTableExtension* ext1_10 = | |
| upb_test_TestExtensions_optional_int32_ext_ext; | |
| const upb_MiniTableExtension* ext1_20 = upb_test_optional_msg_ext_ext; | |
| const upb_MiniTableExtension* ext2_10 = | |
| upb_test_MessageSetMember_message_set_extension_ext; | |
| ASSERT_TRUE(upb_exttable_insert(&table, mt1, (const uint32_t*)ext1_10, a)); | |
| ASSERT_TRUE(upb_exttable_insert(&table, mt1, (const uint32_t*)ext1_20, a)); | |
| ASSERT_TRUE(upb_exttable_insert(&table, mt2, (const uint32_t*)ext2_10, a)); | |
| ASSERT_EQ(3, upb_exttable_count(&table)); | |
| const uint32_t* v; | |
| v = upb_exttable_lookup(&table, mt1, 1000); // optional_int32_ext | |
| ASSERT_NE(nullptr, v); | |
| ASSERT_EQ((const uint32_t*)ext1_10, v); | |
| v = upb_exttable_lookup(&table, mt1, 1002); // optional_msg_ext | |
| ASSERT_NE(nullptr, v); | |
| ASSERT_EQ((const uint32_t*)ext1_20, v); | |
| v = upb_exttable_lookup(&table, mt2, 2000); // message_set_extension | |
| ASSERT_NE(nullptr, v); | |
| ASSERT_EQ((const uint32_t*)ext2_10, v); | |
| ASSERT_EQ(nullptr, upb_exttable_lookup(&table, mt2, 20)); | |
| v = upb_exttable_remove(&table, mt1, 1000); | |
| ASSERT_NE(nullptr, v); | |
| ASSERT_EQ((const uint32_t*)ext1_10, v); | |
| ASSERT_EQ(2, upb_exttable_count(&table)); | |
| ASSERT_EQ(nullptr, upb_exttable_lookup(&table, mt1, 1000)); | |
| upb_Arena_Free(a); | |
| } | |
| TEST(IntTableTest, EmptyTable) { | |
| upb::Arena arena; | |
| upb_inttable t; | |
| EXPECT_TRUE(upb_inttable_init(&t, arena.ptr())); | |
| intptr_t iter = UPB_INTTABLE_BEGIN; | |
| uintptr_t key; | |
| upb_value val; | |
| EXPECT_FALSE(upb_inttable_next(&t, &key, &val, &iter)); | |
| EXPECT_TRUE(upb_inttable_done(&t, iter)); | |
| // Insert a value. | |
| EXPECT_TRUE(upb_inttable_insert(&t, 0, upb_value_bool(true), arena.ptr())); | |
| iter = UPB_INTTABLE_BEGIN; | |
| EXPECT_TRUE(upb_inttable_next(&t, &key, &val, &iter)); | |
| EXPECT_FALSE(upb_inttable_done(&t, iter)); | |
| // Clear the table. | |
| upb_inttable_clear(&t); | |
| iter = UPB_INTTABLE_BEGIN; | |
| EXPECT_FALSE(upb_inttable_next(&t, &key, &val, &iter)); | |
| EXPECT_TRUE(upb_inttable_done(&t, iter)); | |
| } | |
| TEST(IntTableTest, Iteration) { | |
| upb::Arena arena; | |
| upb_inttable t; | |
| EXPECT_TRUE(upb_inttable_init(&t, arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 0, upb_value_bool(true), arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 2, upb_value_bool(true), arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 4, upb_value_bool(true), arena.ptr())); | |
| intptr_t iter = UPB_INTTABLE_BEGIN; | |
| uintptr_t key; | |
| upb_value val; | |
| // First element. | |
| EXPECT_TRUE(upb_inttable_next(&t, &key, &val, &iter)); | |
| EXPECT_EQ(key, 0); | |
| EXPECT_EQ(upb_inttable_iter_key(&t, iter), 0); | |
| EXPECT_EQ(val.val, true); | |
| EXPECT_EQ(upb_inttable_iter_value(&t, iter).val, true); | |
| EXPECT_FALSE(upb_inttable_done(&t, iter)); | |
| // Second element. | |
| EXPECT_TRUE(upb_inttable_next(&t, &key, &val, &iter)); | |
| EXPECT_EQ(key, 2); | |
| EXPECT_EQ(upb_inttable_iter_key(&t, iter), 2); | |
| EXPECT_EQ(val.val, true); | |
| EXPECT_EQ(upb_inttable_iter_value(&t, iter).val, true); | |
| EXPECT_FALSE(upb_inttable_done(&t, iter)); | |
| // Third element. | |
| EXPECT_TRUE(upb_inttable_next(&t, &key, &val, &iter)); | |
| EXPECT_EQ(key, 4); | |
| EXPECT_EQ(upb_inttable_iter_key(&t, iter), 4); | |
| EXPECT_EQ(val.val, true); | |
| EXPECT_EQ(upb_inttable_iter_value(&t, iter).val, true); | |
| EXPECT_FALSE(upb_inttable_done(&t, iter)); | |
| // Update the third element. | |
| upb_inttable_setentryvalue(&t, iter, upb_value_bool(false)); | |
| EXPECT_EQ(upb_inttable_iter_value(&t, iter).val, false); | |
| EXPECT_FALSE(upb_inttable_done(&t, iter)); | |
| // Done with the iteration. | |
| EXPECT_FALSE(upb_inttable_next(&t, &key, &val, &iter)); | |
| EXPECT_TRUE(upb_inttable_done(&t, iter)); | |
| upb_inttable_clear(&t); | |
| } | |
| TEST(IntTableTest, IterationWithNonZeroStart) { | |
| upb::Arena arena; | |
| upb_inttable t; | |
| EXPECT_TRUE(upb_inttable_init(&t, arena.ptr())); | |
| upb_value val_for_key_2 = {uint64_t("value_for_key_2")}; | |
| upb_value val_for_key_4 = {uint64_t("value_for_key_4")}; | |
| EXPECT_TRUE(upb_inttable_insert(&t, 2, val_for_key_2, arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 4, val_for_key_4, arena.ptr())); | |
| intptr_t iter = UPB_INTTABLE_BEGIN; | |
| uintptr_t key; | |
| upb_value val; | |
| // First element. | |
| EXPECT_TRUE(upb_inttable_next(&t, &key, &val, &iter)); | |
| EXPECT_EQ(key, 2); | |
| EXPECT_EQ(upb_inttable_iter_key(&t, iter), 2); | |
| EXPECT_EQ(val.val, val_for_key_2.val); | |
| EXPECT_EQ(upb_inttable_iter_value(&t, iter).val, val_for_key_2.val); | |
| EXPECT_FALSE(upb_inttable_done(&t, iter)); | |
| // Second element. | |
| EXPECT_TRUE(upb_inttable_next(&t, &key, &val, &iter)); | |
| EXPECT_EQ(key, 4); | |
| EXPECT_EQ(upb_inttable_iter_key(&t, iter), 4); | |
| EXPECT_EQ(val.val, val_for_key_4.val); | |
| EXPECT_EQ(upb_inttable_iter_value(&t, iter).val, val_for_key_4.val); | |
| EXPECT_FALSE(upb_inttable_done(&t, iter)); | |
| // Done with the iteration. | |
| EXPECT_FALSE(upb_inttable_next(&t, &key, &val, &iter)); | |
| EXPECT_TRUE(upb_inttable_done(&t, iter)); | |
| upb_inttable_clear(&t); | |
| } | |
| TEST(IntTableTest, IterationWithArrayOnly) { | |
| upb::Arena arena; | |
| upb_inttable t; | |
| EXPECT_TRUE(upb_inttable_init(&t, arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 0, upb_value_bool(true), arena.ptr())); | |
| intptr_t iter = UPB_INTTABLE_BEGIN; | |
| uintptr_t key; | |
| upb_value val; | |
| // The only element. | |
| EXPECT_TRUE(upb_inttable_next(&t, &key, &val, &iter)); | |
| EXPECT_EQ(key, 0); | |
| EXPECT_EQ(upb_inttable_iter_key(&t, iter), 0); | |
| EXPECT_EQ(val.val, true); | |
| EXPECT_EQ(upb_inttable_iter_value(&t, iter).val, true); | |
| EXPECT_FALSE(upb_inttable_done(&t, iter)); | |
| // Done with the iteration. | |
| EXPECT_FALSE(upb_inttable_next(&t, &key, &val, &iter)); | |
| EXPECT_TRUE(upb_inttable_done(&t, iter)); | |
| upb_inttable_clear(&t); | |
| } | |
| TEST(IntTableTest, BoolKeys) { | |
| upb::Arena arena; | |
| upb_inttable t; | |
| EXPECT_TRUE(upb_inttable_init(&t, arena.ptr())); | |
| EXPECT_TRUE( | |
| upb_inttable_insert(&t, false, upb_value_bool(true), arena.ptr())); | |
| EXPECT_TRUE( | |
| upb_inttable_insert(&t, true, upb_value_bool(false), arena.ptr())); | |
| intptr_t iter = UPB_INTTABLE_BEGIN; | |
| uintptr_t key; | |
| upb_value val; | |
| // First element. | |
| EXPECT_TRUE(upb_inttable_next(&t, &key, &val, &iter)); | |
| bool key_bool; | |
| memcpy(&key_bool, &key, sizeof(key_bool)); | |
| EXPECT_EQ(key_bool, false); | |
| EXPECT_EQ(upb_inttable_iter_key(&t, iter), false); | |
| EXPECT_EQ(val.val, true); | |
| EXPECT_EQ(upb_inttable_iter_value(&t, iter).val, true); | |
| EXPECT_FALSE(upb_inttable_done(&t, iter)); | |
| // Second element. | |
| EXPECT_TRUE(upb_inttable_next(&t, &key, &val, &iter)); | |
| memcpy(&key_bool, &key, sizeof(key_bool)); | |
| EXPECT_EQ(key_bool, true); | |
| EXPECT_EQ(upb_inttable_iter_key(&t, iter), true); | |
| EXPECT_EQ(val.val, false); | |
| EXPECT_EQ(upb_inttable_iter_value(&t, iter).val, false); | |
| EXPECT_FALSE(upb_inttable_done(&t, iter)); | |
| // Done with the iteration. | |
| EXPECT_FALSE(upb_inttable_next(&t, &key, &val, &iter)); | |
| EXPECT_TRUE(upb_inttable_done(&t, iter)); | |
| upb_inttable_clear(&t); | |
| } | |
| TEST(IntTableTest, EnumValues) { | |
| upb::Arena arena; | |
| upb_inttable t; | |
| EXPECT_TRUE(upb_inttable_init(&t, arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 0, upb_value_int32(0), arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, -1, upb_value_int32(-1), arena.ptr())); | |
| intptr_t iter = UPB_INTTABLE_BEGIN; | |
| uintptr_t key; | |
| upb_value val; | |
| int32_t key_int32; | |
| // First element. | |
| EXPECT_TRUE(upb_inttable_next(&t, &key, &val, &iter)); | |
| memcpy(&key_int32, &key, sizeof(key_int32)); | |
| EXPECT_EQ(key_int32, 0); | |
| EXPECT_EQ(upb_inttable_iter_key(&t, iter), 0); | |
| EXPECT_EQ(val.val, 0); | |
| EXPECT_EQ(upb_inttable_iter_value(&t, iter).val, 0); | |
| EXPECT_FALSE(upb_inttable_done(&t, iter)); | |
| // Second element. | |
| EXPECT_TRUE(upb_inttable_next(&t, &key, &val, &iter)); | |
| memcpy(&key_int32, &key, sizeof(key_int32)); | |
| EXPECT_EQ(key_int32, -1); | |
| EXPECT_EQ(upb_inttable_iter_key(&t, iter), -1); | |
| EXPECT_EQ(val.val, -1); | |
| EXPECT_EQ(upb_inttable_iter_value(&t, iter).val, -1); | |
| EXPECT_FALSE(upb_inttable_done(&t, iter)); | |
| // Done with the iteration. | |
| EXPECT_FALSE(upb_inttable_next(&t, &key, &val, &iter)); | |
| EXPECT_TRUE(upb_inttable_done(&t, iter)); | |
| upb_inttable_clear(&t); | |
| } | |
| INSTANTIATE_TEST_SUITE_P(IntTableParams, IntTableTest, | |
| testing::Values(8, 64, 512, -32)); | |
| TEST(Table, MaxValue) { | |
| upb::Arena arena; | |
| upb_inttable t; | |
| EXPECT_TRUE(upb_inttable_init(&t, arena.ptr())); | |
| uint64_t uint64_max = (uint64_t)-1; | |
| EXPECT_TRUE( | |
| upb_inttable_insert(&t, 0, upb_value_uint64(uint64_max), arena.ptr())); | |
| EXPECT_TRUE( | |
| upb_inttable_insert(&t, 1, upb_value_uint64(uint64_max), arena.ptr())); | |
| EXPECT_TRUE( | |
| upb_inttable_insert(&t, 2, upb_value_uint64(uint64_max), arena.ptr())); | |
| upb_value val; | |
| EXPECT_TRUE(upb_inttable_lookup(&t, 0, &val)); | |
| EXPECT_EQ(val.val, uint64_max); | |
| EXPECT_TRUE(upb_inttable_lookup(&t, 1, &val)); | |
| EXPECT_EQ(val.val, uint64_max); | |
| EXPECT_TRUE(upb_inttable_lookup(&t, 2, &val)); | |
| EXPECT_EQ(val.val, uint64_max); | |
| upb_inttable_remove(&t, 0, nullptr); | |
| upb_inttable_remove(&t, 1, nullptr); | |
| upb_inttable_remove(&t, 2, nullptr); | |
| EXPECT_FALSE(upb_inttable_lookup(&t, 0, &val)); | |
| EXPECT_FALSE(upb_inttable_lookup(&t, 1, &val)); | |
| EXPECT_FALSE(upb_inttable_lookup(&t, 2, &val)); | |
| } | |
| TEST(Table, MaxValueWithLargeArray) { | |
| upb::Arena arena; | |
| upb_inttable t; | |
| EXPECT_TRUE(upb_inttable_init(&t, arena.ptr())); | |
| uint64_t uint64_max = (uint64_t)-1; | |
| for (int i = 1; i < 121; i++) { | |
| EXPECT_TRUE( | |
| upb_inttable_insert(&t, i, upb_value_uint64(uint64_max), arena.ptr())); | |
| } | |
| upb_value val; | |
| for (int i = 1; i < 121; i++) { | |
| EXPECT_TRUE(upb_inttable_lookup(&t, i, &val)); | |
| EXPECT_EQ(val.val, uint64_max); | |
| } | |
| for (int i = 1; i < 121; i++) { | |
| upb_inttable_remove(&t, i, nullptr); | |
| } | |
| for (int i = 1; i < 121; i++) { | |
| EXPECT_FALSE(upb_inttable_lookup(&t, i, &val)); | |
| } | |
| } | |
| TEST(IntTableTest, RemoveIter) { | |
| upb::Arena arena; | |
| upb_inttable t; | |
| EXPECT_TRUE(upb_inttable_init(&t, arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 0, upb_value_bool(true), arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 2, upb_value_bool(true), arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 4, upb_value_bool(true), arena.ptr())); | |
| intptr_t iter = UPB_INTTABLE_BEGIN; | |
| uintptr_t key; | |
| upb_value val; | |
| EXPECT_TRUE(upb_inttable_next(&t, &key, &val, &iter)); | |
| bool saw_0 = false; | |
| bool saw_2 = false; | |
| bool saw_4 = false; | |
| do { | |
| switch (key) { | |
| case 0: | |
| EXPECT_EQ(upb_inttable_iter_key(&t, iter), 0); | |
| upb_inttable_removeiter(&t, &iter); | |
| EXPECT_FALSE(saw_0); | |
| saw_0 = true; | |
| break; | |
| case 2: | |
| EXPECT_EQ(upb_inttable_iter_key(&t, iter), 2); | |
| EXPECT_FALSE(saw_2); | |
| saw_2 = true; | |
| break; | |
| case 4: | |
| EXPECT_EQ(upb_inttable_iter_key(&t, iter), 4); | |
| upb_inttable_removeiter(&t, &iter); | |
| EXPECT_FALSE(saw_4); | |
| saw_4 = true; | |
| break; | |
| default: | |
| FAIL() << "Unexpected key: " << key; | |
| } | |
| } while (upb_inttable_next(&t, &key, &val, &iter)); | |
| EXPECT_TRUE(saw_0); | |
| EXPECT_TRUE(saw_2); | |
| EXPECT_TRUE(saw_4); | |
| EXPECT_FALSE(upb_inttable_lookup(&t, 0, &val)); | |
| EXPECT_TRUE(upb_inttable_lookup(&t, 2, &val)); | |
| EXPECT_FALSE(upb_inttable_lookup(&t, 4, &val)); | |
| } | |
| TEST(IntTableTest, RemoveIterAll) { | |
| upb::Arena arena; | |
| upb_inttable t; | |
| EXPECT_TRUE(upb_inttable_init(&t, arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 0, upb_value_bool(true), arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 2, upb_value_bool(true), arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 4, upb_value_bool(true), arena.ptr())); | |
| intptr_t iter = UPB_INTTABLE_BEGIN; | |
| uintptr_t key; | |
| upb_value val; | |
| while (upb_inttable_next(&t, &key, &val, &iter)) { | |
| upb_inttable_removeiter(&t, &iter); | |
| } | |
| EXPECT_EQ(upb_inttable_count(&t), 0); | |
| } | |
| TEST(IntTableTest, Delete) { | |
| upb::Arena arena; | |
| upb_inttable t; | |
| EXPECT_TRUE(upb_inttable_init(&t, arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 0, upb_value_bool(true), arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 2, upb_value_bool(true), arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 4, upb_value_bool(true), arena.ptr())); | |
| upb_inttable_remove(&t, 0, nullptr); | |
| upb_inttable_remove(&t, 2, nullptr); | |
| upb_inttable_remove(&t, 4, nullptr); | |
| intptr_t iter = UPB_INTTABLE_BEGIN; | |
| uintptr_t key; | |
| upb_value val; | |
| while (upb_inttable_next(&t, &key, &val, &iter)) { | |
| FAIL(); | |
| } | |
| } | |
| TEST(Table, Init) { | |
| for (int i = 0; i < 2048; i++) { | |
| /* Tests that the size calculations in init() (lg2 size for target load) | |
| * work for all expected sizes. */ | |
| upb::Arena arena; | |
| upb_strtable t; | |
| EXPECT_TRUE(upb_strtable_init(&t, i, arena.ptr())); | |
| } | |
| } | |
| TEST(IntTableTest, RemoveIterHeadOfChain) { | |
| upb::Arena arena; | |
| upb_inttable t; | |
| EXPECT_TRUE(upb_inttable_init(&t, arena.ptr())); | |
| // Insert two keys that collide on the initial table size of 8 (mask 7). | |
| // This will form a chain where 0 is at the head and 8 is its next element. | |
| EXPECT_TRUE(upb_inttable_insert(&t, 0, upb_value_bool(true), arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 8, upb_value_bool(true), arena.ptr())); | |
| intptr_t iter = UPB_INTTABLE_BEGIN; | |
| uintptr_t key; | |
| upb_value val; | |
| // Iterate through the table. When we encounter 0, remove it via removeiter. | |
| while (upb_inttable_next(&t, &key, &val, &iter)) { | |
| if (key == 0) { | |
| upb_inttable_removeiter(&t, &iter); | |
| } | |
| } | |
| // The remaining element 8 should still be in the table and reachable. | |
| EXPECT_EQ(upb_inttable_count(&t), 1); | |
| EXPECT_TRUE(upb_inttable_lookup(&t, 8, &val)); | |
| } | |
| TEST(IntTableTest, RemoveIterHeadOfChainDemonstratesBug) { | |
| upb::Arena arena; | |
| upb_inttable t; | |
| EXPECT_TRUE(upb_inttable_init(&t, arena.ptr())); | |
| // Insert two keys that collide on the initial table size of 8 (mask 7). | |
| // Keys 1 and 9 are used so that they fall into the hash part of the table, | |
| // forming a chain where 1 is at the head and 9 is its next element. | |
| EXPECT_TRUE(upb_inttable_insert(&t, 1, upb_value_bool(true), arena.ptr())); | |
| EXPECT_TRUE(upb_inttable_insert(&t, 9, upb_value_bool(true), arena.ptr())); | |
| intptr_t iter = UPB_INTTABLE_BEGIN; | |
| uintptr_t key; | |
| upb_value val; | |
| // Iterate through the table. When we encounter 1, remove it via removeiter. | |
| while (upb_inttable_next(&t, &key, &val, &iter)) { | |
| if (key == 1) { | |
| upb_inttable_removeiter(&t, &iter); | |
| } | |
| } | |
| // The remaining element 9 should still be in the table and reachable. | |
| EXPECT_EQ(upb_inttable_count(&t), 1); | |
| EXPECT_TRUE(upb_inttable_lookup(&t, 9, &val)); | |
| } | |
| TEST(IntTableTest, InPlaceGrow) { | |
| char initial_buf[65536]; | |
| upb_Arena* arena = upb_Arena_Init(initial_buf, sizeof(initial_buf), nullptr); | |
| upb_inttable t; | |
| ASSERT_TRUE(upb_inttable_init(&t, arena)); | |
| const void* initial_entries = t.t.entries; | |
| for (uintptr_t i = 1; i <= 100; i++) { | |
| ASSERT_TRUE(upb_inttable_insert(&t, i, upb_value_uint64(i * 10), arena)); | |
| } | |
| // Verify that in-place growth actually occurred (entries pointer unchanged). | |
| EXPECT_EQ(t.t.entries, initial_entries); | |
| EXPECT_EQ(upb_inttable_count(&t), 100); | |
| for (uintptr_t i = 1; i <= 100; i++) { | |
| upb_value val; | |
| ASSERT_TRUE(upb_inttable_lookup(&t, i, &val)) | |
| << "Failed lookup for key " << i; | |
| EXPECT_EQ(upb_value_getuint64(val), i * 10); | |
| } | |
| upb_Arena_Free(arena); | |
| } |