llvm.org GIT mirror llvm / 177247f
PDB HashTable: Move TraitsT from class parameter to the methods that need it The traits object is only used by a few methods. Deserializing a hash table and walking it is possible without the traits object, so it shouldn't be required to build a dummy object for that use case. The TraitsT object used to be a function template parameter before r327647, this restores it to that state. This makes it clear that the traits object isn't needed at all in 1 of the current 3 uses of HashTable (and I am going to add another use that doesn't need it), and that the default PdbHashTraits isn't used outside of tests. While here, also re-enable 3 checks in the test that were commented out (which requires making HashTableInternals templated and giving FooBar an operator==). No intended behavior change. Differential Revision: https://reviews.llvm.org/D64640 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@365974 91177308-0d34-0410-b5e6-96231b3b80d8 Nico Weber a month ago
6 changed file(s) with 119 addition(s) and 113 deletion(s). Raw diff Collapse all Expand all
3030 Error readSparseBitVector(BinaryStreamReader &Stream, SparseBitVector<> &V);
3131 Error writeSparseBitVector(BinaryStreamWriter &Writer, SparseBitVector<> &Vec);
3232
33 template class HashTable;
34
35 template , typename TraitsT>
33 template > class HashTable;
34
35 template
3636 class HashTableIterator
37 : public iterator_facade_base, TraitsT>,
37 : public iterator_facade_base>,
3838 std::forward_iterator_tag,
3939 std::pair> {
40 friend HashTable;
41
42 HashTableIterator(const HashTable &Map, uint32_t Index,
40 friend HashTable;
41
42 HashTableIterator(const HashTable &Map, uint32_t Index,
4343 bool IsEnd)
4444 : Map(&Map), Index(Index), IsEnd(IsEnd) {}
4545
4646 public:
47 HashTableIterator(const HashTable, TraitsT> &Map) : Map(&Map) {
47 HashTableIterator(const HashTable> &Map) : Map(&Map) {
4848 int I = Map.Present.find_first();
4949 if (I == -1) {
5050 Index = 0;
8686 bool isEnd() const { return IsEnd; }
8787 uint32_t index() const { return Index; }
8888
89 const HashTable, TraitsT> *Map;
89 const HashTable> *Map;
9090 uint32_t Index;
9191 bool IsEnd;
9292 };
9393
94 template struct PdbHashTraits {};
95
96 template <> struct PdbHashTraits {
97 uint32_t hashLookupKey(uint32_t N) const { return N; }
98 uint32_t storageKeyToLookupKey(uint32_t N) const { return N; }
99 uint32_t lookupKeyToStorageKey(uint32_t N) { return N; }
100 };
101
102 template , typename TraitsT = PdbHashTraits>
94 template >
10395 class HashTable {
104 using iterator = HashTableIterator, TraitsT>;
96 using iterator = HashTableIterator>;
10597 friend iterator;
10698
10799 struct Header {
113105
114106 public:
115107 HashTable() { Buckets.resize(8); }
116
117 explicit HashTable(TraitsT Traits) : HashTable(8, std::move(Traits)) {}
118 HashTable(uint32_t Capacity, TraitsT Traits) : Traits(Traits) {
108 explicit HashTable(uint32_t Capacity) {
119109 Buckets.resize(Capacity);
120110 }
121111
220210
221211 /// Find the entry whose key has the specified hash value, using the specified
222212 /// traits defining hash function and equality.
223 template > iterator find_as(const Key &K) const {
213 template , typename TraitsT>
214 iterator find_as(const Key &K, TraitsT &Traits) const {
224215 uint32_t H = Traits.hashLookupKey(K) % capacity();
225216 uint32_t I = H;
226217 Optional FirstUnused;
251242
252243 /// Set the entry using a key type that the specified Traits can convert
253244 /// from a real key to an internal key.
254 template bool set_as(const Key &K, ValueT V) {
255 return set_as_internal(K, std::move(V), None);
256 }
257
258 template ValueT get(const Key &K) const {
259 auto Iter = find_as(K);
245 template
246 bool set_as(const Key &K, ValueT V, TraitsT &Traits) {
247 return set_as_internal(K, std::move(V), Traits, None);
248 }
249
250 template
251 ValueT get(const Key &K, TraitsT &Traits) const {
252 auto Iter = find_as(K, Traits);
260253 assert(Iter != end());
261254 return (*Iter).second;
262255 }
265258 bool isPresent(uint32_t K) const { return Present.test(K); }
266259 bool isDeleted(uint32_t K) const { return Deleted.test(K); }
267260
268 TraitsT Traits;
269261 BucketList Buckets;
270262 mutable SparseBitVector<> Present;
271263 mutable SparseBitVector<> Deleted;
273265 private:
274266 /// Set the entry using a key type that the specified Traits can convert
275267 /// from a real key to an internal key.
276 template
277 bool set_as_internal(const Key &K, ValueT V, Optional InternalKey) {
278 auto Entry = find_as(K);
268 template
269 bool set_as_internal(const Key &K, ValueT V, TraitsT &Traits,
270 Optional InternalKey) {
271 auto Entry = find_as(K, Traits);
279272 if (Entry != end()) {
280273 assert(isPresent(Entry.index()));
281274 assert(Traits.storageKeyToLookupKey(Buckets[Entry.index()].first) == K);
292285 Present.set(Entry.index());
293286 Deleted.reset(Entry.index());
294287
295 grow();
296
297 assert((find_as(K)) != end());
288 grow(Traits);
289
290 assert((find_as(K, Traits)) != end());
298291 return true;
299292 }
300293
301294 static uint32_t maxLoad(uint32_t capacity) { return capacity * 2 / 3 + 1; }
302295
303 void grow() {
296 template
297 void grow(TraitsT &Traits) {
304298 uint32_t S = size();
305299 uint32_t MaxLoad = maxLoad(capacity());
306300 if (S < maxLoad(capacity()))
312306 // Growing requires rebuilding the table and re-hashing every item. Make a
313307 // copy with a larger capacity, insert everything into the copy, then swap
314308 // it in.
315 HashTable NewMap(NewCapacity, Traits);
309 HashTable NewMap(NewCapacity);
316310 for (auto I : Present) {
317311 auto LookupKey = Traits.storageKeyToLookupKey(Buckets[I].first);
318 NewMap.set_as_internal(LookupKey, Buckets[I].second, Buckets[I].first);
312 NewMap.set_as_internal(LookupKey, Buckets[I].second, Traits,
313 Buckets[I].first);
319314 }
320315
321316 Buckets.swap(NewMap.Buckets);
5858 NamedStreamMapTraits HashTraits;
5959 /// Closed hash table from Offset -> StreamNumber, where Offset is the offset
6060 /// of the stream name in NamesBuffer.
61 HashTable, NamedStreamMapTraits> OffsetIndexMap;
61 HashTable> OffsetIndexMap;
6262
6363 /// Buffer of string data.
6464 std::vector NamesBuffer;
9696
9797 PDBStringTableBuilder Strings;
9898 StringTableHashTraits InjectedSourceHashTraits;
99 HashTable, StringTableHashTraits> InjectedSourceTable;
99 HashTable> InjectedSourceTable;
100100
101101 SmallVector InjectedSources;
102102
4545 return NS->appendStringData(S);
4646 }
4747
48 NamedStreamMap::NamedStreamMap()
49 : HashTraits(*this), OffsetIndexMap(1, HashTraits) {}
48 NamedStreamMap::NamedStreamMap() : HashTraits(*this), OffsetIndexMap(1) {}
5049
5150 Error NamedStreamMap::load(BinaryStreamReader &Stream) {
5251 uint32_t StringBufferSize;
9897 }
9998
10099 bool NamedStreamMap::get(StringRef Stream, uint32_t &StreamNo) const {
101 auto Iter = OffsetIndexMap.find_as(Stream);
100 auto Iter = OffsetIndexMap.find_as(Stream, HashTraits);
102101 if (Iter == OffsetIndexMap.end())
103102 return false;
104103 StreamNo = (*Iter).second;
122121 }
123122
124123 void NamedStreamMap::set(StringRef Stream, uint32_t StreamNo) {
125 OffsetIndexMap.set_as(Stream, support::ulittle32_t(StreamNo));
124 OffsetIndexMap.set_as(Stream, support::ulittle32_t(StreamNo), HashTraits);
126125 }
3333
3434 PDBFileBuilder::PDBFileBuilder(BumpPtrAllocator &Allocator)
3535 : Allocator(Allocator), InjectedSourceHashTraits(Strings),
36 InjectedSourceTable(2, InjectedSourceHashTraits) {}
36 InjectedSourceTable(2) {}
3737
3838 PDBFileBuilder::~PDBFileBuilder() {}
3939
188188 static_cast(PdbRaw_SrcHeaderBlockVer::SrcVerOne);
189189 Entry.CRC = CRC.getCRC();
190190 StringRef VName = getStringTableBuilder().getStringForId(IS.VNameIndex);
191 InjectedSourceTable.set_as(VName, std::move(Entry));
191 InjectedSourceTable.set_as(VName, std::move(Entry),
192 InjectedSourceHashTraits);
192193 }
193194
194195 uint32_t SrcHeaderBlockSize =
2626
2727 namespace {
2828
29 class HashTableInternals : public HashTable {
29 struct IdentityHashTraits {
30 uint32_t hashLookupKey(uint32_t N) const { return N; }
31 uint32_t storageKeyToLookupKey(uint32_t N) const { return N; }
32 uint32_t lookupKeyToStorageKey(uint32_t N) { return N; }
33 };
34
35 template
36 class HashTableInternals : public HashTable {
3037 public:
31 using HashTable::Buckets;
32 using HashTable::Present;
33 using HashTable::Deleted;
38 using HashTable::Buckets;
39 using HashTable::Present;
40 using HashTable::Deleted;
3441 };
3542 }
3643
3744 TEST(HashTableTest, TestSimple) {
38 HashTableInternals Table;
39 EXPECT_EQ(0u, Table.size());
40 EXPECT_GT(Table.capacity(), 0u);
41
42 Table.set_as(3u, 7);
45 HashTableInternals<> Table;
46 EXPECT_EQ(0u, Table.size());
47 EXPECT_GT(Table.capacity(), 0u);
48
49 IdentityHashTraits Traits;
50 Table.set_as(3u, 7, Traits);
4351 EXPECT_EQ(1u, Table.size());
44 ASSERT_NE(Table.end(), Table.find_as(3u));
45 EXPECT_EQ(7u, Table.get(3u));
52 ASSERT_NE(Table.end(), Table.find_as(3u, Traits));
53 EXPECT_EQ(7u, Table.get(3u, Traits));
4654 }
4755
4856 TEST(HashTableTest, TestCollision) {
49 HashTableInternals Table;
57 HashTableInternals<> Table;
5058 EXPECT_EQ(0u, Table.size());
5159 EXPECT_GT(Table.capacity(), 0u);
5260
5664 uint32_t N1 = Table.capacity() + 1;
5765 uint32_t N2 = 2 * N1;
5866
59 Table.set_as(N1, 7);
60 Table.set_as(N2, 12);
67 IdentityHashTraits Traits;
68 Table.set_as(N1, 7, Traits);
69 Table.set_as(N2, 12, Traits);
6170 EXPECT_EQ(2u, Table.size());
62 ASSERT_NE(Table.end(), Table.find_as(N1));
63 ASSERT_NE(Table.end(), Table.find_as(N2));
64
65 EXPECT_EQ(7u, Table.get(N1));
66 EXPECT_EQ(12u, Table.get(N2));
71 ASSERT_NE(Table.end(), Table.find_as(N1, Traits));
72 ASSERT_NE(Table.end(), Table.find_as(N2, Traits));
73
74 EXPECT_EQ(7u, Table.get(N1, Traits));
75 EXPECT_EQ(12u, Table.get(N2, Traits));
6776 }
6877
6978 TEST(HashTableTest, TestRemove) {
70 HashTableInternals Table;
71 EXPECT_EQ(0u, Table.size());
72 EXPECT_GT(Table.capacity(), 0u);
73
74 Table.set_as(1u, 2);
75 Table.set_as(3u, 4);
79 HashTableInternals<> Table;
80 EXPECT_EQ(0u, Table.size());
81 EXPECT_GT(Table.capacity(), 0u);
82
83 IdentityHashTraits Traits;
84 Table.set_as(1u, 2, Traits);
85 Table.set_as(3u, 4, Traits);
7686 EXPECT_EQ(2u, Table.size());
77 ASSERT_NE(Table.end(), Table.find_as(1u));
78 ASSERT_NE(Table.end(), Table.find_as(3u));
79
80 EXPECT_EQ(2u, Table.get(1u));
81 EXPECT_EQ(4u, Table.get(3u));
87 ASSERT_NE(Table.end(), Table.find_as(1u, Traits));
88 ASSERT_NE(Table.end(), Table.find_as(3u, Traits));
89
90 EXPECT_EQ(2u, Table.get(1u, Traits));
91 EXPECT_EQ(4u, Table.get(3u, Traits));
8292 }
8393
8494 TEST(HashTableTest, TestCollisionAfterMultipleProbes) {
85 HashTableInternals Table;
95 HashTableInternals<> Table;
8696 EXPECT_EQ(0u, Table.size());
8797 EXPECT_GT(Table.capacity(), 0u);
8898
93103 uint32_t N2 = N1 + 1;
94104 uint32_t N3 = 2 * N1;
95105
96 Table.set_as(N1, 7);
97 Table.set_as(N2, 11);
98 Table.set_as(N3, 13);
106 IdentityHashTraits Traits;
107 Table.set_as(N1, 7, Traits);
108 Table.set_as(N2, 11, Traits);
109 Table.set_as(N3, 13, Traits);
99110 EXPECT_EQ(3u, Table.size());
100 ASSERT_NE(Table.end(), Table.find_as(N1));
101 ASSERT_NE(Table.end(), Table.find_as(N2));
102 ASSERT_NE(Table.end(), Table.find_as(N3));
103
104 EXPECT_EQ(7u, Table.get(N1));
105 EXPECT_EQ(11u, Table.get(N2));
106 EXPECT_EQ(13u, Table.get(N3));
111 ASSERT_NE(Table.end(), Table.find_as(N1, Traits));
112 ASSERT_NE(Table.end(), Table.find_as(N2, Traits));
113 ASSERT_NE(Table.end(), Table.find_as(N3, Traits));
114
115 EXPECT_EQ(7u, Table.get(N1, Traits));
116 EXPECT_EQ(11u, Table.get(N2, Traits));
117 EXPECT_EQ(13u, Table.get(N3, Traits));
107118 }
108119
109120 TEST(HashTableTest, Grow) {
111122 // guaranteed to trigger a grow. Then verify that the size is the same, the
112123 // capacity is larger, and all the original items are still in the table.
113124
114 HashTableInternals Table;
125 HashTableInternals<> Table;
126 IdentityHashTraits Traits;
115127 uint32_t OldCapacity = Table.capacity();
116128 for (uint32_t I = 0; I < OldCapacity; ++I) {
117 Table.set_as(OldCapacity + I * 2 + 1, I * 2 + 3);
129 Table.set_as(OldCapacity + I * 2 + 1, I * 2 + 3, Traits);
118130 }
119131 EXPECT_EQ(OldCapacity, Table.size());
120132 EXPECT_GT(Table.capacity(), OldCapacity);
121133 for (uint32_t I = 0; I < OldCapacity; ++I) {
122 ASSERT_NE(Table.end(), Table.find_as(OldCapacity + I * 2 + 1));
123 EXPECT_EQ(I * 2 + 3, Table.get(OldCapacity + I * 2 + 1));
134 ASSERT_NE(Table.end(), Table.find_as(OldCapacity + I * 2 + 1, Traits));
135 EXPECT_EQ(I * 2 + 3, Table.get(OldCapacity + I * 2 + 1, Traits));
124136 }
125137 }
126138
127139 TEST(HashTableTest, Serialization) {
128 HashTableInternals Table;
140 HashTableInternals<> Table;
141 IdentityHashTraits Traits;
129142 uint32_t Cap = Table.capacity();
130143 for (uint32_t I = 0; I < Cap; ++I) {
131 Table.set_as(Cap + I * 2 + 1, I * 2 + 3);
144 Table.set_as(Cap + I * 2 + 1, I * 2 + 3, Traits);
132145 }
133146
134147 std::vector Buffer(Table.calculateSerializedLength());
138151 // We should have written precisely the number of bytes we calculated earlier.
139152 EXPECT_EQ(Buffer.size(), Writer.getOffset());
140153
141 HashTableInternals Table2;
154 HashTableInternals<> Table2;
142155 BinaryStreamReader Reader(Stream);
143156 EXPECT_THAT_ERROR(Table2.load(Reader), Succeeded());
144157 // We should have read precisely the number of bytes we calculated earlier.
191204 } while (std::next_permutation(Streams.begin(), Streams.end()));
192205 }
193206
194 namespace {
195207 struct FooBar {
196208 uint32_t X;
197209 uint32_t Y;
198 };
199
200 } // namespace
201
202 namespace llvm {
203 namespace pdb {
204 template <> struct PdbHashTraits {
210
211 bool operator==(const FooBar &RHS) const {
212 return X == RHS.X && Y == RHS.Y;
213 }
214 };
215
216 struct FooBarHashTraits {
205217 std::vector Buffer;
206218
207 PdbHashTraits() { Buffer.push_back(0); }
219 FooBarHashTraits() { Buffer.push_back(0); }
208220
209221 uint32_t hashLookupKey(StringRef S) const {
210222 return llvm::pdb::hashStringV1(S);
224236 return N;
225237 }
226238 };
227 } // namespace pdb
228 } // namespace llvm
229239
230240 TEST(HashTableTest, NonTrivialValueType) {
231 HashTable Table;
241 HashTableInternals Table;
242 FooBarHashTraits Traits;
232243 uint32_t Cap = Table.capacity();
233244 for (uint32_t I = 0; I < Cap; ++I) {
234245 FooBar F;
235246 F.X = I;
236247 F.Y = I + 1;
237 Table.set_as(utostr(I), F);
248 Table.set_as(utostr(I), F, Traits);
238249 }
239250
240251 std::vector Buffer(Table.calculateSerializedLength());
244255 // We should have written precisely the number of bytes we calculated earlier.
245256 EXPECT_EQ(Buffer.size(), Writer.getOffset());
246257
247 HashTable Table2;
258 HashTableInternals Table2;
248259 BinaryStreamReader Reader(Stream);
249260 EXPECT_THAT_ERROR(Table2.load(Reader), Succeeded());
250261 // We should have read precisely the number of bytes we calculated earlier.
252263
253264 EXPECT_EQ(Table.size(), Table2.size());
254265 EXPECT_EQ(Table.capacity(), Table2.capacity());
255 // EXPECT_EQ(Table.Buckets, Table2.Buckets);
256 // EXPECT_EQ(Table.Present, Table2.Present);
257 // EXPECT_EQ(Table.Deleted, Table2.Deleted);
258 }
266 EXPECT_EQ(Table.Buckets, Table2.Buckets);
267 EXPECT_EQ(Table.Present, Table2.Present);
268 EXPECT_EQ(Table.Deleted, Table2.Deleted);
269 }