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