ucx/hash_map.c

changeset 852
83fdf679df99
parent 816
839fefbdedc7
equal deleted inserted replaced
850:bbe2925eb590 852:83fdf679df99
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE. 26 * POSSIBILITY OF SUCH DAMAGE.
27 */ 27 */
28 28
29 #include "cx/hash_map.h" 29 #include "cx/hash_map.h"
30 #include "cx/utils.h"
31 30
32 #include <string.h> 31 #include <string.h>
33 #include <assert.h> 32 #include <assert.h>
33 #include <errno.h>
34 34
35 struct cx_hash_map_element_s { 35 struct cx_hash_map_element_s {
36 /** A pointer to the next element in the current bucket. */ 36 /** A pointer to the next element in the current bucket. */
37 struct cx_hash_map_element_s *next; 37 struct cx_hash_map_element_s *next;
38 38
43 char data[]; 43 char data[];
44 }; 44 };
45 45
46 static void cx_hash_map_clear(struct cx_map_s *map) { 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; 47 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
48 cx_for_n(i, hash_map->bucket_count) { 48 for (size_t i = 0; i < hash_map->bucket_count; i++) {
49 struct cx_hash_map_element_s *elem = hash_map->buckets[i]; 49 struct cx_hash_map_element_s *elem = hash_map->buckets[i];
50 if (elem != NULL) { 50 if (elem != NULL) {
51 do { 51 do {
52 struct cx_hash_map_element_s *next = elem->next; 52 struct cx_hash_map_element_s *next = elem->next;
53 // invoke the destructor 53 // invoke the destructor
82 CxMap *map, 82 CxMap *map,
83 CxHashKey key, 83 CxHashKey key,
84 void *value 84 void *value
85 ) { 85 ) {
86 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; 86 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
87 CxAllocator const *allocator = map->collection.allocator; 87 const CxAllocator *allocator = map->collection.allocator;
88 88
89 unsigned hash = key.hash; 89 unsigned hash = key.hash;
90 if (hash == 0) { 90 if (hash == 0) {
91 cx_hash_murmur(&key); 91 cx_hash_murmur(&key);
92 hash = key.hash; 92 hash = key.hash;
113 // allocate new element 113 // allocate new element
114 struct cx_hash_map_element_s *e = cxMalloc( 114 struct cx_hash_map_element_s *e = cxMalloc(
115 allocator, 115 allocator,
116 sizeof(struct cx_hash_map_element_s) + map->collection.elem_size 116 sizeof(struct cx_hash_map_element_s) + map->collection.elem_size
117 ); 117 );
118 if (e == NULL) { 118 if (e == NULL) return -1;
119 return -1;
120 }
121 119
122 // write the value 120 // write the value
123 if (map->collection.store_pointer) { 121 if (map->collection.store_pointer) {
124 memcpy(e->data, &value, sizeof(void *)); 122 memcpy(e->data, &value, sizeof(void *));
125 } else { 123 } else {
126 memcpy(e->data, value, map->collection.elem_size); 124 memcpy(e->data, value, map->collection.elem_size);
127 } 125 }
128 126
129 // copy the key 127 // copy the key
130 void *kd = cxMalloc(allocator, key.len); 128 void *kd = cxMalloc(allocator, key.len);
131 if (kd == NULL) { 129 if (kd == NULL) return -1;
132 return -1;
133 }
134 memcpy(kd, key.data, key.len); 130 memcpy(kd, key.data, key.len);
135 e->key.data = kd; 131 e->key.data = kd;
136 e->key.len = key.len; 132 e->key.len = key.len;
137 e->key.hash = hash; 133 e->key.hash = hash;
138 134
171 } 167 }
172 168
173 /** 169 /**
174 * Helper function to avoid code duplication. 170 * Helper function to avoid code duplication.
175 * 171 *
172 * If @p remove is true, and @p targetbuf is @c NULL, the element
173 * will be destroyed when found.
174 *
175 * If @p remove is true, and @p targetbuf is set, the element will
176 * be copied to that buffer and no destructor function is called.
177 *
178 * If @p remove is false, @p targetbuf must not be non-null and
179 * either the pointer, when the map is storing pointers, is copied
180 * to the target buffer, or a pointer to the stored object will
181 * be copied to the target buffer.
182 *
176 * @param map the map 183 * @param map the map
177 * @param key the key to look up 184 * @param key the key to look up
185 * @param targetbuf see description
178 * @param remove flag indicating whether the looked up entry shall be removed 186 * @param remove flag indicating whether the looked up entry shall be removed
179 * @param destroy flag indicating whether the destructor shall be invoked 187 * @return zero, if the key was found, non-zero otherwise
180 * @return a pointer to the value corresponding to the key or \c NULL
181 */ 188 */
182 static void *cx_hash_map_get_remove( 189 static int cx_hash_map_get_remove(
183 CxMap *map, 190 CxMap *map,
184 CxHashKey key, 191 CxHashKey key,
185 bool remove, 192 void *targetbuf,
186 bool destroy 193 bool remove
187 ) { 194 ) {
188 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; 195 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
189 196
190 unsigned hash = key.hash; 197 unsigned hash = key.hash;
191 if (hash == 0) { 198 if (hash == 0) {
197 struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; 204 struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
198 struct cx_hash_map_element_s *prev = NULL; 205 struct cx_hash_map_element_s *prev = NULL;
199 while (elm && elm->key.hash <= hash) { 206 while (elm && elm->key.hash <= hash) {
200 if (elm->key.hash == hash && elm->key.len == key.len) { 207 if (elm->key.hash == hash && elm->key.len == key.len) {
201 if (memcmp(elm->key.data, key.data, key.len) == 0) { 208 if (memcmp(elm->key.data, key.data, key.len) == 0) {
202 void *data = NULL; 209 if (remove) {
203 if (destroy) { 210 if (targetbuf == NULL) {
204 cx_invoke_destructor(map, elm->data); 211 cx_invoke_destructor(map, elm->data);
212 } else {
213 memcpy(targetbuf, elm->data, map->collection.elem_size);
214 }
215 cx_hash_map_unlink(hash_map, slot, prev, elm);
205 } else { 216 } else {
217 assert(targetbuf != NULL);
218 void *data = NULL;
206 if (map->collection.store_pointer) { 219 if (map->collection.store_pointer) {
207 data = *(void **) elm->data; 220 data = *(void **) elm->data;
208 } else { 221 } else {
209 data = elm->data; 222 data = elm->data;
210 } 223 }
224 memcpy(targetbuf, &data, sizeof(void *));
211 } 225 }
212 if (remove) { 226 return 0;
213 cx_hash_map_unlink(hash_map, slot, prev, elm);
214 }
215 return data;
216 } 227 }
217 } 228 }
218 prev = elm; 229 prev = elm;
219 elm = prev->next; 230 elm = prev->next;
220 } 231 }
221 232
222 return NULL; 233 return 1;
223 } 234 }
224 235
225 static void *cx_hash_map_get( 236 static void *cx_hash_map_get(
226 CxMap const *map, 237 const CxMap *map,
227 CxHashKey key 238 CxHashKey key
228 ) { 239 ) {
229 // we can safely cast, because we know the map stays untouched 240 // we can safely cast, because we know the map stays untouched
230 return cx_hash_map_get_remove((CxMap *) map, key, false, false); 241 void *ptr = NULL;
231 } 242 int found = cx_hash_map_get_remove((CxMap *) map, key, &ptr, false);
232 243 return found == 0 ? ptr : NULL;
233 static void *cx_hash_map_remove( 244 }
245
246 static int cx_hash_map_remove(
234 CxMap *map, 247 CxMap *map,
235 CxHashKey key, 248 CxHashKey key,
236 bool destroy 249 void *targetbuf
237 ) { 250 ) {
238 return cx_hash_map_get_remove(map, key, true, destroy); 251 return cx_hash_map_get_remove(map, key, targetbuf, true);
239 } 252 }
240 253
241 static void *cx_hash_map_iter_current_entry(void const *it) { 254 static void *cx_hash_map_iter_current_entry(const void *it) {
242 struct cx_iterator_s const *iter = it; 255 const struct cx_iterator_s *iter = it;
243 // struct has to have a compatible signature 256 // struct has to have a compatible signature
244 return (struct cx_map_entry_s *) &(iter->kv_data); 257 return (struct cx_map_entry_s *) &(iter->kv_data);
245 } 258 }
246 259
247 static void *cx_hash_map_iter_current_key(void const *it) { 260 static void *cx_hash_map_iter_current_key(const void *it) {
248 struct cx_iterator_s const *iter = it; 261 const struct cx_iterator_s *iter = it;
249 struct cx_hash_map_element_s *elm = iter->elem_handle; 262 struct cx_hash_map_element_s *elm = iter->elem_handle;
250 return &elm->key; 263 return &elm->key;
251 } 264 }
252 265
253 static void *cx_hash_map_iter_current_value(void const *it) { 266 static void *cx_hash_map_iter_current_value(const void *it) {
254 struct cx_iterator_s const *iter = it; 267 const struct cx_iterator_s *iter = it;
255 struct cx_hash_map_s const *map = iter->src_handle.c; 268 const struct cx_hash_map_s *map = iter->src_handle.c;
256 struct cx_hash_map_element_s *elm = iter->elem_handle; 269 struct cx_hash_map_element_s *elm = iter->elem_handle;
257 if (map->base.collection.store_pointer) { 270 if (map->base.collection.store_pointer) {
258 return *(void **) elm->data; 271 return *(void **) elm->data;
259 } else { 272 } else {
260 return elm->data; 273 return elm->data;
261 } 274 }
262 } 275 }
263 276
264 static bool cx_hash_map_iter_valid(void const *it) { 277 static bool cx_hash_map_iter_valid(const void *it) {
265 struct cx_iterator_s const *iter = it; 278 const struct cx_iterator_s *iter = it;
266 return iter->elem_handle != NULL; 279 return iter->elem_handle != NULL;
267 } 280 }
268 281
269 static void cx_hash_map_iter_next(void *it) { 282 static void cx_hash_map_iter_next(void *it) {
270 struct cx_iterator_s *iter = it; 283 struct cx_iterator_s *iter = it;
322 } 335 }
323 } 336 }
324 } 337 }
325 338
326 static CxIterator cx_hash_map_iterator( 339 static CxIterator cx_hash_map_iterator(
327 CxMap const *map, 340 const CxMap *map,
328 enum cx_map_iterator_type type 341 enum cx_map_iterator_type type
329 ) { 342 ) {
330 CxIterator iter; 343 CxIterator iter;
331 344
332 iter.src_handle.c = map; 345 iter.src_handle.c = map;
344 case CX_MAP_ITERATOR_VALUES: 357 case CX_MAP_ITERATOR_VALUES:
345 iter.elem_size = map->collection.elem_size; 358 iter.elem_size = map->collection.elem_size;
346 iter.base.current = cx_hash_map_iter_current_value; 359 iter.base.current = cx_hash_map_iter_current_value;
347 break; 360 break;
348 default: 361 default:
349 assert(false); 362 assert(false); // LCOV_EXCL_LINE
350 } 363 }
351 364
352 iter.base.valid = cx_hash_map_iter_valid; 365 iter.base.valid = cx_hash_map_iter_valid;
353 iter.base.next = cx_hash_map_iter_next; 366 iter.base.next = cx_hash_map_iter_next;
354 iter.base.remove = false; 367 iter.base.remove = false;
387 cx_hash_map_remove, 400 cx_hash_map_remove,
388 cx_hash_map_iterator, 401 cx_hash_map_iterator,
389 }; 402 };
390 403
391 CxMap *cxHashMapCreate( 404 CxMap *cxHashMapCreate(
392 CxAllocator const *allocator, 405 const CxAllocator *allocator,
393 size_t itemsize, 406 size_t itemsize,
394 size_t buckets 407 size_t buckets
395 ) { 408 ) {
409 if (allocator == NULL) {
410 allocator = cxDefaultAllocator;
411 }
412
396 if (buckets == 0) { 413 if (buckets == 0) {
397 // implementation defined default 414 // implementation defined default
398 buckets = 16; 415 buckets = 16;
399 } 416 }
400 417
404 421
405 // initialize hash map members 422 // initialize hash map members
406 map->bucket_count = buckets; 423 map->bucket_count = buckets;
407 map->buckets = cxCalloc(allocator, buckets, 424 map->buckets = cxCalloc(allocator, buckets,
408 sizeof(struct cx_hash_map_element_s *)); 425 sizeof(struct cx_hash_map_element_s *));
409 if (map->buckets == NULL) { 426 if (map->buckets == NULL) { // LCOV_EXCL_START
410 cxFree(allocator, map); 427 cxFree(allocator, map);
411 return NULL; 428 return NULL;
412 } 429 } // LCOV_EXCL_STOP
413 430
414 // initialize base members 431 // initialize base members
415 map->base.cl = &cx_hash_map_class; 432 map->base.cl = &cx_hash_map_class;
416 map->base.collection.allocator = allocator; 433 map->base.collection.allocator = allocator;
417 434
429 int cxMapRehash(CxMap *map) { 446 int cxMapRehash(CxMap *map) {
430 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; 447 struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
431 if (map->collection.size > ((hash_map->bucket_count * 3) >> 2)) { 448 if (map->collection.size > ((hash_map->bucket_count * 3) >> 2)) {
432 449
433 size_t new_bucket_count = (map->collection.size * 5) >> 1; 450 size_t new_bucket_count = (map->collection.size * 5) >> 1;
451 if (new_bucket_count < hash_map->bucket_count) {
452 errno = EOVERFLOW;
453 return 1;
454 }
434 struct cx_hash_map_element_s **new_buckets = cxCalloc( 455 struct cx_hash_map_element_s **new_buckets = cxCalloc(
435 map->collection.allocator, 456 map->collection.allocator,
436 new_bucket_count, sizeof(struct cx_hash_map_element_s *) 457 new_bucket_count, sizeof(struct cx_hash_map_element_s *)
437 ); 458 );
438 459
439 if (new_buckets == NULL) { 460 if (new_buckets == NULL) return 1;
440 return 1;
441 }
442 461
443 // iterate through the elements and assign them to their new slots 462 // iterate through the elements and assign them to their new slots
444 cx_for_n(slot, hash_map->bucket_count) { 463 for (size_t slot = 0; slot < hash_map->bucket_count; slot++) {
445 struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; 464 struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
446 while (elm != NULL) { 465 while (elm != NULL) {
447 struct cx_hash_map_element_s *next = elm->next; 466 struct cx_hash_map_element_s *next = elm->next;
448 size_t new_slot = elm->key.hash % new_bucket_count; 467 size_t new_slot = elm->key.hash % new_bucket_count;
449 468

mercurial