blob: 52afcfd2177d54a910854bc125d12982c8821b0d [file] [log] [blame]
Derek Bruening84df6be2016-08-08 17:25:40 +00001//===-- esan_hashtable.h ----------------------------------------*- 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//
10// This file is a part of EfficiencySanitizer, a family of performance tuners.
11//
12// Generic resizing hashtable.
13//===----------------------------------------------------------------------===//
14
15#include "sanitizer_common/sanitizer_allocator_internal.h"
16#include "sanitizer_common/sanitizer_internal_defs.h"
17#include "sanitizer_common/sanitizer_mutex.h"
18#include <stddef.h>
19
20namespace __esan {
21
22//===----------------------------------------------------------------------===//
23// Default hash and comparison functions
24//===----------------------------------------------------------------------===//
25
26template <typename T> struct DefaultHash {
27 size_t operator()(const T &Key) const {
28 return (size_t)Key;
29 }
30};
31
32template <typename T> struct DefaultEqual {
33 bool operator()(const T &Key1, const T &Key2) const {
34 return Key1 == Key2;
35 }
36};
37
38//===----------------------------------------------------------------------===//
39// HashTable declaration
40//===----------------------------------------------------------------------===//
41
42// A simple resizing and mutex-locked hashtable.
43//
44// If the default hash functor is used, KeyTy must have an operator size_t().
45// If the default comparison functor is used, KeyTy must have an operator ==.
46//
47// By default all operations are internally-synchronized with a mutex, with no
48// synchronization for payloads once hashtable functions return. If
49// ExternalLock is set to true, the caller should call the lock() and unlock()
50// routines around all hashtable operations and subsequent manipulation of
51// payloads.
52template <typename KeyTy, typename DataTy, bool ExternalLock = false,
53 typename HashFuncTy = DefaultHash<KeyTy>,
54 typename EqualFuncTy = DefaultEqual<KeyTy> >
55class HashTable {
56public:
57 // InitialCapacity must be a power of 2.
58 // ResizeFactor must be between 1 and 99 and indicates the
59 // maximum percentage full that the table should ever be.
60 HashTable(u32 InitialCapacity = 2048, u32 ResizeFactor = 70);
61 ~HashTable();
62 bool lookup(const KeyTy &Key, DataTy &Payload); // Const except for Mutex.
63 bool add(const KeyTy &Key, const DataTy &Payload);
64 bool remove(const KeyTy &Key);
65 u32 size(); // Const except for Mutex.
66 // If the table is internally-synchronized, this lock must not be held
67 // while a hashtable function is called as it will deadlock: the lock
68 // is not recursive. This is meant for use with externally-synchronized
69 // tables.
70 void lock();
71 void unlock();
72
73private:
74 void resize();
75
76 struct HashEntry {
77 KeyTy Key;
78 DataTy Payload;
79 HashEntry *Next;
80 };
81
82 HashEntry **Table;
83 u32 Capacity;
84 u32 Entries;
85 const u32 ResizeFactor;
86 BlockingMutex Mutex;
87 const HashFuncTy HashFunc;
88 const EqualFuncTy EqualFunc;
89};
90
91//===----------------------------------------------------------------------===//
92// Hashtable implementation
93//===----------------------------------------------------------------------===//
94
95template <typename KeyTy, typename DataTy, bool ExternalLock,
96 typename HashFuncTy, typename EqualFuncTy>
97HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::HashTable(
98 u32 InitialCapacity, u32 ResizeFactor)
99 : Capacity(InitialCapacity), Entries(0), ResizeFactor(ResizeFactor),
100 HashFunc(HashFuncTy()), EqualFunc(EqualFuncTy()) {
101 CHECK(IsPowerOfTwo(Capacity));
102 CHECK(ResizeFactor >= 1 && ResizeFactor <= 99);
103 Table = (HashEntry **)InternalAlloc(Capacity * sizeof(HashEntry *));
104 internal_memset(Table, 0, Capacity * sizeof(HashEntry *));
105}
106
107template <typename KeyTy, typename DataTy, bool ExternalLock,
108 typename HashFuncTy, typename EqualFuncTy>
109HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::~HashTable() {
110 for (u32 i = 0; i < Capacity; ++i) {
111 HashEntry *Entry = Table[i];
112 while (Entry != nullptr) {
113 HashEntry *Next = Entry->Next;
114 Entry->Payload.~DataTy();
115 InternalFree(Entry);
116 Entry = Next;
117 }
118 }
119 InternalFree(Table);
120}
121
122template <typename KeyTy, typename DataTy, bool ExternalLock,
123 typename HashFuncTy, typename EqualFuncTy>
124u32 HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::size() {
125 u32 Res;
126 if (!ExternalLock)
127 Mutex.Lock();
128 Res = Entries;
129 if (!ExternalLock)
130 Mutex.Unlock();
131 return Res;
132}
133
134template <typename KeyTy, typename DataTy, bool ExternalLock,
135 typename HashFuncTy, typename EqualFuncTy>
136bool HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::lookup(
137 const KeyTy &Key, DataTy &Payload) {
138 if (!ExternalLock)
139 Mutex.Lock();
140 bool Found = false;
141 size_t Hash = HashFunc(Key) % Capacity;
142 HashEntry *Entry = Table[Hash];
143 for (; Entry != nullptr; Entry = Entry->Next) {
144 if (EqualFunc(Entry->Key, Key)) {
145 Payload = Entry->Payload;
146 Found = true;
147 break;
148 }
149 }
150 if (!ExternalLock)
151 Mutex.Unlock();
152 return Found;
153}
154
155template <typename KeyTy, typename DataTy, bool ExternalLock,
156 typename HashFuncTy, typename EqualFuncTy>
157void HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::resize() {
158 if (!ExternalLock)
159 Mutex.CheckLocked();
160 size_t OldCapacity = Capacity;
161 HashEntry **OldTable = Table;
162 Capacity *= 2;
163 Table = (HashEntry **)InternalAlloc(Capacity * sizeof(HashEntry *));
164 internal_memset(Table, 0, Capacity * sizeof(HashEntry *));
165 // Re-hash
166 for (u32 i = 0; i < OldCapacity; ++i) {
167 HashEntry *OldEntry = OldTable[i];
168 while (OldEntry != nullptr) {
169 HashEntry *Next = OldEntry->Next;
170 size_t Hash = HashFunc(OldEntry->Key) % Capacity;
171 OldEntry->Next = Table[Hash];
172 Table[Hash] = OldEntry;
173 OldEntry = Next;
174 }
175 }
176}
177
178template <typename KeyTy, typename DataTy, bool ExternalLock,
179 typename HashFuncTy, typename EqualFuncTy>
180bool HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::add(
181 const KeyTy &Key, const DataTy &Payload) {
182 if (!ExternalLock)
183 Mutex.Lock();
184 bool Exists = false;
185 size_t Hash = HashFunc(Key) % Capacity;
186 HashEntry *Entry = Table[Hash];
187 for (; Entry != nullptr; Entry = Entry->Next) {
188 if (EqualFunc(Entry->Key, Key)) {
189 Exists = true;
190 break;
191 }
192 }
193 if (!Exists) {
194 Entries++;
195 if (Entries * 100 >= Capacity * ResizeFactor) {
196 resize();
197 Hash = HashFunc(Key) % Capacity;
198 }
199 HashEntry *Add = (HashEntry *)InternalAlloc(sizeof(*Add));
200 Add->Key = Key;
201 Add->Payload = Payload;
202 Add->Next = Table[Hash];
203 Table[Hash] = Add;
204 }
205 if (!ExternalLock)
206 Mutex.Unlock();
207 return !Exists;
208}
209
210template <typename KeyTy, typename DataTy, bool ExternalLock,
211 typename HashFuncTy, typename EqualFuncTy>
212bool HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::remove(
213 const KeyTy &Key) {
214 if (!ExternalLock)
215 Mutex.Lock();
216 bool Found = false;
217 size_t Hash = HashFunc(Key) % Capacity;
218 HashEntry *Entry = Table[Hash];
219 HashEntry *Prev = nullptr;
220 for (; Entry != nullptr; Prev = Entry, Entry = Entry->Next) {
221 if (EqualFunc(Entry->Key, Key)) {
222 Found = true;
223 Entries--;
224 if (Prev == nullptr)
225 Table[Hash] = Entry->Next;
226 else
227 Prev->Next = Entry->Next;
228 Entry->Payload.~DataTy();
229 InternalFree(Entry);
230 break;
231 }
232 }
233 if (!ExternalLock)
234 Mutex.Unlock();
235 return Found;
236}
237
238template <typename KeyTy, typename DataTy, bool ExternalLock,
239 typename HashFuncTy, typename EqualFuncTy>
240void HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::lock() {
241 Mutex.Lock();
242}
243
244template <typename KeyTy, typename DataTy, bool ExternalLock,
245 typename HashFuncTy, typename EqualFuncTy>
246void HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::unlock() {
247 Mutex.Unlock();
248}
249
250} // namespace __esan