1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include "cx/hash_map.h"
30
31 #include <string.h>
32 #include <assert.h>
33 #include <errno.h>
34
35 struct cx_hash_map_element_s {
36
37 struct cx_hash_map_element_s *next;
38
39
40 CxHashKey key;
41
42
43 char data[];
44 };
45
46 static void cx_hash_map_clear(
struct cx_map_s *map) {
47 struct cx_hash_map_s *hash_map = (
struct cx_hash_map_s *) map;
48 for (
size_t i =
0; i < hash_map->bucket_count; i++) {
49 struct cx_hash_map_element_s *elem = hash_map->buckets[i];
50 if (elem !=
NULL) {
51 do {
52 struct cx_hash_map_element_s *next = elem->next;
53
54 cx_invoke_destructor(map, elem->data);
55
56 cxFree(map->collection.allocator, (
void *) elem->key.data);
57
58 cxFree(map->collection.allocator, elem);
59
60 elem = next;
61 }
while (elem !=
NULL);
62
63
64 hash_map->buckets[i] =
NULL;
65 }
66 }
67 map->collection.size =
0;
68 }
69
70 static void cx_hash_map_destructor(
struct cx_map_s *map) {
71 struct cx_hash_map_s *hash_map = (
struct cx_hash_map_s *) map;
72
73
74 cx_hash_map_clear(map);
75 cxFree(map->collection.allocator, hash_map->buckets);
76
77
78 cxFree(map->collection.allocator, map);
79 }
80
81 static void *cx_hash_map_put(
82 CxMap *map,
83 CxHashKey key,
84 void *value
85 ) {
86 struct cx_hash_map_s *hash_map = (
struct cx_hash_map_s *) map;
87 const CxAllocator *allocator = map->collection.allocator;
88
89 uint64_t hash = key.hash;
90 if (hash ==
0) {
91 cx_hash_murmur(&key);
92 hash = key.hash;
93 }
94
95 size_t slot = hash % hash_map->bucket_count;
96 struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
97 struct cx_hash_map_element_s *prev =
NULL;
98
99 while (elm !=
NULL && elm->key.hash < hash) {
100 prev = elm;
101 elm = elm->next;
102 }
103
104 if (elm !=
NULL && cx_hash_key_cmp(&elm->key, &key) ==
0) {
105
106 cx_invoke_destructor(map, elm->data);
107 if (value ==
NULL) {
108 memset(elm->data,
0, map->collection.elem_size);
109 }
else if (map->collection.store_pointer) {
110 memcpy(elm->data, &value,
sizeof(
void *));
111 }
else {
112 memcpy(elm->data, value, map->collection.elem_size);
113 }
114 }
else {
115
116 struct cx_hash_map_element_s *e = cxMalloc(
117 allocator,
118 sizeof(
struct cx_hash_map_element_s) + map->collection.elem_size
119 );
120 if (e ==
NULL)
return NULL;
121
122
123 if (value ==
NULL) {
124 memset(e->data,
0, map->collection.elem_size);
125 }
else if (map->collection.store_pointer) {
126 memcpy(e->data, &value,
sizeof(
void *));
127 }
else {
128 memcpy(e->data, value, map->collection.elem_size);
129 }
130
131
132 void *kd = cxMalloc(allocator, key.len);
133 if (kd ==
NULL) {
134 cxFree(allocator, e);
135 return NULL;
136 }
137 memcpy(kd, key.data, key.len);
138 e->key.data = kd;
139 e->key.len = key.len;
140 e->key.hash = hash;
141
142
143 if (prev ==
NULL) {
144 hash_map->buckets[slot] = e;
145 }
else {
146 prev->next = e;
147 }
148 e->next = elm;
149 elm = e;
150
151
152 map->collection.size++;
153 }
154
155
156 return elm->data;
157 }
158
159 static void cx_hash_map_unlink(
160 struct cx_hash_map_s *hash_map,
161 size_t slot,
162 struct cx_hash_map_element_s *prev,
163 struct cx_hash_map_element_s *elm
164 ) {
165
166 if (prev ==
NULL) {
167 hash_map->buckets[slot] = elm->next;
168 }
else {
169 prev->next = elm->next;
170 }
171
172 cxFree(hash_map->base.collection.allocator, (
void *) elm->key.data);
173 cxFree(hash_map->base.collection.allocator, elm);
174
175 hash_map->base.collection.size--;
176 }
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198 static int cx_hash_map_get_remove(
199 CxMap *map,
200 CxHashKey key,
201 void *targetbuf,
202 bool remove
203 ) {
204 struct cx_hash_map_s *hash_map = (
struct cx_hash_map_s *) map;
205
206 uint64_t hash = key.hash;
207 if (hash ==
0) {
208 cx_hash_murmur(&key);
209 hash = key.hash;
210 }
211
212 size_t slot = hash % hash_map->bucket_count;
213 struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
214 struct cx_hash_map_element_s *prev =
NULL;
215 while (elm && elm->key.hash <= hash) {
216 if (cx_hash_key_cmp(&elm->key, &key) ==
0) {
217 if (remove) {
218 if (targetbuf ==
NULL) {
219 cx_invoke_destructor(map, elm->data);
220 }
else {
221 memcpy(targetbuf, elm->data, map->collection.elem_size);
222 }
223 cx_hash_map_unlink(hash_map, slot, prev, elm);
224 }
else {
225 assert(targetbuf !=
NULL);
226 void *data =
NULL;
227 if (map->collection.store_pointer) {
228 data = *(
void **) elm->data;
229 }
else {
230 data = elm->data;
231 }
232 memcpy(targetbuf, &data,
sizeof(
void *));
233 }
234 return 0;
235 }
236 prev = elm;
237 elm = prev->next;
238 }
239
240 return 1;
241 }
242
243 static void *cx_hash_map_get(
244 const CxMap *map,
245 CxHashKey key
246 ) {
247
248 void *ptr =
NULL;
249 int found = cx_hash_map_get_remove((CxMap *) map, key, &ptr, false);
250 return found ==
0 ? ptr :
NULL;
251 }
252
253 static int cx_hash_map_remove(
254 CxMap *map,
255 CxHashKey key,
256 void *targetbuf
257 ) {
258 return cx_hash_map_get_remove(map, key, targetbuf, true);
259 }
260
261 static void *cx_hash_map_iter_current_entry(
const void *it) {
262 const CxMapIterator *iter = it;
263
264 return (
void*) &iter->entry;
265 }
266
267 static void *cx_hash_map_iter_current_key(
const void *it) {
268 const CxMapIterator *iter = it;
269 return (
void*) iter->entry.key;
270 }
271
272 static void *cx_hash_map_iter_current_value(
const void *it) {
273 const CxMapIterator *iter = it;
274 return iter->entry.value;
275 }
276
277 static bool cx_hash_map_iter_valid(
const void *it) {
278 const CxMapIterator *iter = it;
279 return iter->elem !=
NULL;
280 }
281
282 static void cx_hash_map_iter_next(
void *it) {
283 CxMapIterator *iter = it;
284 CxMap *map = iter->map;
285 struct cx_hash_map_s *hmap = (
struct cx_hash_map_s *) map;
286 struct cx_hash_map_element_s *elm = iter->elem;
287
288
289 if (iter->base.remove) {
290
291
292 iter->base.remove = false;
293
294
295 struct cx_hash_map_element_s *next = elm->next;
296
297
298 struct cx_hash_map_element_s *prev =
NULL;
299 if (hmap->buckets[iter->slot] != elm) {
300 prev = hmap->buckets[iter->slot];
301 while (prev->next != elm) {
302 prev = prev->next;
303 }
304 }
305
306
307 cx_invoke_destructor(map, elm->data);
308
309
310 cx_hash_map_unlink(hmap, iter->slot, prev, elm);
311 iter->elem_count--;
312
313
314 elm = next;
315 }
else {
316
317 elm = elm->next;
318 iter->index++;
319 }
320
321
322 while (elm ==
NULL && ++iter->slot < hmap->bucket_count) {
323 elm = hmap->buckets[iter->slot];
324 }
325 iter->elem = elm;
326
327
328
329
330 if (elm !=
NULL) {
331 iter->entry.key = &elm->key;
332 if (map->collection.store_pointer) {
333 iter->entry.value = *(
void **) elm->data;
334 }
else {
335 iter->entry.value = elm->data;
336 }
337 }
338 }
339
340 static CxMapIterator cx_hash_map_iterator(
341 const CxMap *map,
342 enum cx_map_iterator_type type
343 ) {
344 CxMapIterator iter;
345
346 iter.map = (CxMap*) map;
347 iter.elem_count = map->collection.size;
348
349 switch (type) {
350 case CX_MAP_ITERATOR_PAIRS:
351 iter.elem_size =
sizeof(CxMapEntry);
352 iter.base.current = cx_hash_map_iter_current_entry;
353 break;
354 case CX_MAP_ITERATOR_KEYS:
355 iter.elem_size =
sizeof(CxHashKey);
356 iter.base.current = cx_hash_map_iter_current_key;
357 break;
358 case CX_MAP_ITERATOR_VALUES:
359 iter.elem_size = map->collection.elem_size;
360 iter.base.current = cx_hash_map_iter_current_value;
361 break;
362 default:
363 assert(false);
364 }
365
366 iter.base.valid = cx_hash_map_iter_valid;
367 iter.base.next = cx_hash_map_iter_next;
368 iter.base.remove = false;
369 iter.base.allow_remove = true;
370
371 iter.slot =
0;
372 iter.index =
0;
373
374 if (map->collection.size >
0) {
375 struct cx_hash_map_s *hash_map = (
struct cx_hash_map_s *) map;
376 struct cx_hash_map_element_s *elm = hash_map->buckets[
0];
377 while (elm ==
NULL) {
378 elm = hash_map->buckets[++iter.slot];
379 }
380 iter.elem = elm;
381 iter.entry.key = &elm->key;
382 if (map->collection.store_pointer) {
383 iter.entry.value = *(
void **) elm->data;
384 }
else {
385 iter.entry.value = elm->data;
386 }
387 }
else {
388 iter.elem =
NULL;
389 }
390
391 return iter;
392 }
393
394 static cx_map_class cx_hash_map_class = {
395 cx_hash_map_destructor,
396 cx_hash_map_clear,
397 cx_hash_map_put,
398 cx_hash_map_get,
399 cx_hash_map_remove,
400 cx_hash_map_iterator,
401 };
402
403 CxMap *cxHashMapCreate(
404 const CxAllocator *allocator,
405 size_t itemsize,
406 size_t buckets
407 ) {
408 if (allocator ==
NULL) {
409 allocator = cxDefaultAllocator;
410 }
411
412 if (buckets ==
0) {
413
414 buckets =
16;
415 }
416
417 struct cx_hash_map_s *map = cxCalloc(allocator,
1,
418 sizeof(
struct cx_hash_map_s));
419 if (map ==
NULL)
return NULL;
420
421
422 map->bucket_count = buckets;
423 map->buckets = cxCalloc(allocator, buckets,
424 sizeof(
struct cx_hash_map_element_s *));
425 if (map->buckets ==
NULL) {
426 cxFree(allocator, map);
427 return NULL;
428 }
429
430
431 map->base.cl = &cx_hash_map_class;
432 map->base.collection.allocator = allocator;
433
434 if (itemsize >
0) {
435 map->base.collection.elem_size = itemsize;
436 }
else {
437 map->base.collection.elem_size =
sizeof(
void *);
438 map->base.collection.store_pointer = true;
439 }
440
441 return (CxMap *) map;
442 }
443
444 int cxMapRehash(CxMap *map) {
445 struct cx_hash_map_s *hash_map = (
struct cx_hash_map_s *) map;
446 if (map->collection.size > ((hash_map->bucket_count *
3) >>
2)) {
447
448 size_t new_bucket_count = (map->collection.size *
5) >>
1;
449 if (new_bucket_count < hash_map->bucket_count) {
450
451 errno =
EOVERFLOW;
452 return 1;
453 }
454 struct cx_hash_map_element_s **new_buckets = cxCalloc(
455 map->collection.allocator,
456 new_bucket_count,
sizeof(
struct cx_hash_map_element_s *)
457 );
458
459 if (new_buckets ==
NULL)
return 1;
460
461
462 for (
size_t slot =
0; slot < hash_map->bucket_count; slot++) {
463 struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
464 while (elm !=
NULL) {
465 struct cx_hash_map_element_s *next = elm->next;
466 size_t new_slot = elm->key.hash % new_bucket_count;
467
468
469 struct cx_hash_map_element_s *bucket_next = new_buckets[new_slot];
470 struct cx_hash_map_element_s *bucket_prev =
NULL;
471 while (bucket_next !=
NULL &&
472 bucket_next->key.hash < elm->key.hash) {
473 bucket_prev = bucket_next;
474 bucket_next = bucket_next->next;
475 }
476
477
478 if (bucket_prev ==
NULL) {
479 elm->next = new_buckets[new_slot];
480 new_buckets[new_slot] = elm;
481 }
else {
482 bucket_prev->next = elm;
483 elm->next = bucket_next;
484 }
485
486
487 elm = next;
488 }
489 }
490
491
492 hash_map->bucket_count = new_bucket_count;
493 cxFree(map->collection.allocator, hash_map->buckets);
494 hash_map->buckets = new_buckets;
495 }
496 return 0;
497 }
498