// Protocol Buffers - Google's data interchange format // Copyright 2023 Google LLC. All rights reserved. // https://developers.google.com/protocol-buffers/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Tests for upb_table. #include #include #include #include #include #include #include #include #include "gtest/gtest.h" #include "upb/hash/int_table.h" #include "upb/hash/str_table.h" #include "upb/upb.hpp" // Must be last. #include "upb/port/def.inc" using std::vector; TEST(Table, StringTable) { vector 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; upb_strtable_init(&t, keys.size(), arena.ptr()); std::map m; std::set all; for (const auto& key : keys) { all.insert(key); upb_value val = {uint64_t(key[0])}; 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::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 { 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 keys_; }; TEST_P(IntTableTest, TestIntTable) { /* Initialize structures. */ upb::Arena arena; upb_inttable t; upb_inttable_init(&t, arena.ptr()); uint32_t largest_key = 0; std::map m; std::unordered_map 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)); // Compact and test correctness again. upb_inttable_compact(&t, arena.ptr()); 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 * 3); EXPECT_EQ(m[i], i * 3); EXPECT_EQ(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)); } INSTANTIATE_TEST_SUITE_P(IntTableParams, IntTableTest, testing::Values(8, 64, 512, -32)); /* * This test can't pass right now because the table can't store a value of * (uint64_t)-1. */ TEST(Table, MaxValue) { /* typedef upb::TypedIntTable Table; Table table; uintptr_t uint64_max = (uint64_t)-1; table.Insert(1, uint64_max); std::pair found = table.Lookup(1); ASSERT(found.first); ASSERT(found.second == uint64_max); */ } TEST(Table, Delete) { upb::Arena arena; upb_inttable t; upb_inttable_init(&t, arena.ptr()); upb_inttable_insert(&t, 0, upb_value_bool(true), arena.ptr()); upb_inttable_insert(&t, 2, upb_value_bool(true), arena.ptr()); upb_inttable_insert(&t, 4, upb_value_bool(true), arena.ptr()); upb_inttable_compact(&t, arena.ptr()); upb_inttable_remove(&t, 0, NULL); upb_inttable_remove(&t, 2, NULL); upb_inttable_remove(&t, 4, NULL); intptr_t iter = UPB_INTTABLE_BEGIN; uintptr_t key; upb_value val; while (upb_inttable_next(&t, &key, &val, &iter)) { ASSERT_TRUE(false); } } 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; upb_strtable_init(&t, i, arena.ptr()); } }