Fix some memory leaks
[satune.git] / src / Collections / hashtable.h
1 /*      Copyright (c) 2015 Regents of the University of California
2  *
3  *      Author: Brian Demsky <bdemsky@uci.edu>
4  *
5  *      This program is free software; you can redistribute it and/or
6  *      modify it under the terms of the GNU General Public License
7  *      version 2 as published by the Free Software Foundation.
8  */
9
10 /** @file hashtable.h
11  *  @brief Hashtable.  Standard chained bucket variety.
12  */
13
14 #ifndef HASHTABLE_H__
15 #define HASHTABLE_H__
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include "mymemory.h"
21 #include "common.h"
22
23 /**
24  * @brief Hashtable node
25  *
26  * @tparam _Key    Type name for the key
27  * @tparam _Val    Type name for the values to be stored
28  */
29 template<typename _Key, typename _Val>
30 struct Hashlistnode {
31         _Key key;
32         _Val val;
33 };
34
35 template<typename _Key, int _Shift, typename _KeyInt>
36 inline unsigned int defaultHashFunction(_Key hash) {
37         return (unsigned int)(((_KeyInt)hash) >> _Shift);
38 }
39
40 template<typename _Key>
41 inline bool defaultEquals(_Key key1, _Key key2) {
42         return key1 == key2;
43 }
44
45 /**
46  * @brief A simple, custom hash table
47  *
48  * By default it is snapshotting, but you can pass in your own allocation
49  * functions. Note that this table does not support the value 0 (NULL) used as
50  * a key and is designed primarily with pointer-based keys in mind. Other
51  * primitive key types are supported only for non-zero values.
52  *
53  * @tparam _Key    Type name for the key
54  * @tparam _Val    Type name for the values to be stored
55  * @tparam _KeyInt Integer type that is at least as large as _Key. Used for key
56  *                 manipulation and storage.
57  * @tparam _Shift  Logical shift to apply to all keys. Default 0.
58  */
59 template<typename _Key, typename _Val, typename _KeyInt, int _Shift = 0, unsigned int (*hash_function)(_Key) = defaultHashFunction<_Key, _Shift, _KeyInt>, bool (*equals)(_Key, _Key) = defaultEquals<_Key> >
60 class Hashtable {
61 public:
62         /**
63          * @brief Hash table constructor
64          * @param initialcapacity Sets the initial capacity of the hash table.
65          * Default size 1024.
66          * @param factor Sets the percentage full before the hashtable is
67          * resized. Default ratio 0.5.
68          */
69         Hashtable(unsigned int initialcapacity = 1024, double factor = 0.5) {
70                 // Allocate space for the hash table
71                 table = (struct Hashlistnode<_Key, _Val> *)ourcalloc(initialcapacity, sizeof(struct Hashlistnode<_Key, _Val>));
72                 zero = NULL;
73                 loadfactor = factor;
74                 capacity = initialcapacity;
75                 capacitymask = initialcapacity - 1;
76
77                 threshold = (unsigned int)(initialcapacity * loadfactor);
78                 size = 0;                                                       // Initial number of elements in the hash
79         }
80
81         /** @brief Hash table destructor */
82         ~Hashtable() {
83                 ourfree(table);
84                 if (zero)
85                         ourfree(zero);
86         }
87
88         /** Override: new operator */
89         void *operator new(size_t size) {
90                 return ourmalloc(size);
91         }
92
93         /** Override: delete operator */
94         void operator delete(void *p, size_t size) {
95                 ourfree(p);
96         }
97
98         /** Override: new[] operator */
99         void *operator new[](size_t size) {
100                 return ourmalloc(size);
101         }
102
103         /** Override: delete[] operator */
104         void operator delete[](void *p, size_t size) {
105                 ourfree(p);
106         }
107
108         /** @brief Reset the table to its initial state. */
109         void reset() {
110                 memset(table, 0, capacity * sizeof(struct Hashlistnode<_Key, _Val>));
111                 if (zero) {
112                         ourfree(zero);
113                         zero = NULL;
114                 }
115                 size = 0;
116         }
117
118         /** Doesn't work with zero value */
119         _Val getRandomValue() {
120                 while (true) {
121                         unsigned int index = random() & capacitymask;
122                         struct Hashlistnode<_Key, _Val> *bin = &table[index];
123                         if (bin->key != NULL && bin->val != NULL) {
124                                 return bin->val;
125                         }
126                 }
127         }
128
129         void resetAndDeleteKeys() {
130                 for (unsigned int i = 0; i < capacity; i++) {
131                         struct Hashlistnode<_Key, _Val> *bin = &table[i];
132                         if (bin->key != NULL) {
133                                 delete bin->key;
134                                 bin->key = NULL;
135                                 if (bin->val != NULL) {
136                                         bin->val = NULL;
137                                 }
138                         }
139                 }
140                 if (zero) {
141                         ourfree(zero);
142                         zero = NULL;
143                 }
144                 size = 0;
145         }
146
147         void resetAndDeleteVals() {
148                 for (unsigned int i = 0; i < capacity; i++) {
149                         struct Hashlistnode<_Key, _Val> *bin = &table[i];
150                         if (bin->key != NULL) {
151                                 bin->key = NULL;
152                                 if (bin->val != NULL) {
153                                         delete bin->val;
154                                         bin->val = NULL;
155                                 }
156                         }
157                 }
158                 if (zero) {
159                         if (zero->val != NULL)
160                                 delete zero->val;
161                         ourfree(zero);
162                         zero = NULL;
163                 }
164                 size = 0;
165         }
166
167         void resetAndFreeVals() {
168                 for (unsigned int i = 0; i < capacity; i++) {
169                         struct Hashlistnode<_Key, _Val> *bin = &table[i];
170                         if (bin->key != NULL) {
171                                 bin->key = NULL;
172                                 if (bin->val != NULL) {
173                                         ourfree(bin->val);
174                                         bin->val = NULL;
175                                 }
176                         }
177                 }
178                 if (zero) {
179                         if (zero->val != NULL)
180                                 ourfree(zero->val);
181                         ourfree(zero);
182                         zero = NULL;
183                 }
184                 size = 0;
185         }
186
187         /**
188          * @brief Put a key/value pair into the table
189          * @param key The key for the new value; must not be 0 or NULL
190          * @param val The value to store in the table
191          */
192         void put(_Key key, _Val val) {
193                 /* Hashtable cannot handle 0 as a key */
194                 if (!key) {
195                         if (!zero) {
196                                 zero = (struct Hashlistnode<_Key, _Val> *)ourmalloc(sizeof(struct Hashlistnode<_Key, _Val>));
197                                 size++;
198                         }
199                         zero->key = key;
200                         zero->val = val;
201                         return;
202                 }
203
204                 if (size > threshold)
205                         resize(capacity << 1);
206
207                 struct Hashlistnode<_Key, _Val> *search;
208
209                 unsigned int index = hash_function(key);
210                 do {
211                         index &= capacitymask;
212                         search = &table[index];
213                         if (!search->key) {
214                                 //key is null, probably done
215                                 break;
216                         }
217                         if (equals(search->key, key)) {
218                                 search->val = val;
219                                 return;
220                         }
221                         index++;
222                 } while (true);
223
224                 search->key = key;
225                 search->val = val;
226                 size++;
227         }
228
229         /**
230          * @brief Lookup the corresponding value for the given key
231          * @param key The key for finding the value; must not be 0 or NULL
232          * @return The value in the table, if the key is found; otherwise 0
233          */
234         _Val get(_Key key) const {
235                 struct Hashlistnode<_Key, _Val> *search;
236
237                 /* Hashtable cannot handle 0 as a key */
238                 if (!key) {
239                         if (zero)
240                                 return zero->val;
241                         else
242                                 return (_Val) 0;
243                 }
244
245                 unsigned int oindex = hash_function(key) & capacitymask;
246                 unsigned int index = oindex;
247                 do {
248                         search = &table[index];
249                         if (!search->key) {
250                                 if (!search->val)
251                                         break;
252                         } else
253                         if (equals(search->key, key))
254                                 return search->val;
255                         index++;
256                         index &= capacitymask;
257                         if (index == oindex)
258                                 break;
259                 } while (true);
260                 return (_Val)0;
261         }
262
263         /**
264          * @brief Remove the given key and return the corresponding value
265          * @param key The key for finding the value; must not be 0 or NULL
266          * @return The value in the table, if the key is found; otherwise 0
267          */
268         _Val remove(_Key key) {
269                 struct Hashlistnode<_Key, _Val> *search;
270
271                 /* Hashtable cannot handle 0 as a key */
272                 if (!key) {
273                         if (!zero) {
274                                 return (_Val)0;
275                         } else {
276                                 _Val v = zero->val;
277                                 ourfree(zero);
278                                 zero = NULL;
279                                 size--;
280                                 return v;
281                         }
282                 }
283
284
285                 unsigned int index = hash_function(key);
286                 do {
287                         index &= capacitymask;
288                         search = &table[index];
289                         if (!search->key) {
290                                 if (!search->val)
291                                         break;
292                         } else
293                         if (equals(search->key, key)) {
294                                 _Val v = search->val;
295                                 //empty out this bin
296                                 search->val = (_Val) 1;
297                                 search->key = 0;
298                                 size--;
299                                 return v;
300                         }
301                         index++;
302                 } while (true);
303                 return (_Val)0;
304         }
305
306         unsigned int getSize() const {
307                 return size;
308         }
309
310
311         /**
312          * @brief Check whether the table contains a value for the given key
313          * @param key The key for finding the value; must not be 0 or NULL
314          * @return True, if the key is found; false otherwise
315          */
316         bool contains(_Key key) const {
317                 struct Hashlistnode<_Key, _Val> *search;
318
319                 /* Hashtable cannot handle 0 as a key */
320                 if (!key) {
321                         return zero != NULL;
322                 }
323
324                 unsigned int index = hash_function(key);
325                 do {
326                         index &= capacitymask;
327                         search = &table[index];
328                         if (!search->key) {
329                                 if (!search->val)
330                                         break;
331                         } else
332                         if (equals(search->key, key))
333                                 return true;
334                         index++;
335                 } while (true);
336                 return false;
337         }
338
339         /**
340          * @brief Resize the table
341          * @param newsize The new size of the table
342          */
343         void resize(unsigned int newsize) {
344                 struct Hashlistnode<_Key, _Val> *oldtable = table;
345                 struct Hashlistnode<_Key, _Val> *newtable;
346                 unsigned int oldcapacity = capacity;
347
348                 if ((newtable = (struct Hashlistnode<_Key, _Val> *)ourcalloc(newsize, sizeof(struct Hashlistnode<_Key, _Val>))) == NULL) {
349                         model_print("calloc error %s %d\n", __FILE__, __LINE__);
350                         exit(EXIT_FAILURE);
351                 }
352
353                 table = newtable;                                                                                       // Update the global hashtable upon resize()
354                 capacity = newsize;
355                 capacitymask = newsize - 1;
356
357                 threshold = (unsigned int)(newsize * loadfactor);
358
359                 struct Hashlistnode<_Key, _Val> *bin = &oldtable[0];
360                 struct Hashlistnode<_Key, _Val> *lastbin = &oldtable[oldcapacity];
361                 for (; bin < lastbin; bin++) {
362                         _Key key = bin->key;
363
364                         struct Hashlistnode<_Key, _Val> *search;
365                         if (!key)
366                                 continue;
367
368                         unsigned int index = hash_function(key);
369                         do {
370                                 index &= capacitymask;
371                                 search = &table[index];
372                                 index++;
373                         } while (search->key);
374
375                         search->key = key;
376                         search->val = bin->val;
377                 }
378
379                 ourfree(oldtable);                                                                                              // Free the memory of the old hash table
380         }
381         double getLoadFactor() {return loadfactor;}
382         unsigned int getCapacity() {return capacity;}
383         struct Hashlistnode<_Key, _Val> *table;
384         struct Hashlistnode<_Key, _Val> *zero;
385         unsigned int capacity;
386         unsigned int size;
387 private:
388         unsigned int capacitymask;
389         unsigned int threshold;
390         double loadfactor;
391 };
392
393 #endif/* __HASHTABLE_H__ */