llvm.org GIT mirror llvm / 1ae46a0
Refactor the PDB HashTable class. It previously only worked when the key and value types were both 4 byte integers. We now have a use case for a non trivial value type, so we need to extend it to support arbitrary value types, which means templatizing it. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@327647 91177308-0d34-0410-b5e6-96231b3b80d8 Zachary Turner 1 year, 6 months ago
8 changed file(s) with 341 addition(s) and 334 deletion(s). Raw diff Collapse all Expand all
1111
1212 #include "llvm/ADT/SparseBitVector.h"
1313 #include "llvm/ADT/iterator.h"
14 #include "llvm/DebugInfo/PDB/Native/RawError.h"
1415 #include "llvm/Support/Endian.h"
1516 #include "llvm/Support/Error.h"
1617 #include
2526
2627 namespace pdb {
2728
28 class HashTable;
29
29 template class HashTable;
30
31 template
3032 class HashTableIterator
31 : public iterator_facade_base
32 std::pair> {
33 friend HashTable;
34
35 HashTableIterator(const HashTable &Map, uint32_t Index, bool IsEnd);
33 : public iterator_facade_base,
34 std::forward_iterator_tag,
35 std::pair> {
36 friend HashTable;
37
38 HashTableIterator(const HashTable &Map, uint32_t Index,
39 bool IsEnd)
40 : Map(&Map), Index(Index), IsEnd(IsEnd) {}
3641
3742 public:
38 HashTableIterator(const HashTable &Map);
39
40 HashTableIterator &operator=(const HashTableIterator &R);
41 bool operator==(const HashTableIterator &R) const;
42 const std::pair &operator*() const;
43 HashTableIterator &operator++();
43 HashTableIterator(const HashTable &Map) : Map(&Map) {
44 int I = Map.Present.find_first();
45 if (I == -1) {
46 Index = 0;
47 IsEnd = true;
48 } else {
49 Index = static_cast(I);
50 IsEnd = false;
51 }
52 }
53
54 HashTableIterator &operator=(const HashTableIterator &R) {
55 Map = R.Map;
56 return *this;
57 }
58 bool operator==(const HashTableIterator &R) const {
59 if (IsEnd && R.IsEnd)
60 return true;
61 if (IsEnd != R.IsEnd)
62 return false;
63
64 return (Map == R.Map) && (Index == R.Index);
65 }
66 const std::pair &operator*() const {
67 assert(Map->Present.test(Index));
68 return Map->Buckets[Index];
69 }
70 HashTableIterator &operator++() {
71 while (Index < Map->Buckets.size()) {
72 ++Index;
73 if (Map->Present.test(Index))
74 return *this;
75 }
76
77 IsEnd = true;
78 return *this;
79 }
4480
4581 private:
4682 bool isEnd() const { return IsEnd; }
4783 uint32_t index() const { return Index; }
4884
49 const HashTable *Map;
85 const HashTable *Map;
5086 uint32_t Index;
5187 bool IsEnd;
5288 };
5389
90 template struct PdbHashTraits {};
91
92 template <> struct PdbHashTraits {
93 uint32_t hashLookupKey(uint32_t N) const { return N; }
94 uint32_t storageKeyToLookupKey(uint32_t N) const { return N; }
95 uint32_t lookupKeyToStorageKey(uint32_t N) { return N; }
96 };
97
98 template >
5499 class HashTable {
55 friend class HashTableIterator;
100 using iterator = HashTableIterator;
101 friend iterator;
56102
57103 struct Header {
58104 support::ulittle32_t Size;
59105 support::ulittle32_t Capacity;
60106 };
61107
62 using BucketList = std::vectoruint32_t>>;
108 using BucketList = std::vectorValueT>>;
63109
64110 public:
65 HashTable();
66 explicit HashTable(uint32_t Capacity);
67
68 Error load(BinaryStreamReader &Stream);
69
70 uint32_t calculateSerializedLength() const;
71 Error commit(BinaryStreamWriter &Writer) const;
72
73 void clear();
74
75 uint32_t capacity() const;
76 uint32_t size() const;
77
78 HashTableIterator begin() const;
79 HashTableIterator end() const;
80
81 /// Find the entry with the specified key value.
82 HashTableIterator find(uint32_t K) const;
111 HashTable() { Buckets.resize(8); }
112
113 explicit HashTable(TraitsT Traits) : HashTable(8, std::move(Traits)) {}
114 HashTable(uint32_t Capacity, TraitsT Traits) : Traits(Traits) {
115 Buckets.resize(Capacity);
116 }
117
118 Error load(BinaryStreamReader &Stream) {
119 const Header *H;
120 if (auto EC = Stream.readObject(H))
121 return EC;
122 if (H->Capacity == 0)
123 return make_error(raw_error_code::corrupt_file,
124 "Invalid Hash Table Capacity");
125 if (H->Size > maxLoad(H->Capacity))
126 return make_error(raw_error_code::corrupt_file,
127 "Invalid Hash Table Size");
128
129 Buckets.resize(H->Capacity);
130
131 if (auto EC = readSparseBitVector(Stream, Present))
132 return EC;
133 if (Present.count() != H->Size)
134 return make_error(raw_error_code::corrupt_file,
135 "Present bit vector does not match size!");
136
137 if (auto EC = readSparseBitVector(Stream, Deleted))
138 return EC;
139 if (Present.intersects(Deleted))
140 return make_error(raw_error_code::corrupt_file,
141 "Present bit vector interesects deleted!");
142
143 for (uint32_t P : Present) {
144 if (auto EC = Stream.readInteger(Buckets[P].first))
145 return EC;
146 const ValueT *Value;
147 if (auto EC = Stream.readObject(Value))
148 return EC;
149 Buckets[P].second = *Value;
150 }
151
152 return Error::success();
153 }
154
155 uint32_t calculateSerializedLength() const {
156 uint32_t Size = sizeof(Header);
157
158 int NumBitsP = Present.find_last() + 1;
159 int NumBitsD = Deleted.find_last() + 1;
160
161 // Present bit set number of words, followed by that many actual words.
162 Size += sizeof(uint32_t);
163 Size += alignTo(NumBitsP, sizeof(uint32_t));
164
165 // Deleted bit set number of words, followed by that many actual words.
166 Size += sizeof(uint32_t);
167 Size += alignTo(NumBitsD, sizeof(uint32_t));
168
169 // One (Key, ValueT) pair for each entry Present.
170 Size += (sizeof(uint32_t) + sizeof(ValueT)) * size();
171
172 return Size;
173 }
174
175 Error commit(BinaryStreamWriter &Writer) const {
176 Header H;
177 H.Size = size();
178 H.Capacity = capacity();
179 if (auto EC = Writer.writeObject(H))
180 return EC;
181
182 if (auto EC = writeSparseBitVector(Writer, Present))
183 return EC;
184
185 if (auto EC = writeSparseBitVector(Writer, Deleted))
186 return EC;
187
188 for (const auto &Entry : *this) {
189 if (auto EC = Writer.writeInteger(Entry.first))
190 return EC;
191 if (auto EC = Writer.writeObject(Entry.second))
192 return EC;
193 }
194 return Error::success();
195 }
196
197 void clear() {
198 Buckets.resize(8);
199 Present.clear();
200 Deleted.clear();
201 }
202
203 uint32_t capacity() const { return Buckets.size(); }
204 uint32_t size() const { return Present.count(); }
205
206 iterator begin() const { return iterator(*this); }
207 iterator end() const { return iterator(*this, 0, true); }
83208
84209 /// Find the entry whose key has the specified hash value, using the specified
85210 /// traits defining hash function and equality.
86 template
87 HashTableIterator find_as(const Key &K, const Context &Ctx) const {
88 uint32_t H = Traits::hash(K, Ctx) % capacity();
211 template iterator find_as(const Key &K) const {
212 uint32_t H = Traits.hashLookupKey(K) % capacity();
89213 uint32_t I = H;
90214 Optional FirstUnused;
91215 do {
92216 if (isPresent(I)) {
93 if (Traits::realKey(Buckets[I].first, Ctx) == K)
94 return HashTableIterator(*this, I, false);
217 if (Traits.storageKeyToLookupKey(Buckets[I].first) == K)
218 return iterator(*this, I, false);
95219 } else {
96220 if (!FirstUnused)
97221 FirstUnused = I;
110234 // table were Present. But this would violate the load factor constraints
111235 // that we impose, so it should never happen.
112236 assert(FirstUnused);
113 return HashTableIterator(*this, *FirstUnused, true);
114 }
115
116 /// Set the entry with the specified key to the specified value.
117 void set(uint32_t K, uint32_t V);
237 return iterator(*this, *FirstUnused, true);
238 }
118239
119240 /// Set the entry using a key type that the specified Traits can convert
120241 /// from a real key to an internal key.
121 template
122 bool set_as(const Key &K, uint32_t V, Context &Ctx) {
123 return set_as_internal(K, V, None, Ctx);
124 }
125
126 void remove(uint32_t K);
127
128 template
129 void remove_as(const Key &K, Context &Ctx) {
130 auto Iter = find_as(K, Ctx);
131 // It wasn't here to begin with, just exit.
132 if (Iter == end())
133 return;
134
135 assert(Present.test(Iter.index()));
136 assert(!Deleted.test(Iter.index()));
137 Deleted.set(Iter.index());
138 Present.reset(Iter.index());
139 }
140
141 uint32_t get(uint32_t K);
242 template bool set_as(const Key &K, ValueT V) {
243 return set_as_internal(K, std::move(V), None);
244 }
245
246 template ValueT get(const Key &K) const {
247 auto Iter = find_as(K);
248 assert(Iter != end());
249 return (*Iter).second;
250 }
142251
143252 protected:
144253 bool isPresent(uint32_t K) const { return Present.test(K); }
145254 bool isDeleted(uint32_t K) const { return Deleted.test(K); }
146255
256 TraitsT Traits;
147257 BucketList Buckets;
148258 mutable SparseBitVector<> Present;
149259 mutable SparseBitVector<> Deleted;
151261 private:
152262 /// Set the entry using a key type that the specified Traits can convert
153263 /// from a real key to an internal key.
154 template
155 bool set_as_internal(const Key &K, uint32_t V, Optional InternalKey,
156 Context &Ctx) {
157 auto Entry = find_as(K, Ctx);
264 template
265 bool set_as_internal(const Key &K, ValueT V, Optional InternalKey) {
266 auto Entry = find_as(K);
158267 if (Entry != end()) {
159268 assert(isPresent(Entry.index()));
160 assert(Traits::realKey(Buckets[Entry.index()].first, Ctx) == K);
269 assert(Traits.storageKeyToLookupKey(Buckets[Entry.index()].first) == K);
161270 // We're updating, no need to do anything special.
162271 Buckets[Entry.index()].second = V;
163272 return false;
166275 auto &B = Buckets[Entry.index()];
167276 assert(!isPresent(Entry.index()));
168277 assert(Entry.isEnd());
169 B.first = InternalKey ? *InternalKey : Traits::lowerKey(K, Ctx);
278 B.first = InternalKey ? *InternalKey : Traits.lookupKeyToStorageKey(K);
170279 B.second = V;
171280 Present.set(Entry.index());
172281 Deleted.reset(Entry.index());
173282
174 grow(Ctx);
175
176 assert((find_as(K, Ctx)) != end());
283 grow();
284
285 assert((find_as(K)) != end());
177286 return true;
178287 }
179288
180 static uint32_t maxLoad(uint32_t capacity);
181
182 template
183 void grow(Context &Ctx) {
289 static uint32_t maxLoad(uint32_t capacity) { return capacity * 2 / 3 + 1; }
290
291 void grow() {
184292 uint32_t S = size();
185293 if (S < maxLoad(capacity()))
186294 return;
192300 // Growing requires rebuilding the table and re-hashing every item. Make a
193301 // copy with a larger capacity, insert everything into the copy, then swap
194302 // it in.
195 HashTable NewMap(NewCapacity);
303 HashTable NewMap(NewCapacity, Traits);
196304 for (auto I : Present) {
197 auto RealKey = Traits::realKey(Buckets[I].first, Ctx);
198 NewMap.set_as_internal(RealKey, Buckets[I].second,
199 Buckets[I].first, Ctx);
305 auto LookupKey = Traits.storageKeyToLookupKey(Buckets[I].first);
306 NewMap.set_as_internal(LookupKey, Buckets[I].second, Buckets[I].first);
200307 }
201308
202309 Buckets.swap(NewMap.Buckets);
205312 assert(capacity() == NewCapacity);
206313 assert(size() == S);
207314 }
208
209 static Error readSparseBitVector(BinaryStreamReader &Stream,
210 SparseBitVector<> &V);
211 static Error writeSparseBitVector(BinaryStreamWriter &Writer,
212 SparseBitVector<> &Vec);
213315 };
214316
317 Error readSparseBitVector(BinaryStreamReader &Stream, SparseBitVector<> &V);
318 Error writeSparseBitVector(BinaryStreamWriter &Writer, SparseBitVector<> &Vec);
319
215320 } // end namespace pdb
216321
217322 } // end namespace llvm
2424
2525 namespace pdb {
2626
27 class NamedStreamMap;
28
29 struct NamedStreamMapTraits {
30 NamedStreamMap *NS;
31
32 explicit NamedStreamMapTraits(NamedStreamMap &NS);
33 uint16_t hashLookupKey(StringRef S) const;
34 StringRef storageKeyToLookupKey(uint32_t Offset) const;
35 uint32_t lookupKeyToStorageKey(StringRef S);
36 };
37
2738 class NamedStreamMap {
2839 friend class NamedStreamMapBuilder;
2940
4556 StringMap entries() const;
4657
4758 private:
59 NamedStreamMapTraits HashTraits;
4860 /// Closed hash table from Offset -> StreamNumber, where Offset is the offset
4961 /// of the stream name in NamesBuffer.
50 HashTable OffsetIndexMap;
62 HashTable OffsetIndexMap;
5163
5264 /// Buffer of string data.
5365 std::vector NamesBuffer;
5050 uint32_t getNumHashBuckets() const;
5151 FixedStreamArray getHashValues() const;
5252 FixedStreamArray getTypeIndexOffsets() const;
53 HashTable &getHashAdjusters();
53 HashTable &getHashAdjusters();
5454
5555 codeview::CVTypeRange types(bool *HadError) const;
5656 const codeview::CVTypeArray &typeArray() const { return TypeRecords; }
7474 std::unique_ptr HashStream;
7575 FixedStreamArray HashValues;
7676 FixedStreamArray TypeIndexOffsets;
77 HashTable HashAdjusters;
77 HashTable HashAdjusters;
7878
7979 const TpiStreamHeader *Header;
8080 };
2121 using namespace llvm;
2222 using namespace llvm::pdb;
2323
24 namespace {
25 struct IdentityTraits {
26 static uint32_t hash(uint32_t K, const HashTable &Ctx) { return K; }
27 static uint32_t realKey(uint32_t K, const HashTable &Ctx) { return K; }
28 static uint32_t lowerKey(uint32_t K, const HashTable &Ctx) { return K; }
29 };
30 } // namespace
31
32 HashTable::HashTable() : HashTable(8) {}
33
34 HashTable::HashTable(uint32_t Capacity) { Buckets.resize(Capacity); }
35
36 Error HashTable::load(BinaryStreamReader &Stream) {
37 const Header *H;
38 if (auto EC = Stream.readObject(H))
39 return EC;
40 if (H->Capacity == 0)
41 return make_error(raw_error_code::corrupt_file,
42 "Invalid Hash Table Capacity");
43 if (H->Size > maxLoad(H->Capacity))
44 return make_error(raw_error_code::corrupt_file,
45 "Invalid Hash Table Size");
46
47 Buckets.resize(H->Capacity);
48
49 if (auto EC = readSparseBitVector(Stream, Present))
50 return EC;
51 if (Present.count() != H->Size)
52 return make_error(raw_error_code::corrupt_file,
53 "Present bit vector does not match size!");
54
55 if (auto EC = readSparseBitVector(Stream, Deleted))
56 return EC;
57 if (Present.intersects(Deleted))
58 return make_error(raw_error_code::corrupt_file,
59 "Present bit vector interesects deleted!");
60
61 for (uint32_t P : Present) {
62 if (auto EC = Stream.readInteger(Buckets[P].first))
63 return EC;
64 if (auto EC = Stream.readInteger(Buckets[P].second))
65 return EC;
66 }
67
68 return Error::success();
69 }
70
71 uint32_t HashTable::calculateSerializedLength() const {
72 uint32_t Size = sizeof(Header);
73
74 int NumBitsP = Present.find_last() + 1;
75 int NumBitsD = Deleted.find_last() + 1;
76
77 // Present bit set number of words, followed by that many actual words.
78 Size += sizeof(uint32_t);
79 Size += alignTo(NumBitsP, sizeof(uint32_t));
80
81 // Deleted bit set number of words, followed by that many actual words.
82 Size += sizeof(uint32_t);
83 Size += alignTo(NumBitsD, sizeof(uint32_t));
84
85 // One (Key, Value) pair for each entry Present.
86 Size += 2 * sizeof(uint32_t) * size();
87
88 return Size;
89 }
90
91 Error HashTable::commit(BinaryStreamWriter &Writer) const {
92 Header H;
93 H.Size = size();
94 H.Capacity = capacity();
95 if (auto EC = Writer.writeObject(H))
96 return EC;
97
98 if (auto EC = writeSparseBitVector(Writer, Present))
99 return EC;
100
101 if (auto EC = writeSparseBitVector(Writer, Deleted))
102 return EC;
103
104 for (const auto &Entry : *this) {
105 if (auto EC = Writer.writeInteger(Entry.first))
106 return EC;
107 if (auto EC = Writer.writeInteger(Entry.second))
108 return EC;
109 }
110 return Error::success();
111 }
112
113 void HashTable::clear() {
114 Buckets.resize(8);
115 Present.clear();
116 Deleted.clear();
117 }
118
119 uint32_t HashTable::capacity() const { return Buckets.size(); }
120
121 uint32_t HashTable::size() const { return Present.count(); }
122
123 HashTableIterator HashTable::begin() const { return HashTableIterator(*this); }
124
125 HashTableIterator HashTable::end() const {
126 return HashTableIterator(*this, 0, true);
127 }
128
129 HashTableIterator HashTable::find(uint32_t K) const {
130 return find_as(K, *this);
131 }
132
133 void HashTable::set(uint32_t K, uint32_t V) {
134 set_as(K, V, *this);
135 }
136
137 void HashTable::remove(uint32_t K) { remove_as(K, *this); }
138
139 uint32_t HashTable::get(uint32_t K) {
140 auto I = find(K);
141 assert(I != end());
142 return (*I).second;
143 }
144
145 uint32_t HashTable::maxLoad(uint32_t capacity) { return capacity * 2 / 3 + 1; }
146
147 Error HashTable::readSparseBitVector(BinaryStreamReader &Stream,
24 Error llvm::pdb::readSparseBitVector(BinaryStreamReader &Stream,
14825 SparseBitVector<> &V) {
14926 uint32_t NumWords;
15027 if (auto EC = Stream.readInteger(NumWords))
16643 return Error::success();
16744 }
16845
169 Error HashTable::writeSparseBitVector(BinaryStreamWriter &Writer,
46 Error llvm::pdb::writeSparseBitVector(BinaryStreamWriter &Writer,
17047 SparseBitVector<> &Vec) {
17148 int ReqBits = Vec.find_last() + 1;
17249 uint32_t NumWords = alignTo(ReqBits, sizeof(uint32_t)) / sizeof(uint32_t);
19067 }
19168 return Error::success();
19269 }
193
194 HashTableIterator::HashTableIterator(const HashTable &Map, uint32_t Index,
195 bool IsEnd)
196 : Map(&Map), Index(Index), IsEnd(IsEnd) {}
197
198 HashTableIterator::HashTableIterator(const HashTable &Map) : Map(&Map) {
199 int I = Map.Present.find_first();
200 if (I == -1) {
201 Index = 0;
202 IsEnd = true;
203 } else {
204 Index = static_cast(I);
205 IsEnd = false;
206 }
207 }
208
209 HashTableIterator &HashTableIterator::operator=(const HashTableIterator &R) {
210 Map = R.Map;
211 return *this;
212 }
213
214 bool HashTableIterator::operator==(const HashTableIterator &R) const {
215 if (IsEnd && R.IsEnd)
216 return true;
217 if (IsEnd != R.IsEnd)
218 return false;
219
220 return (Map == R.Map) && (Index == R.Index);
221 }
222
223 const std::pair &HashTableIterator::operator*() const {
224 assert(Map->Present.test(Index));
225 return Map->Buckets[Index];
226 }
227
228 HashTableIterator &HashTableIterator::operator++() {
229 while (Index < Map->Buckets.size()) {
230 ++Index;
231 if (Map->Present.test(Index))
232 return *this;
233 }
234
235 IsEnd = true;
236 return *this;
237 }
2626 using namespace llvm;
2727 using namespace llvm::pdb;
2828
29 namespace {
30 struct NamedStreamMapTraits {
31 static uint16_t hash(StringRef S, const NamedStreamMap &NS) {
32 // In the reference implementation, this uses
33 // HASH Hasher::hashPbCb(PB pb, size_t cb, ULONG ulMod).
34 // Here, the type HASH is a typedef of unsigned short.
35 // ** It is not a bug that we truncate the result of hashStringV1, in fact
36 // it is a bug if we do not! **
37 return static_cast(hashStringV1(S));
38 }
39 static StringRef realKey(uint32_t Offset, const NamedStreamMap &NS) {
40 return NS.getString(Offset);
41 }
42 static uint32_t lowerKey(StringRef S, NamedStreamMap &NS) {
43 return NS.appendStringData(S);
44 }
45 };
46 } // namespace
29 NamedStreamMapTraits::NamedStreamMapTraits(NamedStreamMap &NS) : NS(&NS) {}
4730
48 NamedStreamMap::NamedStreamMap() {}
31 uint16_t NamedStreamMapTraits::hashLookupKey(StringRef S) const {
32 // In the reference implementation, this uses
33 // HASH Hasher::hashPbCb(PB pb, size_t cb, ULONG ulMod).
34 // Here, the type HASH is a typedef of unsigned short.
35 // ** It is not a bug that we truncate the result of hashStringV1, in fact
36 // it is a bug if we do not! **
37 return static_cast(hashStringV1(S));
38 }
39
40 StringRef NamedStreamMapTraits::storageKeyToLookupKey(uint32_t Offset) const {
41 return NS->getString(Offset);
42 }
43
44 uint32_t NamedStreamMapTraits::lookupKeyToStorageKey(StringRef S) {
45 return NS->appendStringData(S);
46 }
47
48 NamedStreamMap::NamedStreamMap()
49 : HashTraits(*this), OffsetIndexMap(HashTraits) {}
4950
5051 Error NamedStreamMap::load(BinaryStreamReader &Stream) {
5152 uint32_t StringBufferSize;
9798 }
9899
99100 bool NamedStreamMap::get(StringRef Stream, uint32_t &StreamNo) const {
100 auto Iter = OffsetIndexMap.find_as(Stream, *this);
101 auto Iter = OffsetIndexMap.find_as(Stream);
101102 if (Iter == OffsetIndexMap.end())
102103 return false;
103104 StreamNo = (*Iter).second;
121122 }
122123
123124 void NamedStreamMap::set(StringRef Stream, uint32_t StreamNo) {
124 OffsetIndexMap.set_as(Stream, StreamNo, *this);
125 OffsetIndexMap.set_as(Stream, support::ulittle32_t(StreamNo));
125126 }
151151 return TypeIndexOffsets;
152152 }
153153
154 HashTable &TpiStream::getHashAdjusters() { return HashAdjusters; }
154 HashTable &TpiStream::getHashAdjusters() {
155 return HashAdjusters;
156 }
155157
156158 CVTypeRange TpiStream::types(bool *HadError) const {
157159 return make_range(TypeRecords.begin(HadError), TypeRecords.end());
124124
125125 const auto &Collisions = CollisionsIter->second;
126126 outs() << TypeName << "\n";
127 outs() << formatv(" [HEAD] {0:x} {1} {2}\n", A.second,
127 outs() << formatv(" [HEAD] {0:x} {1} {2}\n", uint32_t(A.second),
128128 getLeafTypeName(HeadRecord.Type), TypeName);
129129 for (const auto &Chain : Collisions) {
130130 if (Chain.TI == TI)
77 //===----------------------------------------------------------------------===//
88
99 #include "llvm/DebugInfo/PDB/Native/HashTable.h"
10
11 #include "llvm/DebugInfo/PDB/Native/Hash.h"
1012 #include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h"
13 #include "llvm/Support/Allocator.h"
1114 #include "llvm/Support/BinaryByteStream.h"
1215 #include "llvm/Support/BinaryStreamReader.h"
1316 #include "llvm/Support/BinaryStreamWriter.h"
17 #include "llvm/Support/StringSaver.h"
1418 #include "llvm/Testing/Support/Error.h"
1519
1620 #include "gtest/gtest.h"
2226 using namespace llvm::support;
2327
2428 namespace {
25 class HashTableInternals : public HashTable {
29
30 class HashTableInternals : public HashTable {
2631 public:
2732 using HashTable::Buckets;
2833 using HashTable::Present;
3136 }
3237
3338 TEST(HashTableTest, TestSimple) {
34 HashTable Table;
35 EXPECT_EQ(0u, Table.size());
36 EXPECT_GT(Table.capacity(), 0u);
37
38 Table.set(3, 7);
39 HashTableInternals Table;
40 EXPECT_EQ(0u, Table.size());
41 EXPECT_GT(Table.capacity(), 0u);
42
43 Table.set_as(3, 7);
3944 EXPECT_EQ(1u, Table.size());
40 ASSERT_NE(Table.end(), Table.find(3));
45 ASSERT_NE(Table.end(), Table.find_as(3));
4146 EXPECT_EQ(7u, Table.get(3));
4247 }
4348
4449 TEST(HashTableTest, TestCollision) {
45 HashTable Table;
50 HashTableInternals Table;
4651 EXPECT_EQ(0u, Table.size());
4752 EXPECT_GT(Table.capacity(), 0u);
4853
5257 uint32_t N1 = Table.capacity() + 1;
5358 uint32_t N2 = 2 * N1;
5459
55 Table.set(N1, 7);
56 Table.set(N2, 12);
60 Table.set_as(N1, 7);
61 Table.set_as(N2, 12);
5762 EXPECT_EQ(2u, Table.size());
58 ASSERT_NE(Table.end(), Table.find(N1));
59 ASSERT_NE(Table.end(), Table.find(N2));
63 ASSERT_NE(Table.end(), Table.find_as(N1));
64 ASSERT_NE(Table.end(), Table.find_as(N2));
6065
6166 EXPECT_EQ(7u, Table.get(N1));
6267 EXPECT_EQ(12u, Table.get(N2));
6368 }
6469
6570 TEST(HashTableTest, TestRemove) {
66 HashTable Table;
67 EXPECT_EQ(0u, Table.size());
68 EXPECT_GT(Table.capacity(), 0u);
69
70 Table.set(1, 2);
71 Table.set(3, 4);
71 HashTableInternals Table;
72 EXPECT_EQ(0u, Table.size());
73 EXPECT_GT(Table.capacity(), 0u);
74
75 Table.set_as(1, 2);
76 Table.set_as(3, 4);
7277 EXPECT_EQ(2u, Table.size());
73 ASSERT_NE(Table.end(), Table.find(1));
74 ASSERT_NE(Table.end(), Table.find(3));
78 ASSERT_NE(Table.end(), Table.find_as(1));
79 ASSERT_NE(Table.end(), Table.find_as(3));
7580
7681 EXPECT_EQ(2u, Table.get(1));
7782 EXPECT_EQ(4u, Table.get(3));
78
79 Table.remove(1u);
80 EXPECT_EQ(1u, Table.size());
81 EXPECT_EQ(Table.end(), Table.find(1));
82 ASSERT_NE(Table.end(), Table.find(3));
83 EXPECT_EQ(4u, Table.get(3));
8483 }
8584
8685 TEST(HashTableTest, TestCollisionAfterMultipleProbes) {
87 HashTable Table;
86 HashTableInternals Table;
8887 EXPECT_EQ(0u, Table.size());
8988 EXPECT_GT(Table.capacity(), 0u);
9089
9594 uint32_t N2 = N1 + 1;
9695 uint32_t N3 = 2 * N1;
9796
98 Table.set(N1, 7);
99 Table.set(N2, 11);
100 Table.set(N3, 13);
97 Table.set_as(N1, 7);
98 Table.set_as(N2, 11);
99 Table.set_as(N3, 13);
101100 EXPECT_EQ(3u, Table.size());
102 ASSERT_NE(Table.end(), Table.find(N1));
103 ASSERT_NE(Table.end(), Table.find(N2));
104 ASSERT_NE(Table.end(), Table.find(N3));
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));
105104
106105 EXPECT_EQ(7u, Table.get(N1));
107106 EXPECT_EQ(11u, Table.get(N2));
108107 EXPECT_EQ(13u, Table.get(N3));
109
110 // Remove the one that had been filled in the middle, then insert another one
111 // with a collision. It should fill the newly emptied slot.
112 Table.remove(N2);
113 uint32_t N4 = N1 * 3;
114 Table.set(N4, 17);
115 EXPECT_EQ(3u, Table.size());
116 ASSERT_NE(Table.end(), Table.find(N1));
117 ASSERT_NE(Table.end(), Table.find(N3));
118 ASSERT_NE(Table.end(), Table.find(N4));
119
120 EXPECT_EQ(7u, Table.get(N1));
121 EXPECT_EQ(13u, Table.get(N3));
122 EXPECT_EQ(17u, Table.get(N4));
123108 }
124109
125110 TEST(HashTableTest, Grow) {
127112 // guaranteed to trigger a grow. Then verify that the size is the same, the
128113 // capacity is larger, and all the original items are still in the table.
129114
130 HashTable Table;
115 HashTableInternals Table;
131116 uint32_t OldCapacity = Table.capacity();
132117 for (uint32_t I = 0; I < OldCapacity; ++I) {
133 Table.set(OldCapacity + I * 2 + 1, I * 2 + 3);
118 Table.set_as(OldCapacity + I * 2 + 1, I * 2 + 3);
134119 }
135120 EXPECT_EQ(OldCapacity, Table.size());
136121 EXPECT_GT(Table.capacity(), OldCapacity);
137122 for (uint32_t I = 0; I < OldCapacity; ++I) {
138 ASSERT_NE(Table.end(), Table.find(OldCapacity + I * 2 + 1));
123 ASSERT_NE(Table.end(), Table.find_as(OldCapacity + I * 2 + 1));
139124 EXPECT_EQ(I * 2 + 3, Table.get(OldCapacity + I * 2 + 1));
140125 }
141126 }
144129 HashTableInternals Table;
145130 uint32_t Cap = Table.capacity();
146131 for (uint32_t I = 0; I < Cap; ++I) {
147 Table.set(Cap + I * 2 + 1, I * 2 + 3);
132 Table.set_as(Cap + I * 2 + 1, I * 2 + 3);
148133 }
149134
150135 std::vector Buffer(Table.calculateSerializedLength());
206191 EXPECT_EQ(7U, N);
207192 } while (std::next_permutation(Streams.begin(), Streams.end()));
208193 }
194
195 namespace {
196 struct FooBar {
197 std::string S;
198 uint32_t X;
199 uint32_t Y;
200 double Z;
201 };
202
203 } // namespace
204
205 namespace llvm {
206 namespace pdb {
207 template <> struct PdbHashTraits {
208 std::vector Buffer;
209
210 PdbHashTraits() { Buffer.push_back(0); }
211
212 uint32_t hashLookupKey(StringRef S) const {
213 return llvm::pdb::hashStringV1(S);
214 }
215
216 StringRef storageKeyToLookupKey(uint32_t N) const {
217 if (N >= Buffer.size())
218 return StringRef();
219
220 return StringRef(Buffer.data() + N);
221 }
222
223 uint32_t lookupKeyToStorageKey(StringRef S) {
224 uint32_t N = Buffer.size();
225 Buffer.insert(Buffer.end(), S.begin(), S.end());
226 Buffer.push_back('\0');
227 return N;
228 }
229 };
230 } // namespace pdb
231 } // namespace llvm
232
233 TEST(HashTableTest, NonTrivialValueType) {
234 HashTable Table;
235 uint32_t Cap = Table.capacity();
236 for (uint32_t I = 0; I < Cap; ++I) {
237 FooBar F;
238 F.S = utostr(I);
239 F.X = I;
240 F.Y = I + 1;
241 F.Z = static_cast(I + 2);
242 Table.set_as(utostr(I), F);
243 }
244
245 std::vector Buffer(Table.calculateSerializedLength());
246 MutableBinaryByteStream Stream(Buffer, little);
247 BinaryStreamWriter Writer(Stream);
248 EXPECT_THAT_ERROR(Table.commit(Writer), Succeeded());
249 // We should have written precisely the number of bytes we calculated earlier.
250 EXPECT_EQ(Buffer.size(), Writer.getOffset());
251
252 HashTable Table2;
253 BinaryStreamReader Reader(Stream);
254 EXPECT_THAT_ERROR(Table2.load(Reader), Succeeded());
255 // We should have read precisely the number of bytes we calculated earlier.
256 EXPECT_EQ(Buffer.size(), Reader.getOffset());
257
258 EXPECT_EQ(Table.size(), Table2.size());
259 EXPECT_EQ(Table.capacity(), Table2.capacity());
260 // EXPECT_EQ(Table.Buckets, Table2.Buckets);
261 // EXPECT_EQ(Table.Present, Table2.Present);
262 // EXPECT_EQ(Table.Deleted, Table2.Deleted);
263 }