1 /** @file hash.c  Hash of integer values.
2 
3 @authors Copyright (c) 2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 
5 @par License
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice, this
11    list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright notice,
13    this list of conditions and the following disclaimer in the documentation
14    and/or other materials provided with the distribution.
15 
16 <small>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</small>
26 */
27 
28 #include "the_Foundation/hash.h"
29 
30 #include <stdlib.h>
31 
32 iDeclareType(HashBucket)
33 
34 #define iHashMaxNodes           8 // per node
35 #define iHashMaxDepth           32
36 
37 #define iHashBucketChildCount   4
38 #define iHashBucketChildMask    0x3
39 #define iHashBucketChildShift   2
40 
41 struct Impl_HashBucket {
42     iHashBucket *parent;
43     iHashBucket *child[iHashBucketChildCount];
44     iHashNode *node;
45 };
46 
47 #define isBranch_HashBucket_(d)             ((d)->child[0] != NULL)
48 #define shiftKey_HashBucket_(key, levels)   ((key) >> (iHashBucketChildShift * (levels)))
49 #define child_HashBucket_(d, key, depth)    \
50         ((d)->child[shiftKey_HashBucket_(key, depth) & iHashBucketChildMask])
51 
delete_HashBucket_(iHashBucket * d)52 static void delete_HashBucket_(iHashBucket *d) {
53     if (d) {
54         for (int i = 0; i < iHashBucketChildCount; ++i) {
55             if (d->child[i]) {
56                 delete_HashBucket_(d->child[i]);
57             }
58         }
59         free(d);
60     }
61 }
62 
find_HashBucket_(iHashBucket * d,iHashKey key,int * depth)63 static iHashBucket *find_HashBucket_(iHashBucket *d, iHashKey key, int *depth) {
64     *depth = 0;
65     while (isBranch_HashBucket_(d)) {
66         d = d->child[key & iHashBucketChildMask];
67         key = shiftKey_HashBucket_(key, 1);
68         *depth += 1;
69     }
70     return d;
71 }
72 
findNode_HashBucket_(const iHashBucket * d,iHashKey key)73 static iHashNode *findNode_HashBucket_(const iHashBucket *d, iHashKey key) {
74     const iHashKey elemKey = key;
75     while (isBranch_HashBucket_(d)) {
76         d = d->child[key & iHashBucketChildMask];
77         key = shiftKey_HashBucket_(key, 1);
78     }
79     for (iHashNode *i = d->node; i; i = i->next) {
80         if (i->key == elemKey) {
81             return i;
82         }
83     }
84     return NULL;
85 }
86 
addNode_HashBucket_(iHashBucket * d,iHashNode * node)87 static void addNode_HashBucket_(iHashBucket *d, iHashNode *node) {
88     node->next = d->node;
89     d->node = node;
90 }
91 
split_HashBucket_(iHashBucket * d,int depth)92 static void split_HashBucket_(iHashBucket *d, int depth) {
93     iAssert(depth < iHashMaxDepth);
94     /* Create the new children. */
95     for (int i = 0; i < iHashBucketChildCount; ++i) {
96         d->child[i] = calloc(sizeof(iHashBucket), 1);
97         d->child[i]->parent = d;
98     }
99     /* Divide the listed nodes. */
100     iHashNode *next;
101     for (iHashNode *i = d->node; i; i = next) {
102         next = i->next;
103         iHashBucket *dest = child_HashBucket_(d, i->key, depth);
104         addNode_HashBucket_(dest, i);
105     }
106     d->node = NULL;
107 }
108 
isEmpty_HashBucket_(const iHashBucket * d)109 static iBool isEmpty_HashBucket_(const iHashBucket *d) {
110     return d->node == NULL && d->child[0] == NULL && d->child[1] == NULL;
111 }
112 
trim_HashBucket_(iHashBucket * d)113 static iHashBucket *trim_HashBucket_(iHashBucket *d) {
114     while (d->parent &&
115            isEmpty_HashBucket_(d->parent->child[0]) &&
116            isEmpty_HashBucket_(d->parent->child[1]) &&
117            isEmpty_HashBucket_(d->parent->child[2]) &&
118            isEmpty_HashBucket_(d->parent->child[3])) {
119         d = d->parent;
120         /* All child nodes empty, get rid of them. */
121         for (int i = 0; i < iHashBucketChildCount; ++i) {
122             free(d->child[i]);
123             d->child[i] = NULL;
124         }
125     }
126     return d;
127 }
128 
remove_HashBucket_(iHashBucket ** d,iHashKey key)129 static iHashNode *remove_HashBucket_(iHashBucket **d, iHashKey key) {
130     for (iHashNode *i = (*d)->node, **prev = &(*d)->node; i; i = i->next) {
131         if (i->key == key) {
132             *prev = i->next;
133             i->next = NULL;
134             if ((*d)->node == NULL) {
135                 /* Node became empty, it may need removing. */
136                 *d = trim_HashBucket_(*d);
137             }
138             return i;
139         }
140         prev = &i->next;
141     }
142     return NULL;
143 }
144 
ordinal_HashBucket_(const iHashBucket * d)145 static int ordinal_HashBucket_(const iHashBucket *d) {
146     if (d->parent) {
147         for (int i = 0; i < iHashBucketChildCount; ++i) {
148             if (d->parent->child[i] == d) return i;
149         }
150     }
151     return -1;
152 }
153 
firstInOrder_HashBucket_(const iHashBucket * d)154 static iHashBucket *firstInOrder_HashBucket_(const iHashBucket *d) {
155     if (!d) return NULL;
156     if (d->node) {
157         iAssert(d->child[0] == NULL);
158         iAssert(d->child[1] == NULL);
159         iAssert(d->child[2] == NULL);
160         iAssert(d->child[3] == NULL);
161         return iConstCast(iHashBucket *, d);
162     }
163     iHashBucket *elem = NULL;
164     for (int i = 0; i < iHashBucketChildCount; ++i) {
165         if ((elem = firstInOrder_HashBucket_(d->child[i])) != NULL) {
166             break;
167         }
168     }
169     return elem;
170 }
171 
nextInOrder_HashBucket_(const iHashBucket * d)172 static iHashBucket *nextInOrder_HashBucket_(const iHashBucket *d) {
173     while (d->parent) {
174         /* Switch to the next sibling. */
175         int ord = ordinal_HashBucket_(d);
176         iAssert(ord != -1);
177         for (int i = ord + 1; i < iHashBucketChildCount; ++i) {
178             iHashBucket *successor = firstInOrder_HashBucket_(d->parent->child[i]);
179             if (successor) {
180                 return successor;
181             }
182         }
183         /* Go back up then and try again. */
184         d = d->parent;
185     }
186     return NULL;
187 }
188 
189 /*-------------------------------------------------------------------------------------*/
190 
iDefineTypeConstruction(Hash)191 iDefineTypeConstruction(Hash)
192 
193 void init_Hash(iHash *d) {
194     d->root = calloc(sizeof(iHashBucket), 1);
195     d->size = 0;
196 }
197 
deinit_Hash(iHash * d)198 void deinit_Hash(iHash *d) {
199     delete_HashBucket_(d->root);
200 }
201 
contains_Hash(const iHash * d,iHashKey key)202 iBool contains_Hash(const iHash *d, iHashKey key) {
203     return value_Hash(d, key) != NULL;
204 }
205 
value_Hash(const iHash * d,iHashKey key)206 iHashNode *value_Hash(const iHash *d, iHashKey key) {
207     return findNode_HashBucket_(d->root, key);
208 }
209 
clear_Hash(iHash * d)210 void clear_Hash(iHash *d) {
211     for (int i = 0; i < iHashBucketChildCount; ++i) {
212         delete_HashBucket_(d->root->child[i]);
213     }
214     iZap(*d->root);
215     d->size = 0;
216 }
217 
insert_Hash(iHash * d,iHashNode * node)218 iHashNode *insert_Hash(iHash *d, iHashNode *node) {
219     iAssert(node != NULL);
220     int depth;
221     iHashBucket *bucket = find_HashBucket_(d->root, node->key, &depth);
222     iHashNode *existing = NULL;
223     size_t nodeSize = 0;
224     /* An existing node with a clashing key must be removed. */
225     for (iHashNode *i = bucket->node, **prev = &bucket->node; i; i = i->next, nodeSize++) {
226         if (i->key == node->key) {
227             *prev = i->next;
228             existing = i;
229             existing->next = NULL;
230             d->size--;
231             break;
232         }
233         prev = &i->next;
234     }
235     if (nodeSize >= iHashMaxNodes) {
236         split_HashBucket_(bucket, depth);
237         addNode_HashBucket_(child_HashBucket_(bucket, node->key, depth), node);
238     }
239     else {
240         addNode_HashBucket_(bucket, node);
241     }
242     /* Update total count. */
243     d->size++;
244     return existing;
245 }
246 
remove_Hash(iHash * d,iHashKey key)247 iHashNode *remove_Hash(iHash *d, iHashKey key) {
248     int depth;
249     iHashBucket *bucket = find_HashBucket_(d->root, key, &depth);
250     iHashNode *removed = remove_HashBucket_(&bucket, key);
251     if (removed) d->size--;
252     return removed;
253 }
254 
255 /*-------------------------------------------------------------------------------------*/
256 
init_HashIterator(iHashIterator * d,iHash * hash)257 void init_HashIterator(iHashIterator *d, iHash *hash) {
258     d->hash = hash;
259     d->bucket = firstInOrder_HashBucket_(hash->root);
260     d->value = (d->bucket? d->bucket->node : NULL);
261     /* The current node may be deleted, so keep the next one in a safe place. */
262     d->next = (d->value? d->value->next : NULL);
263 }
264 
next_HashIterator(iHashIterator * d)265 void next_HashIterator(iHashIterator *d) {
266     d->value = d->next;
267     if (!d->value) {
268         if((d->bucket = nextInOrder_HashBucket_(d->bucket)) != NULL) {
269             d->value = d->bucket->node;
270         }
271     }
272     d->next = (d->value? d->value->next : NULL);
273 }
274 
remove_HashIterator(iHashIterator * d)275 iHashNode *remove_HashIterator(iHashIterator *d) {
276     remove_HashBucket_(&d->bucket, d->value->key);
277     d->hash->size--;
278     return d->value;
279 }
280 
init_HashConstIterator(iHashConstIterator * d,const iHash * hash)281 void init_HashConstIterator(iHashConstIterator *d, const iHash *hash) {
282     d->hash = hash;
283     d->bucket = firstInOrder_HashBucket_(hash->root);
284     d->value = (d->bucket? d->bucket->node : NULL);
285 }
286 
next_HashConstIterator(iHashConstIterator * d)287 void next_HashConstIterator(iHashConstIterator *d) {
288     d->value = d->value->next;
289     if (!d->value) {
290         if((d->bucket = nextInOrder_HashBucket_(d->bucket)) != NULL) {
291             d->value = d->bucket->node;
292         }
293     }
294 }
295