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