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