1 //===- llvm/unittest/DebugInfo/PDB/HashTableTest.cpp ----------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "llvm/DebugInfo/PDB/Native/HashTable.h"
11 #include "llvm/DebugInfo/PDB/Native/Hash.h"
12 #include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h"
13 #include "llvm/Support/Allocator.h"
14 #include "llvm/Support/BinaryByteStream.h"
15 #include "llvm/Support/BinaryStreamReader.h"
16 #include "llvm/Support/BinaryStreamWriter.h"
17 #include "llvm/Support/StringSaver.h"
18 #include "llvm/Testing/Support/Error.h"
20 #include "gtest/gtest.h"
25 using namespace llvm::pdb
;
26 using namespace llvm::support
;
30 class HashTableInternals
: public HashTable
<uint32_t> {
32 using HashTable::Buckets
;
33 using HashTable::Present
;
34 using HashTable::Deleted
;
38 TEST(HashTableTest
, TestSimple
) {
39 HashTableInternals Table
;
40 EXPECT_EQ(0u, Table
.size());
41 EXPECT_GT(Table
.capacity(), 0u);
44 EXPECT_EQ(1u, Table
.size());
45 ASSERT_NE(Table
.end(), Table
.find_as(3u));
46 EXPECT_EQ(7u, Table
.get(3u));
49 TEST(HashTableTest
, TestCollision
) {
50 HashTableInternals Table
;
51 EXPECT_EQ(0u, Table
.size());
52 EXPECT_GT(Table
.capacity(), 0u);
54 // We use knowledge of the hash table's implementation details to make sure
55 // to add another value that is the equivalent to the first value modulo the
56 // hash table's capacity.
57 uint32_t N1
= Table
.capacity() + 1;
62 EXPECT_EQ(2u, Table
.size());
63 ASSERT_NE(Table
.end(), Table
.find_as(N1
));
64 ASSERT_NE(Table
.end(), Table
.find_as(N2
));
66 EXPECT_EQ(7u, Table
.get(N1
));
67 EXPECT_EQ(12u, Table
.get(N2
));
70 TEST(HashTableTest
, TestRemove
) {
71 HashTableInternals Table
;
72 EXPECT_EQ(0u, Table
.size());
73 EXPECT_GT(Table
.capacity(), 0u);
77 EXPECT_EQ(2u, Table
.size());
78 ASSERT_NE(Table
.end(), Table
.find_as(1u));
79 ASSERT_NE(Table
.end(), Table
.find_as(3u));
81 EXPECT_EQ(2u, Table
.get(1u));
82 EXPECT_EQ(4u, Table
.get(3u));
85 TEST(HashTableTest
, TestCollisionAfterMultipleProbes
) {
86 HashTableInternals Table
;
87 EXPECT_EQ(0u, Table
.size());
88 EXPECT_GT(Table
.capacity(), 0u);
90 // Probing looks for the first available slot. A slot may already be filled
91 // as a result of an item with a *different* hash value already being there.
92 // Test that when this happens, the probe still finds the value.
93 uint32_t N1
= Table
.capacity() + 1;
100 EXPECT_EQ(3u, Table
.size());
101 ASSERT_NE(Table
.end(), Table
.find_as(N1
));
102 ASSERT_NE(Table
.end(), Table
.find_as(N2
));
103 ASSERT_NE(Table
.end(), Table
.find_as(N3
));
105 EXPECT_EQ(7u, Table
.get(N1
));
106 EXPECT_EQ(11u, Table
.get(N2
));
107 EXPECT_EQ(13u, Table
.get(N3
));
110 TEST(HashTableTest
, Grow
) {
111 // So that we are independent of the load factor, `capacity` items, which is
112 // guaranteed to trigger a grow. Then verify that the size is the same, the
113 // capacity is larger, and all the original items are still in the table.
115 HashTableInternals Table
;
116 uint32_t OldCapacity
= Table
.capacity();
117 for (uint32_t I
= 0; I
< OldCapacity
; ++I
) {
118 Table
.set_as(OldCapacity
+ I
* 2 + 1, I
* 2 + 3);
120 EXPECT_EQ(OldCapacity
, Table
.size());
121 EXPECT_GT(Table
.capacity(), OldCapacity
);
122 for (uint32_t I
= 0; I
< OldCapacity
; ++I
) {
123 ASSERT_NE(Table
.end(), Table
.find_as(OldCapacity
+ I
* 2 + 1));
124 EXPECT_EQ(I
* 2 + 3, Table
.get(OldCapacity
+ I
* 2 + 1));
128 TEST(HashTableTest
, Serialization
) {
129 HashTableInternals Table
;
130 uint32_t Cap
= Table
.capacity();
131 for (uint32_t I
= 0; I
< Cap
; ++I
) {
132 Table
.set_as(Cap
+ I
* 2 + 1, I
* 2 + 3);
135 std::vector
<uint8_t> Buffer(Table
.calculateSerializedLength());
136 MutableBinaryByteStream
Stream(Buffer
, little
);
137 BinaryStreamWriter
Writer(Stream
);
138 EXPECT_THAT_ERROR(Table
.commit(Writer
), Succeeded());
139 // We should have written precisely the number of bytes we calculated earlier.
140 EXPECT_EQ(Buffer
.size(), Writer
.getOffset());
142 HashTableInternals Table2
;
143 BinaryStreamReader
Reader(Stream
);
144 EXPECT_THAT_ERROR(Table2
.load(Reader
), Succeeded());
145 // We should have read precisely the number of bytes we calculated earlier.
146 EXPECT_EQ(Buffer
.size(), Reader
.getOffset());
148 EXPECT_EQ(Table
.size(), Table2
.size());
149 EXPECT_EQ(Table
.capacity(), Table2
.capacity());
150 EXPECT_EQ(Table
.Buckets
, Table2
.Buckets
);
151 EXPECT_EQ(Table
.Present
, Table2
.Present
);
152 EXPECT_EQ(Table
.Deleted
, Table2
.Deleted
);
155 TEST(HashTableTest
, NamedStreamMap
) {
156 std::vector
<StringRef
> Streams
= {"One", "Two", "Three", "Four",
157 "Five", "Six", "Seven"};
158 StringMap
<uint32_t> ExpectedIndices
;
159 for (uint32_t I
= 0; I
< Streams
.size(); ++I
)
160 ExpectedIndices
[Streams
[I
]] = I
+ 1;
162 // To verify the hash table actually works, we want to verify that insertion
163 // order doesn't matter. So try inserting in every possible order of 7 items.
166 for (StringRef S
: Streams
)
167 NSM
.set(S
, ExpectedIndices
[S
]);
169 EXPECT_EQ(Streams
.size(), NSM
.size());
172 EXPECT_TRUE(NSM
.get("One", N
));
175 EXPECT_TRUE(NSM
.get("Two", N
));
178 EXPECT_TRUE(NSM
.get("Three", N
));
181 EXPECT_TRUE(NSM
.get("Four", N
));
184 EXPECT_TRUE(NSM
.get("Five", N
));
187 EXPECT_TRUE(NSM
.get("Six", N
));
190 EXPECT_TRUE(NSM
.get("Seven", N
));
192 } while (std::next_permutation(Streams
.begin(), Streams
.end()));
205 template <> struct PdbHashTraits
<FooBar
> {
206 std::vector
<char> Buffer
;
208 PdbHashTraits() { Buffer
.push_back(0); }
210 uint32_t hashLookupKey(StringRef S
) const {
211 return llvm::pdb::hashStringV1(S
);
214 StringRef
storageKeyToLookupKey(uint32_t N
) const {
215 if (N
>= Buffer
.size())
218 return StringRef(Buffer
.data() + N
);
221 uint32_t lookupKeyToStorageKey(StringRef S
) {
222 uint32_t N
= Buffer
.size();
223 Buffer
.insert(Buffer
.end(), S
.begin(), S
.end());
224 Buffer
.push_back('\0');
231 TEST(HashTableTest
, NonTrivialValueType
) {
232 HashTable
<FooBar
> Table
;
233 uint32_t Cap
= Table
.capacity();
234 for (uint32_t I
= 0; I
< Cap
; ++I
) {
238 Table
.set_as(utostr(I
), F
);
241 std::vector
<uint8_t> Buffer(Table
.calculateSerializedLength());
242 MutableBinaryByteStream
Stream(Buffer
, little
);
243 BinaryStreamWriter
Writer(Stream
);
244 EXPECT_THAT_ERROR(Table
.commit(Writer
), Succeeded());
245 // We should have written precisely the number of bytes we calculated earlier.
246 EXPECT_EQ(Buffer
.size(), Writer
.getOffset());
248 HashTable
<FooBar
> Table2
;
249 BinaryStreamReader
Reader(Stream
);
250 EXPECT_THAT_ERROR(Table2
.load(Reader
), Succeeded());
251 // We should have read precisely the number of bytes we calculated earlier.
252 EXPECT_EQ(Buffer
.size(), Reader
.getOffset());
254 EXPECT_EQ(Table
.size(), Table2
.size());
255 EXPECT_EQ(Table
.capacity(), Table2
.capacity());
256 // EXPECT_EQ(Table.Buckets, Table2.Buckets);
257 // EXPECT_EQ(Table.Present, Table2.Present);
258 // EXPECT_EQ(Table.Deleted, Table2.Deleted);