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