28 |
28 |
29 #include <string.h> |
29 #include <string.h> |
30 #include "cx/hash_map.h" |
30 #include "cx/hash_map.h" |
31 #include "cx/utils.h" |
31 #include "cx/utils.h" |
32 |
32 |
|
33 struct cx_hash_map_element_s { |
|
34 /** A pointer to the next element in the current bucket. */ |
|
35 struct cx_hash_map_element_s *next; |
|
36 |
|
37 /** The corresponding key. */ |
|
38 CxHashKey key; |
|
39 |
|
40 /** The value data. */ |
|
41 char data[]; |
|
42 }; |
|
43 |
33 static void cx_hash_map_clear(struct cx_map_s *map) { |
44 static void cx_hash_map_clear(struct cx_map_s *map) { |
34 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; |
45 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; |
35 cx_for_n(i, hash_map->bucket_count) { |
46 cx_for_n(i, hash_map->bucket_count) { |
36 struct cx_hash_map_element_s *elem = hash_map->buckets[i]; |
47 struct cx_hash_map_element_s *elem = hash_map->buckets[i]; |
37 if (elem != NULL) { |
48 if (elem != NULL) { |
38 do { |
49 do { |
39 struct cx_hash_map_element_s *next = elem->next; |
50 struct cx_hash_map_element_s *next = elem->next; |
|
51 // invoke the destructor |
|
52 cx_invoke_destructor(map, elem->data); |
40 // free the key data |
53 // free the key data |
41 cxFree(map->allocator, elem->key.data.obj); |
54 cxFree(map->allocator, (void *) elem->key.data); |
42 // free the node |
55 // free the node |
43 cxFree(map->allocator, elem); |
56 cxFree(map->allocator, elem); |
44 // proceed |
57 // proceed |
45 elem = next; |
58 elem = next; |
46 } while (elem != NULL); |
59 } while (elem != NULL); |
85 prev = elm; |
98 prev = elm; |
86 elm = elm->next; |
99 elm = elm->next; |
87 } |
100 } |
88 |
101 |
89 if (elm != NULL && elm->key.hash == hash && elm->key.len == key.len && |
102 if (elm != NULL && elm->key.hash == hash && elm->key.len == key.len && |
90 memcmp(elm->key.data.obj, key.data.obj, key.len) == 0) { |
103 memcmp(elm->key.data, key.data, key.len) == 0) { |
91 // overwrite existing element |
104 // overwrite existing element |
92 elm->data = value; |
105 if (map->store_pointer) { |
|
106 memcpy(elm->data, &value, sizeof(void *)); |
|
107 } else { |
|
108 memcpy(elm->data, value, map->item_size); |
|
109 } |
93 } else { |
110 } else { |
94 // allocate new element |
111 // allocate new element |
95 struct cx_hash_map_element_s *e = cxMalloc(allocator, sizeof(struct cx_hash_map_element_s)); |
112 struct cx_hash_map_element_s *e = cxMalloc( |
|
113 allocator, |
|
114 sizeof(struct cx_hash_map_element_s) + map->item_size |
|
115 ); |
96 if (e == NULL) { |
116 if (e == NULL) { |
97 return -1; |
117 return -1; |
98 } |
118 } |
99 |
119 |
100 // write the value |
120 // write the value |
101 // TODO: depending on future map features, we may want to copy here |
121 if (map->store_pointer) { |
102 e->data = value; |
122 memcpy(e->data, &value, sizeof(void *)); |
|
123 } else { |
|
124 memcpy(e->data, value, map->item_size); |
|
125 } |
103 |
126 |
104 // copy the key |
127 // copy the key |
105 void *kd = cxMalloc(allocator, key.len); |
128 void *kd = cxMalloc(allocator, key.len); |
106 if (kd == NULL) { |
129 if (kd == NULL) { |
107 return -1; |
130 return -1; |
108 } |
131 } |
109 memcpy(kd, key.data.obj, key.len); |
132 memcpy(kd, key.data, key.len); |
110 e->key.data.obj = kd; |
133 e->key.data = kd; |
111 e->key.len = key.len; |
134 e->key.len = key.len; |
112 e->key.hash = hash; |
135 e->key.hash = hash; |
113 |
136 |
114 // insert the element into the linked list |
137 // insert the element into the linked list |
115 if (prev == NULL) { |
138 if (prev == NULL) { |
149 * Helper function to avoid code duplication. |
172 * Helper function to avoid code duplication. |
150 * |
173 * |
151 * @param map the map |
174 * @param map the map |
152 * @param key the key to look up |
175 * @param key the key to look up |
153 * @param remove flag indicating whether the looked up entry shall be removed |
176 * @param remove flag indicating whether the looked up entry shall be removed |
154 * @return the value corresponding to the key or \c NULL |
177 * @param destroy flag indicating whether the destructor shall be invoked |
|
178 * @return a pointer to the value corresponding to the key or \c NULL |
155 */ |
179 */ |
156 static void *cx_hash_map_get_remove( |
180 static void *cx_hash_map_get_remove( |
157 CxMap *map, |
181 CxMap *map, |
158 CxHashKey key, |
182 CxHashKey key, |
159 bool remove |
183 bool remove, |
|
184 bool destroy |
160 ) { |
185 ) { |
161 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; |
186 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; |
162 |
187 |
163 unsigned hash = key.hash; |
188 unsigned hash = key.hash; |
164 if (hash == 0) { |
189 if (hash == 0) { |
169 size_t slot = hash % hash_map->bucket_count; |
194 size_t slot = hash % hash_map->bucket_count; |
170 struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; |
195 struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; |
171 struct cx_hash_map_element_s *prev = NULL; |
196 struct cx_hash_map_element_s *prev = NULL; |
172 while (elm && elm->key.hash <= hash) { |
197 while (elm && elm->key.hash <= hash) { |
173 if (elm->key.hash == hash && elm->key.len == key.len) { |
198 if (elm->key.hash == hash && elm->key.len == key.len) { |
174 if (memcmp(elm->key.data.obj, key.data.obj, key.len) == 0) { |
199 if (memcmp(elm->key.data, key.data, key.len) == 0) { |
175 void *data = elm->data; |
200 void *data = NULL; |
|
201 if (destroy) { |
|
202 cx_invoke_destructor(map, elm->data); |
|
203 } else { |
|
204 if (map->store_pointer) { |
|
205 data = *(void **) elm->data; |
|
206 } else { |
|
207 data = elm->data; |
|
208 } |
|
209 } |
176 if (remove) { |
210 if (remove) { |
177 cx_hash_map_unlink(hash_map, slot, prev, elm); |
211 cx_hash_map_unlink(hash_map, slot, prev, elm); |
178 } |
212 } |
179 return data; |
213 return data; |
180 } |
214 } |
188 |
222 |
189 static void *cx_hash_map_get( |
223 static void *cx_hash_map_get( |
190 CxMap const *map, |
224 CxMap const *map, |
191 CxHashKey key |
225 CxHashKey key |
192 ) { |
226 ) { |
193 // we can safely cast, because we know when remove=false, the map stays untouched |
227 // we can safely cast, because we know the map stays untouched |
194 return cx_hash_map_get_remove((CxMap *) map, key, false); |
228 return cx_hash_map_get_remove((CxMap *) map, key, false, false); |
195 } |
229 } |
196 |
230 |
197 static void *cx_hash_map_remove( |
231 static void *cx_hash_map_remove( |
198 CxMap *map, |
232 CxMap *map, |
199 CxHashKey key |
233 CxHashKey key, |
|
234 bool destroy |
200 ) { |
235 ) { |
201 return cx_hash_map_get_remove(map, key, true); |
236 return cx_hash_map_get_remove(map, key, true, destroy); |
202 } |
237 } |
203 |
238 |
204 static void *cx_hash_map_iter_current_entry(void const *it) { |
239 static void *cx_hash_map_iter_current_entry(void const *it) { |
205 struct cx_iterator_s const *iter = it; |
240 struct cx_iterator_s const *iter = it; |
206 // struct has to have a compatible signature |
241 // struct has to have a compatible signature |
213 return &elm->key; |
248 return &elm->key; |
214 } |
249 } |
215 |
250 |
216 static void *cx_hash_map_iter_current_value(void const *it) { |
251 static void *cx_hash_map_iter_current_value(void const *it) { |
217 struct cx_iterator_s const *iter = it; |
252 struct cx_iterator_s const *iter = it; |
|
253 struct cx_hash_map_s const *map = iter->src_handle; |
218 struct cx_hash_map_element_s *elm = iter->elem_handle; |
254 struct cx_hash_map_element_s *elm = iter->elem_handle; |
219 // TODO: return a pointer to data if this map is storing copies |
255 if (map->base.store_pointer) { |
220 return elm->data; |
256 return *(void **) elm->data; |
|
257 } else { |
|
258 return elm->data; |
|
259 } |
221 } |
260 } |
222 |
261 |
223 static bool cx_hash_map_iter_valid(void const *it) { |
262 static bool cx_hash_map_iter_valid(void const *it) { |
224 struct cx_iterator_s const *iter = it; |
263 struct cx_iterator_s const *iter = it; |
225 return iter->elem_handle != NULL; |
264 return iter->elem_handle != NULL; |
272 if (elm == NULL) { |
314 if (elm == NULL) { |
273 iter->kv_data.key = NULL; |
315 iter->kv_data.key = NULL; |
274 iter->kv_data.value = NULL; |
316 iter->kv_data.value = NULL; |
275 } else { |
317 } else { |
276 iter->kv_data.key = &elm->key; |
318 iter->kv_data.key = &elm->key; |
277 // TODO: pointer to data if this map is storing copies |
319 if (map->base.store_pointer) { |
278 iter->kv_data.value = elm->data; |
320 iter->kv_data.value = *(void **) elm->data; |
|
321 } else { |
|
322 iter->kv_data.value = elm->data; |
|
323 } |
279 } |
324 } |
280 } |
325 } |
281 |
326 |
282 static bool cx_hash_map_iter_flag_rm(void *it) { |
327 static bool cx_hash_map_iter_flag_rm(void *it) { |
283 struct cx_iterator_base_s *iter = it; |
328 struct cx_iterator_base_s *iter = it; |
300 iter.base.remove = false; |
345 iter.base.remove = false; |
301 iter.base.mutating = false; |
346 iter.base.mutating = false; |
302 |
347 |
303 iter.slot = 0; |
348 iter.slot = 0; |
304 iter.index = 0; |
349 iter.index = 0; |
305 |
350 |
306 if (map->size > 0) { |
351 if (map->size > 0) { |
307 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; |
352 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; |
308 struct cx_hash_map_element_s *elm = hash_map->buckets[0]; |
353 struct cx_hash_map_element_s *elm = hash_map->buckets[0]; |
309 while (elm == NULL) { |
354 while (elm == NULL) { |
310 elm = hash_map->buckets[++iter.slot]; |
355 elm = hash_map->buckets[++iter.slot]; |
311 } |
356 } |
312 iter.elem_handle = elm; |
357 iter.elem_handle = elm; |
313 iter.kv_data.key = &elm->key; |
358 iter.kv_data.key = &elm->key; |
314 // TODO: pointer to data if this map is storing copies |
359 if (map->store_pointer) { |
315 iter.kv_data.value = elm->data; |
360 iter.kv_data.value = *(void **) elm->data; |
|
361 } else { |
|
362 iter.kv_data.value = elm->data; |
|
363 } |
316 } else { |
364 } else { |
317 iter.elem_handle = NULL; |
365 iter.elem_handle = NULL; |
318 iter.kv_data.key = NULL; |
366 iter.kv_data.key = NULL; |
319 iter.kv_data.value = NULL; |
367 iter.kv_data.value = NULL; |
320 } |
368 } |
369 cx_hash_map_mut_iterator_keys, |
417 cx_hash_map_mut_iterator_keys, |
370 cx_hash_map_mut_iterator_values, |
418 cx_hash_map_mut_iterator_values, |
371 }; |
419 }; |
372 |
420 |
373 CxMap *cxHashMapCreate( |
421 CxMap *cxHashMapCreate( |
374 CxAllocator *allocator, |
422 CxAllocator const *allocator, |
|
423 size_t itemsize, |
375 size_t buckets |
424 size_t buckets |
376 ) { |
425 ) { |
377 if (buckets == 0) { |
426 if (buckets == 0) { |
378 // implementation defined default |
427 // implementation defined default |
379 buckets = 16; |
428 buckets = 16; |
380 } |
429 } |
381 |
430 |
382 struct cx_hash_map_s *map = cxMalloc(allocator, sizeof(struct cx_hash_map_s)); |
431 struct cx_hash_map_s *map = cxCalloc(allocator, 1, |
|
432 sizeof(struct cx_hash_map_s)); |
383 if (map == NULL) return NULL; |
433 if (map == NULL) return NULL; |
384 |
434 |
385 // initialize hash map members |
435 // initialize hash map members |
386 map->bucket_count = buckets; |
436 map->bucket_count = buckets; |
387 map->buckets = cxCalloc(allocator, buckets, sizeof(struct cx_hash_map_element_s *)); |
437 map->buckets = cxCalloc(allocator, buckets, |
|
438 sizeof(struct cx_hash_map_element_s *)); |
388 if (map->buckets == NULL) { |
439 if (map->buckets == NULL) { |
389 cxFree(allocator, map); |
440 cxFree(allocator, map); |
390 return NULL; |
441 return NULL; |
391 } |
442 } |
392 |
443 |
393 // initialize base members |
444 // initialize base members |
394 map->base.cl = &cx_hash_map_class; |
445 map->base.cl = &cx_hash_map_class; |
395 map->base.allocator = allocator; |
446 map->base.allocator = allocator; |
396 map->base.size = 0; |
447 |
|
448 if (itemsize > 0) { |
|
449 map->base.store_pointer = false; |
|
450 map->base.item_size = itemsize; |
|
451 } else { |
|
452 map->base.store_pointer = true; |
|
453 map->base.item_size = sizeof(void *); |
|
454 } |
397 |
455 |
398 return (CxMap *) map; |
456 return (CxMap *) map; |
399 } |
457 } |
400 |
458 |
401 int cxMapRehash(CxMap *map) { |
459 int cxMapRehash(CxMap *map) { |
402 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; |
460 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; |
403 if (map->size > ((hash_map->bucket_count * 3) >> 2)) { |
461 if (map->size > ((hash_map->bucket_count * 3) >> 2)) { |
404 |
462 |
405 size_t new_bucket_count = (map->size * 5) >> 1; |
463 size_t new_bucket_count = (map->size * 5) >> 1; |
406 struct cx_hash_map_element_s **new_buckets = cxCalloc(map->allocator, |
464 struct cx_hash_map_element_s **new_buckets = cxCalloc( |
407 new_bucket_count, sizeof(struct cx_hash_map_element_s *)); |
465 map->allocator, |
|
466 new_bucket_count, sizeof(struct cx_hash_map_element_s *) |
|
467 ); |
408 |
468 |
409 if (new_buckets == NULL) { |
469 if (new_buckets == NULL) { |
410 return 1; |
470 return 1; |
411 } |
471 } |
412 |
472 |
418 size_t new_slot = elm->key.hash % new_bucket_count; |
478 size_t new_slot = elm->key.hash % new_bucket_count; |
419 |
479 |
420 // find position where to insert |
480 // find position where to insert |
421 struct cx_hash_map_element_s *bucket_next = new_buckets[new_slot]; |
481 struct cx_hash_map_element_s *bucket_next = new_buckets[new_slot]; |
422 struct cx_hash_map_element_s *bucket_prev = NULL; |
482 struct cx_hash_map_element_s *bucket_prev = NULL; |
423 while (bucket_next != NULL && bucket_next->key.hash < elm->key.hash) { |
483 while (bucket_next != NULL && |
|
484 bucket_next->key.hash < elm->key.hash) { |
424 bucket_prev = bucket_next; |
485 bucket_prev = bucket_next; |
425 bucket_next = bucket_next->next; |
486 bucket_next = bucket_next->next; |
426 } |
487 } |
427 |
488 |
428 // insert |
489 // insert |