1 //===- HashTable.h - PDB Hash Table -----------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #ifndef LLVM_DEBUGINFO_PDB_NATIVE_HASHTABLE_H
11 #define LLVM_DEBUGINFO_PDB_NATIVE_HASHTABLE_H
13 #include "llvm/ADT/SparseBitVector.h"
14 #include "llvm/ADT/iterator.h"
15 #include "llvm/DebugInfo/PDB/Native/RawError.h"
16 #include "llvm/Support/BinaryStreamReader.h"
17 #include "llvm/Support/BinaryStreamWriter.h"
18 #include "llvm/Support/Endian.h"
19 #include "llvm/Support/Error.h"
27 class BinaryStreamReader;
28 class BinaryStreamWriter;
32 Error readSparseBitVector(BinaryStreamReader &Stream, SparseBitVector<> &V);
33 Error writeSparseBitVector(BinaryStreamWriter &Writer, SparseBitVector<> &Vec);
35 template <typename ValueT, typename TraitsT> class HashTable;
37 template <typename ValueT, typename TraitsT>
38 class HashTableIterator
39 : public iterator_facade_base<HashTableIterator<ValueT, TraitsT>,
40 std::forward_iterator_tag,
41 std::pair<uint32_t, ValueT>> {
42 friend HashTable<ValueT, TraitsT>;
44 HashTableIterator(const HashTable<ValueT, TraitsT> &Map, uint32_t Index,
46 : Map(&Map), Index(Index), IsEnd(IsEnd) {}
49 HashTableIterator(const HashTable<ValueT, TraitsT> &Map) : Map(&Map) {
50 int I = Map.Present.find_first();
55 Index = static_cast<uint32_t>(I);
60 HashTableIterator &operator=(const HashTableIterator &R) {
64 bool operator==(const HashTableIterator &R) const {
70 return (Map == R.Map) && (Index == R.Index);
72 const std::pair<uint32_t, ValueT> &operator*() const {
73 assert(Map->Present.test(Index));
74 return Map->Buckets[Index];
76 HashTableIterator &operator++() {
77 while (Index < Map->Buckets.size()) {
79 if (Map->Present.test(Index))
88 bool isEnd() const { return IsEnd; }
89 uint32_t index() const { return Index; }
91 const HashTable<ValueT, TraitsT> *Map;
96 template <typename T> struct PdbHashTraits {};
98 template <> struct PdbHashTraits<uint32_t> {
99 uint32_t hashLookupKey(uint32_t N) const { return N; }
100 uint32_t storageKeyToLookupKey(uint32_t N) const { return N; }
101 uint32_t lookupKeyToStorageKey(uint32_t N) { return N; }
104 template <typename ValueT, typename TraitsT = PdbHashTraits<ValueT>>
106 using iterator = HashTableIterator<ValueT, TraitsT>;
110 support::ulittle32_t Size;
111 support::ulittle32_t Capacity;
114 using BucketList = std::vector<std::pair<uint32_t, ValueT>>;
117 HashTable() { Buckets.resize(8); }
119 explicit HashTable(TraitsT Traits) : HashTable(8, std::move(Traits)) {}
120 HashTable(uint32_t Capacity, TraitsT Traits) : Traits(Traits) {
121 Buckets.resize(Capacity);
124 Error load(BinaryStreamReader &Stream) {
126 if (auto EC = Stream.readObject(H))
128 if (H->Capacity == 0)
129 return make_error<RawError>(raw_error_code::corrupt_file,
130 "Invalid Hash Table Capacity");
131 if (H->Size > maxLoad(H->Capacity))
132 return make_error<RawError>(raw_error_code::corrupt_file,
133 "Invalid Hash Table Size");
135 Buckets.resize(H->Capacity);
137 if (auto EC = readSparseBitVector(Stream, Present))
139 if (Present.count() != H->Size)
140 return make_error<RawError>(raw_error_code::corrupt_file,
141 "Present bit vector does not match size!");
143 if (auto EC = readSparseBitVector(Stream, Deleted))
145 if (Present.intersects(Deleted))
146 return make_error<RawError>(raw_error_code::corrupt_file,
147 "Present bit vector interesects deleted!");
149 for (uint32_t P : Present) {
150 if (auto EC = Stream.readInteger(Buckets[P].first))
153 if (auto EC = Stream.readObject(Value))
155 Buckets[P].second = *Value;
158 return Error::success();
161 uint32_t calculateSerializedLength() const {
162 uint32_t Size = sizeof(Header);
164 constexpr int BitsPerWord = 8 * sizeof(uint32_t);
166 int NumBitsP = Present.find_last() + 1;
167 int NumBitsD = Deleted.find_last() + 1;
169 uint32_t NumWordsP = alignTo(NumBitsP, BitsPerWord) / BitsPerWord;
170 uint32_t NumWordsD = alignTo(NumBitsD, BitsPerWord) / BitsPerWord;
172 // Present bit set number of words (4 bytes), followed by that many actual
173 // words (4 bytes each).
174 Size += sizeof(uint32_t);
175 Size += NumWordsP * sizeof(uint32_t);
177 // Deleted bit set number of words (4 bytes), followed by that many actual
178 // words (4 bytes each).
179 Size += sizeof(uint32_t);
180 Size += NumWordsD * sizeof(uint32_t);
182 // One (Key, ValueT) pair for each entry Present.
183 Size += (sizeof(uint32_t) + sizeof(ValueT)) * size();
188 Error commit(BinaryStreamWriter &Writer) const {
191 H.Capacity = capacity();
192 if (auto EC = Writer.writeObject(H))
195 if (auto EC = writeSparseBitVector(Writer, Present))
198 if (auto EC = writeSparseBitVector(Writer, Deleted))
201 for (const auto &Entry : *this) {
202 if (auto EC = Writer.writeInteger(Entry.first))
204 if (auto EC = Writer.writeObject(Entry.second))
207 return Error::success();
216 bool empty() const { return size() == 0; }
217 uint32_t capacity() const { return Buckets.size(); }
218 uint32_t size() const { return Present.count(); }
220 iterator begin() const { return iterator(*this); }
221 iterator end() const { return iterator(*this, 0, true); }
223 /// Find the entry whose key has the specified hash value, using the specified
224 /// traits defining hash function and equality.
225 template <typename Key> iterator find_as(const Key &K) const {
226 uint32_t H = Traits.hashLookupKey(K) % capacity();
228 Optional<uint32_t> FirstUnused;
231 if (Traits.storageKeyToLookupKey(Buckets[I].first) == K)
232 return iterator(*this, I, false);
236 // Insertion occurs via linear probing from the slot hint, and will be
237 // inserted at the first empty / deleted location. Therefore, if we are
238 // probing and find a location that is neither present nor deleted, then
239 // nothing must have EVER been inserted at this location, and thus it is
240 // not possible for a matching value to occur later.
244 I = (I + 1) % capacity();
247 // The only way FirstUnused would not be set is if every single entry in the
248 // table were Present. But this would violate the load factor constraints
249 // that we impose, so it should never happen.
251 return iterator(*this, *FirstUnused, true);
254 /// Set the entry using a key type that the specified Traits can convert
255 /// from a real key to an internal key.
256 template <typename Key> bool set_as(const Key &K, ValueT V) {
257 return set_as_internal(K, std::move(V), None);
260 template <typename Key> ValueT get(const Key &K) const {
261 auto Iter = find_as(K);
262 assert(Iter != end());
263 return (*Iter).second;
267 bool isPresent(uint32_t K) const { return Present.test(K); }
268 bool isDeleted(uint32_t K) const { return Deleted.test(K); }
272 mutable SparseBitVector<> Present;
273 mutable SparseBitVector<> Deleted;
276 /// Set the entry using a key type that the specified Traits can convert
277 /// from a real key to an internal key.
278 template <typename Key>
279 bool set_as_internal(const Key &K, ValueT V, Optional<uint32_t> InternalKey) {
280 auto Entry = find_as(K);
281 if (Entry != end()) {
282 assert(isPresent(Entry.index()));
283 assert(Traits.storageKeyToLookupKey(Buckets[Entry.index()].first) == K);
284 // We're updating, no need to do anything special.
285 Buckets[Entry.index()].second = V;
289 auto &B = Buckets[Entry.index()];
290 assert(!isPresent(Entry.index()));
291 assert(Entry.isEnd());
292 B.first = InternalKey ? *InternalKey : Traits.lookupKeyToStorageKey(K);
294 Present.set(Entry.index());
295 Deleted.reset(Entry.index());
299 assert((find_as(K)) != end());
303 static uint32_t maxLoad(uint32_t capacity) { return capacity * 2 / 3 + 1; }
307 uint32_t MaxLoad = maxLoad(capacity());
308 if (S < maxLoad(capacity()))
310 assert(capacity() != UINT32_MAX && "Can't grow Hash table!");
312 uint32_t NewCapacity = (capacity() <= INT32_MAX) ? MaxLoad * 2 : UINT32_MAX;
314 // Growing requires rebuilding the table and re-hashing every item. Make a
315 // copy with a larger capacity, insert everything into the copy, then swap
317 HashTable NewMap(NewCapacity, Traits);
318 for (auto I : Present) {
319 auto LookupKey = Traits.storageKeyToLookupKey(Buckets[I].first);
320 NewMap.set_as_internal(LookupKey, Buckets[I].second, Buckets[I].first);
323 Buckets.swap(NewMap.Buckets);
324 std::swap(Present, NewMap.Present);
325 std::swap(Deleted, NewMap.Deleted);
326 assert(capacity() == NewCapacity);
331 } // end namespace pdb
333 } // end namespace llvm
335 #endif // LLVM_DEBUGINFO_PDB_NATIVE_HASHTABLE_H