111036a90SZachary Turner //===- llvm/unittest/DebugInfo/PDB/HashTableTest.cpp ----------------------===//
211036a90SZachary Turner //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
611036a90SZachary Turner //
711036a90SZachary Turner //===----------------------------------------------------------------------===//
811036a90SZachary Turner 
91f5f0643SAdrian McCarthy #include "llvm/DebugInfo/PDB/Native/HashTable.h"
10ebf03f6cSZachary Turner 
11ebf03f6cSZachary Turner #include "llvm/DebugInfo/PDB/Native/Hash.h"
12cafd4768SZachary Turner #include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h"
13ebf03f6cSZachary Turner #include "llvm/Support/Allocator.h"
14d9dc2829SZachary Turner #include "llvm/Support/BinaryByteStream.h"
15d9dc2829SZachary Turner #include "llvm/Support/BinaryStreamReader.h"
16d9dc2829SZachary Turner #include "llvm/Support/BinaryStreamWriter.h"
17ebf03f6cSZachary Turner #include "llvm/Support/StringSaver.h"
18cb30e705SZachary Turner #include "llvm/Testing/Support/Error.h"
19cb30e705SZachary Turner 
20cb30e705SZachary Turner #include "gtest/gtest.h"
2111036a90SZachary Turner 
2211036a90SZachary Turner #include <vector>
2311036a90SZachary Turner 
2411036a90SZachary Turner using namespace llvm;
2511036a90SZachary Turner using namespace llvm::pdb;
26695ed56bSZachary Turner using namespace llvm::support;
2711036a90SZachary Turner 
2811036a90SZachary Turner namespace {
29ebf03f6cSZachary Turner 
30*51a52b58SNico Weber struct IdentityHashTraits {
hashLookupKey__anonb05a8faf0111::IdentityHashTraits31*51a52b58SNico Weber   uint32_t hashLookupKey(uint32_t N) const { return N; }
storageKeyToLookupKey__anonb05a8faf0111::IdentityHashTraits32*51a52b58SNico Weber   uint32_t storageKeyToLookupKey(uint32_t N) const { return N; }
lookupKeyToStorageKey__anonb05a8faf0111::IdentityHashTraits33*51a52b58SNico Weber   uint32_t lookupKeyToStorageKey(uint32_t N) { return N; }
34*51a52b58SNico Weber };
35*51a52b58SNico Weber 
36*51a52b58SNico Weber template <class T = uint32_t>
37*51a52b58SNico Weber class HashTableInternals : public HashTable<T> {
3811036a90SZachary Turner public:
39*51a52b58SNico Weber   using HashTable<T>::Buckets;
40*51a52b58SNico Weber   using HashTable<T>::Present;
41*51a52b58SNico Weber   using HashTable<T>::Deleted;
4211036a90SZachary Turner };
4311036a90SZachary Turner }
4411036a90SZachary Turner 
TEST(HashTableTest,TestSimple)4511036a90SZachary Turner TEST(HashTableTest, TestSimple) {
46*51a52b58SNico Weber   HashTableInternals<> Table;
4711036a90SZachary Turner   EXPECT_EQ(0u, Table.size());
4811036a90SZachary Turner   EXPECT_GT(Table.capacity(), 0u);
4911036a90SZachary Turner 
50*51a52b58SNico Weber   IdentityHashTraits Traits;
51*51a52b58SNico Weber   Table.set_as(3u, 7, Traits);
5211036a90SZachary Turner   EXPECT_EQ(1u, Table.size());
53*51a52b58SNico Weber   ASSERT_NE(Table.end(), Table.find_as(3u, Traits));
54*51a52b58SNico Weber   EXPECT_EQ(7u, Table.get(3u, Traits));
5511036a90SZachary Turner }
5611036a90SZachary Turner 
TEST(HashTableTest,TestCollision)5711036a90SZachary Turner TEST(HashTableTest, TestCollision) {
58*51a52b58SNico Weber   HashTableInternals<> Table;
5911036a90SZachary Turner   EXPECT_EQ(0u, Table.size());
6011036a90SZachary Turner   EXPECT_GT(Table.capacity(), 0u);
6111036a90SZachary Turner 
6211036a90SZachary Turner   // We use knowledge of the hash table's implementation details to make sure
6311036a90SZachary Turner   // to add another value that is the equivalent to the first value modulo the
6411036a90SZachary Turner   // hash table's capacity.
6511036a90SZachary Turner   uint32_t N1 = Table.capacity() + 1;
6611036a90SZachary Turner   uint32_t N2 = 2 * N1;
6711036a90SZachary Turner 
68*51a52b58SNico Weber   IdentityHashTraits Traits;
69*51a52b58SNico Weber   Table.set_as(N1, 7, Traits);
70*51a52b58SNico Weber   Table.set_as(N2, 12, Traits);
7111036a90SZachary Turner   EXPECT_EQ(2u, Table.size());
72*51a52b58SNico Weber   ASSERT_NE(Table.end(), Table.find_as(N1, Traits));
73*51a52b58SNico Weber   ASSERT_NE(Table.end(), Table.find_as(N2, Traits));
7411036a90SZachary Turner 
75*51a52b58SNico Weber   EXPECT_EQ(7u, Table.get(N1, Traits));
76*51a52b58SNico Weber   EXPECT_EQ(12u, Table.get(N2, Traits));
7711036a90SZachary Turner }
7811036a90SZachary Turner 
TEST(HashTableTest,TestRemove)7911036a90SZachary Turner TEST(HashTableTest, TestRemove) {
80*51a52b58SNico Weber   HashTableInternals<> Table;
8111036a90SZachary Turner   EXPECT_EQ(0u, Table.size());
8211036a90SZachary Turner   EXPECT_GT(Table.capacity(), 0u);
8311036a90SZachary Turner 
84*51a52b58SNico Weber   IdentityHashTraits Traits;
85*51a52b58SNico Weber   Table.set_as(1u, 2, Traits);
86*51a52b58SNico Weber   Table.set_as(3u, 4, Traits);
8711036a90SZachary Turner   EXPECT_EQ(2u, Table.size());
88*51a52b58SNico Weber   ASSERT_NE(Table.end(), Table.find_as(1u, Traits));
89*51a52b58SNico Weber   ASSERT_NE(Table.end(), Table.find_as(3u, Traits));
9011036a90SZachary Turner 
91*51a52b58SNico Weber   EXPECT_EQ(2u, Table.get(1u, Traits));
92*51a52b58SNico Weber   EXPECT_EQ(4u, Table.get(3u, Traits));
9311036a90SZachary Turner }
9411036a90SZachary Turner 
TEST(HashTableTest,TestCollisionAfterMultipleProbes)9511036a90SZachary Turner TEST(HashTableTest, TestCollisionAfterMultipleProbes) {
96*51a52b58SNico Weber   HashTableInternals<> Table;
9711036a90SZachary Turner   EXPECT_EQ(0u, Table.size());
9811036a90SZachary Turner   EXPECT_GT(Table.capacity(), 0u);
9911036a90SZachary Turner 
10011036a90SZachary Turner   // Probing looks for the first available slot.  A slot may already be filled
10111036a90SZachary Turner   // as a result of an item with a *different* hash value already being there.
10211036a90SZachary Turner   // Test that when this happens, the probe still finds the value.
10311036a90SZachary Turner   uint32_t N1 = Table.capacity() + 1;
10411036a90SZachary Turner   uint32_t N2 = N1 + 1;
10511036a90SZachary Turner   uint32_t N3 = 2 * N1;
10611036a90SZachary Turner 
107*51a52b58SNico Weber   IdentityHashTraits Traits;
108*51a52b58SNico Weber   Table.set_as(N1, 7, Traits);
109*51a52b58SNico Weber   Table.set_as(N2, 11, Traits);
110*51a52b58SNico Weber   Table.set_as(N3, 13, Traits);
11111036a90SZachary Turner   EXPECT_EQ(3u, Table.size());
112*51a52b58SNico Weber   ASSERT_NE(Table.end(), Table.find_as(N1, Traits));
113*51a52b58SNico Weber   ASSERT_NE(Table.end(), Table.find_as(N2, Traits));
114*51a52b58SNico Weber   ASSERT_NE(Table.end(), Table.find_as(N3, Traits));
11511036a90SZachary Turner 
116*51a52b58SNico Weber   EXPECT_EQ(7u, Table.get(N1, Traits));
117*51a52b58SNico Weber   EXPECT_EQ(11u, Table.get(N2, Traits));
118*51a52b58SNico Weber   EXPECT_EQ(13u, Table.get(N3, Traits));
11911036a90SZachary Turner }
12011036a90SZachary Turner 
TEST(HashTableTest,Grow)12111036a90SZachary Turner TEST(HashTableTest, Grow) {
12211036a90SZachary Turner   // So that we are independent of the load factor, `capacity` items, which is
12311036a90SZachary Turner   // guaranteed to trigger a grow.  Then verify that the size is the same, the
12411036a90SZachary Turner   // capacity is larger, and all the original items are still in the table.
12511036a90SZachary Turner 
126*51a52b58SNico Weber   HashTableInternals<> Table;
127*51a52b58SNico Weber   IdentityHashTraits Traits;
12811036a90SZachary Turner   uint32_t OldCapacity = Table.capacity();
12911036a90SZachary Turner   for (uint32_t I = 0; I < OldCapacity; ++I) {
130*51a52b58SNico Weber     Table.set_as(OldCapacity + I * 2 + 1, I * 2 + 3, Traits);
13111036a90SZachary Turner   }
13211036a90SZachary Turner   EXPECT_EQ(OldCapacity, Table.size());
13311036a90SZachary Turner   EXPECT_GT(Table.capacity(), OldCapacity);
13411036a90SZachary Turner   for (uint32_t I = 0; I < OldCapacity; ++I) {
135*51a52b58SNico Weber     ASSERT_NE(Table.end(), Table.find_as(OldCapacity + I * 2 + 1, Traits));
136*51a52b58SNico Weber     EXPECT_EQ(I * 2 + 3, Table.get(OldCapacity + I * 2 + 1, Traits));
13711036a90SZachary Turner   }
13811036a90SZachary Turner }
13911036a90SZachary Turner 
TEST(HashTableTest,Serialization)14011036a90SZachary Turner TEST(HashTableTest, Serialization) {
141*51a52b58SNico Weber   HashTableInternals<> Table;
142*51a52b58SNico Weber   IdentityHashTraits Traits;
14311036a90SZachary Turner   uint32_t Cap = Table.capacity();
14411036a90SZachary Turner   for (uint32_t I = 0; I < Cap; ++I) {
145*51a52b58SNico Weber     Table.set_as(Cap + I * 2 + 1, I * 2 + 3, Traits);
14611036a90SZachary Turner   }
14711036a90SZachary Turner 
14811036a90SZachary Turner   std::vector<uint8_t> Buffer(Table.calculateSerializedLength());
149695ed56bSZachary Turner   MutableBinaryByteStream Stream(Buffer, little);
150120faca4SZachary Turner   BinaryStreamWriter Writer(Stream);
151cb30e705SZachary Turner   EXPECT_THAT_ERROR(Table.commit(Writer), Succeeded());
15211036a90SZachary Turner   // We should have written precisely the number of bytes we calculated earlier.
15311036a90SZachary Turner   EXPECT_EQ(Buffer.size(), Writer.getOffset());
15411036a90SZachary Turner 
155*51a52b58SNico Weber   HashTableInternals<> Table2;
156120faca4SZachary Turner   BinaryStreamReader Reader(Stream);
157cb30e705SZachary Turner   EXPECT_THAT_ERROR(Table2.load(Reader), Succeeded());
15811036a90SZachary Turner   // We should have read precisely the number of bytes we calculated earlier.
15911036a90SZachary Turner   EXPECT_EQ(Buffer.size(), Reader.getOffset());
16011036a90SZachary Turner 
16111036a90SZachary Turner   EXPECT_EQ(Table.size(), Table2.size());
16211036a90SZachary Turner   EXPECT_EQ(Table.capacity(), Table2.capacity());
16311036a90SZachary Turner   EXPECT_EQ(Table.Buckets, Table2.Buckets);
16411036a90SZachary Turner   EXPECT_EQ(Table.Present, Table2.Present);
16511036a90SZachary Turner   EXPECT_EQ(Table.Deleted, Table2.Deleted);
16611036a90SZachary Turner }
167cafd4768SZachary Turner 
TEST(HashTableTest,NamedStreamMap)168cafd4768SZachary Turner TEST(HashTableTest, NamedStreamMap) {
169cafd4768SZachary Turner   std::vector<StringRef> Streams = {"One",  "Two", "Three", "Four",
170cafd4768SZachary Turner                                     "Five", "Six", "Seven"};
171cafd4768SZachary Turner   StringMap<uint32_t> ExpectedIndices;
172cafd4768SZachary Turner   for (uint32_t I = 0; I < Streams.size(); ++I)
173cafd4768SZachary Turner     ExpectedIndices[Streams[I]] = I + 1;
174cafd4768SZachary Turner 
175cafd4768SZachary Turner   // To verify the hash table actually works, we want to verify that insertion
176cafd4768SZachary Turner   // order doesn't matter.  So try inserting in every possible order of 7 items.
177cafd4768SZachary Turner   do {
178cafd4768SZachary Turner     NamedStreamMap NSM;
179cafd4768SZachary Turner     for (StringRef S : Streams)
180cafd4768SZachary Turner       NSM.set(S, ExpectedIndices[S]);
181cafd4768SZachary Turner 
182cafd4768SZachary Turner     EXPECT_EQ(Streams.size(), NSM.size());
183cafd4768SZachary Turner 
184cafd4768SZachary Turner     uint32_t N;
185cafd4768SZachary Turner     EXPECT_TRUE(NSM.get("One", N));
186e752e685SEric Christopher     EXPECT_EQ(1U, N);
187cafd4768SZachary Turner 
188cafd4768SZachary Turner     EXPECT_TRUE(NSM.get("Two", N));
189e752e685SEric Christopher     EXPECT_EQ(2U, N);
190cafd4768SZachary Turner 
191cafd4768SZachary Turner     EXPECT_TRUE(NSM.get("Three", N));
192e752e685SEric Christopher     EXPECT_EQ(3U, N);
193cafd4768SZachary Turner 
194cafd4768SZachary Turner     EXPECT_TRUE(NSM.get("Four", N));
195e752e685SEric Christopher     EXPECT_EQ(4U, N);
196cafd4768SZachary Turner 
197cafd4768SZachary Turner     EXPECT_TRUE(NSM.get("Five", N));
198e752e685SEric Christopher     EXPECT_EQ(5U, N);
199cafd4768SZachary Turner 
200cafd4768SZachary Turner     EXPECT_TRUE(NSM.get("Six", N));
201e752e685SEric Christopher     EXPECT_EQ(6U, N);
202cafd4768SZachary Turner 
203cafd4768SZachary Turner     EXPECT_TRUE(NSM.get("Seven", N));
204e752e685SEric Christopher     EXPECT_EQ(7U, N);
205cafd4768SZachary Turner   } while (std::next_permutation(Streams.begin(), Streams.end()));
206cafd4768SZachary Turner }
207ebf03f6cSZachary Turner 
208ebf03f6cSZachary Turner struct FooBar {
209ebf03f6cSZachary Turner   uint32_t X;
210ebf03f6cSZachary Turner   uint32_t Y;
211*51a52b58SNico Weber 
operator ==FooBar212*51a52b58SNico Weber   bool operator==(const FooBar &RHS) const {
213*51a52b58SNico Weber     return X == RHS.X && Y == RHS.Y;
214*51a52b58SNico Weber   }
215ebf03f6cSZachary Turner };
216ebf03f6cSZachary Turner 
217*51a52b58SNico Weber struct FooBarHashTraits {
218ebf03f6cSZachary Turner   std::vector<char> Buffer;
219ebf03f6cSZachary Turner 
FooBarHashTraitsFooBarHashTraits220*51a52b58SNico Weber   FooBarHashTraits() { Buffer.push_back(0); }
221ebf03f6cSZachary Turner 
hashLookupKeyFooBarHashTraits222ebf03f6cSZachary Turner   uint32_t hashLookupKey(StringRef S) const {
223ebf03f6cSZachary Turner     return llvm::pdb::hashStringV1(S);
224ebf03f6cSZachary Turner   }
225ebf03f6cSZachary Turner 
storageKeyToLookupKeyFooBarHashTraits226ebf03f6cSZachary Turner   StringRef storageKeyToLookupKey(uint32_t N) const {
227ebf03f6cSZachary Turner     if (N >= Buffer.size())
228ebf03f6cSZachary Turner       return StringRef();
229ebf03f6cSZachary Turner 
230ebf03f6cSZachary Turner     return StringRef(Buffer.data() + N);
231ebf03f6cSZachary Turner   }
232ebf03f6cSZachary Turner 
lookupKeyToStorageKeyFooBarHashTraits233ebf03f6cSZachary Turner   uint32_t lookupKeyToStorageKey(StringRef S) {
234ebf03f6cSZachary Turner     uint32_t N = Buffer.size();
235ebf03f6cSZachary Turner     Buffer.insert(Buffer.end(), S.begin(), S.end());
236ebf03f6cSZachary Turner     Buffer.push_back('\0');
237ebf03f6cSZachary Turner     return N;
238ebf03f6cSZachary Turner   }
239ebf03f6cSZachary Turner };
240ebf03f6cSZachary Turner 
TEST(HashTableTest,NonTrivialValueType)241ebf03f6cSZachary Turner TEST(HashTableTest, NonTrivialValueType) {
242*51a52b58SNico Weber   HashTableInternals<FooBar> Table;
243*51a52b58SNico Weber   FooBarHashTraits Traits;
244ebf03f6cSZachary Turner   uint32_t Cap = Table.capacity();
245ebf03f6cSZachary Turner   for (uint32_t I = 0; I < Cap; ++I) {
246ebf03f6cSZachary Turner     FooBar F;
247ebf03f6cSZachary Turner     F.X = I;
248ebf03f6cSZachary Turner     F.Y = I + 1;
249*51a52b58SNico Weber     Table.set_as(utostr(I), F, Traits);
250ebf03f6cSZachary Turner   }
251ebf03f6cSZachary Turner 
252ebf03f6cSZachary Turner   std::vector<uint8_t> Buffer(Table.calculateSerializedLength());
253ebf03f6cSZachary Turner   MutableBinaryByteStream Stream(Buffer, little);
254ebf03f6cSZachary Turner   BinaryStreamWriter Writer(Stream);
255ebf03f6cSZachary Turner   EXPECT_THAT_ERROR(Table.commit(Writer), Succeeded());
256ebf03f6cSZachary Turner   // We should have written precisely the number of bytes we calculated earlier.
257ebf03f6cSZachary Turner   EXPECT_EQ(Buffer.size(), Writer.getOffset());
258ebf03f6cSZachary Turner 
259*51a52b58SNico Weber   HashTableInternals<FooBar> Table2;
260ebf03f6cSZachary Turner   BinaryStreamReader Reader(Stream);
261ebf03f6cSZachary Turner   EXPECT_THAT_ERROR(Table2.load(Reader), Succeeded());
262ebf03f6cSZachary Turner   // We should have read precisely the number of bytes we calculated earlier.
263ebf03f6cSZachary Turner   EXPECT_EQ(Buffer.size(), Reader.getOffset());
264ebf03f6cSZachary Turner 
265ebf03f6cSZachary Turner   EXPECT_EQ(Table.size(), Table2.size());
266ebf03f6cSZachary Turner   EXPECT_EQ(Table.capacity(), Table2.capacity());
267*51a52b58SNico Weber   EXPECT_EQ(Table.Buckets, Table2.Buckets);
268*51a52b58SNico Weber   EXPECT_EQ(Table.Present, Table2.Present);
269*51a52b58SNico Weber   EXPECT_EQ(Table.Deleted, Table2.Deleted);
270ebf03f6cSZachary Turner }
271