blob: dd95c078d7e9b87eebdfbe7fd5ece356cef4a200 [file] [log] [blame]
Zachary Turner11036a92017-01-19 23:31:24 +00001//===- HashTable.cpp - PDB Hash Table ---------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Adrian McCarthy6b6b8c42017-01-25 22:38:55 +000010#include "llvm/DebugInfo/PDB/Native/HashTable.h"
Zachary Turner11036a92017-01-19 23:31:24 +000011
12#include "llvm/ADT/Optional.h"
13#include "llvm/ADT/SparseBitVector.h"
Adrian McCarthy6b6b8c42017-01-25 22:38:55 +000014#include "llvm/DebugInfo/PDB/Native/RawError.h"
Zachary Turner11036a92017-01-19 23:31:24 +000015
Zachary Turnera332fa32017-01-19 23:44:14 +000016#include <assert.h>
17
Zachary Turner11036a92017-01-19 23:31:24 +000018using namespace llvm;
19using namespace llvm::pdb;
20
21HashTable::HashTable() : HashTable(8) {}
22
23HashTable::HashTable(uint32_t Capacity) { Buckets.resize(Capacity); }
24
25Error HashTable::load(msf::StreamReader &Stream) {
26 const Header *H;
27 if (auto EC = Stream.readObject(H))
28 return EC;
29 if (H->Capacity == 0)
30 return make_error<RawError>(raw_error_code::corrupt_file,
31 "Invalid Hash Table Capacity");
32 if (H->Size > maxLoad(H->Capacity))
33 return make_error<RawError>(raw_error_code::corrupt_file,
34 "Invalid Hash Table Size");
35
36 Buckets.resize(H->Capacity);
37
38 if (auto EC = readSparseBitVector(Stream, Present))
39 return EC;
40 if (Present.count() != H->Size)
41 return make_error<RawError>(raw_error_code::corrupt_file,
42 "Present bit vector does not match size!");
43
44 if (auto EC = readSparseBitVector(Stream, Deleted))
45 return EC;
46 if (Present.intersects(Deleted))
47 return make_error<RawError>(raw_error_code::corrupt_file,
48 "Present bit vector interesects deleted!");
49
50 for (uint32_t P : Present) {
Zachary Turner181fe172017-02-18 01:35:33 +000051 if (auto EC = Stream.readInteger(Buckets[P].first, llvm::support::little))
Zachary Turner11036a92017-01-19 23:31:24 +000052 return EC;
Zachary Turner181fe172017-02-18 01:35:33 +000053 if (auto EC = Stream.readInteger(Buckets[P].second, llvm::support::little))
Zachary Turner11036a92017-01-19 23:31:24 +000054 return EC;
55 }
56
57 return Error::success();
58}
59
60uint32_t HashTable::calculateSerializedLength() const {
61 uint32_t Size = sizeof(Header);
62
63 int NumBitsP = Present.find_last() + 1;
64 int NumBitsD = Deleted.find_last() + 1;
65
66 // Present bit set number of words, followed by that many actual words.
67 Size += sizeof(uint32_t);
68 Size += alignTo(NumBitsP, sizeof(uint32_t));
69
70 // Deleted bit set number of words, followed by that many actual words.
71 Size += sizeof(uint32_t);
72 Size += alignTo(NumBitsD, sizeof(uint32_t));
73
74 // One (Key, Value) pair for each entry Present.
75 Size += 2 * sizeof(uint32_t) * size();
76
77 return Size;
78}
79
80Error HashTable::commit(msf::StreamWriter &Writer) const {
81 Header H;
82 H.Size = size();
83 H.Capacity = capacity();
84 if (auto EC = Writer.writeObject(H))
85 return EC;
86
87 if (auto EC = writeSparseBitVector(Writer, Present))
88 return EC;
89
90 if (auto EC = writeSparseBitVector(Writer, Deleted))
91 return EC;
92
93 for (const auto &Entry : *this) {
Zachary Turner181fe172017-02-18 01:35:33 +000094 if (auto EC = Writer.writeInteger(Entry.first, llvm::support::little))
Zachary Turner11036a92017-01-19 23:31:24 +000095 return EC;
Zachary Turner181fe172017-02-18 01:35:33 +000096 if (auto EC = Writer.writeInteger(Entry.second, llvm::support::little))
Zachary Turner11036a92017-01-19 23:31:24 +000097 return EC;
98 }
99 return Error::success();
100}
101
Zachary Turner60667ca2017-01-20 22:41:40 +0000102void HashTable::clear() {
103 Buckets.resize(8);
104 Present.clear();
105 Deleted.clear();
106}
107
Zachary Turner11036a92017-01-19 23:31:24 +0000108uint32_t HashTable::capacity() const { return Buckets.size(); }
109uint32_t HashTable::size() const { return Present.count(); }
110
111HashTableIterator HashTable::begin() const { return HashTableIterator(*this); }
112HashTableIterator HashTable::end() const {
113 return HashTableIterator(*this, 0, true);
114}
115
116HashTableIterator HashTable::find(uint32_t K) {
117 uint32_t H = K % capacity();
118 uint32_t I = H;
119 Optional<uint32_t> FirstUnused;
120 do {
121 if (isPresent(I)) {
122 if (Buckets[I].first == K)
123 return HashTableIterator(*this, I, false);
124 } else {
125 if (!FirstUnused)
126 FirstUnused = I;
127 // Insertion occurs via linear probing from the slot hint, and will be
128 // inserted at the first empty / deleted location. Therefore, if we are
129 // probing and find a location that is neither present nor deleted, then
130 // nothing must have EVER been inserted at this location, and thus it is
131 // not possible for a matching value to occur later.
132 if (!isDeleted(I))
133 break;
134 }
135 I = (I + 1) % capacity();
136 } while (I != H);
137
138 // The only way FirstUnused would not be set is if every single entry in the
139 // table were Present. But this would violate the load factor constraints
140 // that we impose, so it should never happen.
141 assert(FirstUnused);
142 return HashTableIterator(*this, *FirstUnused, true);
143}
144
145void HashTable::set(uint32_t K, uint32_t V) {
146 auto Entry = find(K);
147 if (Entry != end()) {
148 assert(isPresent(Entry.index()));
149 assert(Buckets[Entry.index()].first == K);
150 // We're updating, no need to do anything special.
151 Buckets[Entry.index()].second = V;
152 return;
153 }
154
155 auto &B = Buckets[Entry.index()];
156 assert(!isPresent(Entry.index()));
157 assert(Entry.isEnd());
158 B.first = K;
159 B.second = V;
160 Present.set(Entry.index());
161 Deleted.reset(Entry.index());
162
163 grow();
164
165 assert(find(K) != end());
166}
167
168void HashTable::remove(uint32_t K) {
169 auto Iter = find(K);
170 // It wasn't here to begin with, just exit.
171 if (Iter == end())
172 return;
173
174 assert(Present.test(Iter.index()));
175 assert(!Deleted.test(Iter.index()));
176 Deleted.set(Iter.index());
177 Present.reset(Iter.index());
178}
179
180uint32_t HashTable::get(uint32_t K) {
181 auto I = find(K);
182 assert(I != end());
183 return (*I).second;
184}
185
186uint32_t HashTable::maxLoad(uint32_t capacity) { return capacity * 2 / 3 + 1; }
187
188void HashTable::grow() {
189 uint32_t S = size();
190 if (S < maxLoad(capacity()))
191 return;
Zachary Turnerd54deae2017-01-19 23:41:11 +0000192 assert(capacity() != UINT32_MAX && "Can't grow Hash table!");
Zachary Turner11036a92017-01-19 23:31:24 +0000193
194 uint32_t NewCapacity =
195 (capacity() <= INT32_MAX) ? capacity() * 2 : UINT32_MAX;
196
197 // Growing requires rebuilding the table and re-hashing every item. Make a
198 // copy with a larger capacity, insert everything into the copy, then swap
199 // it in.
200 HashTable NewMap(NewCapacity);
201 for (auto I : Present) {
202 NewMap.set(Buckets[I].first, Buckets[I].second);
203 }
204
205 Buckets.swap(NewMap.Buckets);
206 std::swap(Present, NewMap.Present);
207 std::swap(Deleted, NewMap.Deleted);
208 assert(capacity() == NewCapacity);
209 assert(size() == S);
210}
211
212Error HashTable::readSparseBitVector(msf::StreamReader &Stream,
213 SparseBitVector<> &V) {
214 uint32_t NumWords;
Zachary Turner181fe172017-02-18 01:35:33 +0000215 if (auto EC = Stream.readInteger(NumWords, llvm::support::little))
Zachary Turner11036a92017-01-19 23:31:24 +0000216 return joinErrors(
217 std::move(EC),
218 make_error<RawError>(raw_error_code::corrupt_file,
219 "Expected hash table number of words"));
220
221 for (uint32_t I = 0; I != NumWords; ++I) {
222 uint32_t Word;
Zachary Turner181fe172017-02-18 01:35:33 +0000223 if (auto EC = Stream.readInteger(Word, llvm::support::little))
Zachary Turner11036a92017-01-19 23:31:24 +0000224 return joinErrors(std::move(EC),
225 make_error<RawError>(raw_error_code::corrupt_file,
226 "Expected hash table word"));
227 for (unsigned Idx = 0; Idx < 32; ++Idx)
228 if (Word & (1U << Idx))
229 V.set((I * 32) + Idx);
230 }
231 return Error::success();
232}
233
234Error HashTable::writeSparseBitVector(msf::StreamWriter &Writer,
235 SparseBitVector<> &Vec) {
236 int ReqBits = Vec.find_last() + 1;
237 uint32_t NumWords = alignTo(ReqBits, sizeof(uint32_t)) / sizeof(uint32_t);
Zachary Turner181fe172017-02-18 01:35:33 +0000238 if (auto EC = Writer.writeInteger(NumWords, llvm::support::little))
Zachary Turner11036a92017-01-19 23:31:24 +0000239 return joinErrors(
240 std::move(EC),
241 make_error<RawError>(raw_error_code::corrupt_file,
242 "Could not write linear map number of words"));
243
244 uint32_t Idx = 0;
245 for (uint32_t I = 0; I != NumWords; ++I) {
246 uint32_t Word = 0;
247 for (uint32_t WordIdx = 0; WordIdx < 32; ++WordIdx, ++Idx) {
248 if (Vec.test(Idx))
249 Word |= (1 << WordIdx);
250 }
Zachary Turner181fe172017-02-18 01:35:33 +0000251 if (auto EC = Writer.writeInteger(Word, llvm::support::little))
Zachary Turner11036a92017-01-19 23:31:24 +0000252 return joinErrors(std::move(EC), make_error<RawError>(
253 raw_error_code::corrupt_file,
254 "Could not write linear map word"));
255 }
256 return Error::success();
257}
258
259HashTableIterator::HashTableIterator(const HashTable &Map, uint32_t Index,
260 bool IsEnd)
261 : Map(&Map), Index(Index), IsEnd(IsEnd) {}
262
263HashTableIterator::HashTableIterator(const HashTable &Map) : Map(&Map) {
264 int I = Map.Present.find_first();
265 if (I == -1) {
266 Index = 0;
267 IsEnd = true;
268 } else {
269 Index = static_cast<uint32_t>(I);
270 IsEnd = false;
271 }
272}
273
274HashTableIterator &HashTableIterator::operator=(const HashTableIterator &R) {
275 Map = R.Map;
276 return *this;
277}
278
279bool HashTableIterator::operator==(const HashTableIterator &R) const {
280 if (IsEnd && R.IsEnd)
281 return true;
282 if (IsEnd != R.IsEnd)
283 return false;
284
285 return (Map == R.Map) && (Index == R.Index);
286}
287
288const std::pair<uint32_t, uint32_t> &HashTableIterator::operator*() const {
289 assert(Map->Present.test(Index));
290 return Map->Buckets[Index];
291}
292
293HashTableIterator &HashTableIterator::operator++() {
294 while (Index < Map->Buckets.size()) {
295 ++Index;
296 if (Map->Present.test(Index))
297 return *this;
298 }
299
300 IsEnd = true;
301 return *this;
302}