| 96 list->map->map_base.base.collection.destructor_data = NULL; |
96 list->map->map_base.base.collection.destructor_data = NULL; |
| 97 } |
97 } |
| 98 } |
98 } |
| 99 |
99 |
| 100 static CxHashKey *cx_kv_list_loc_key(cx_kv_list *list, void *node_data) { |
100 static CxHashKey *cx_kv_list_loc_key(cx_kv_list *list, void *node_data) { |
| 101 return (CxHashKey*)((char*)node_data + list->list.base.collection.elem_size); |
101 return (CxHashKey*)((char*)node_data - list->list.loc_data + list->list.loc_extra); |
| 102 } |
102 } |
| 103 |
103 |
| 104 static void cx_kvl_deallocate(struct cx_list_s *list) { |
104 static void cx_kvl_deallocate(struct cx_list_s *list) { |
| 105 cx_kv_list *kv_list = (cx_kv_list*)list; |
105 cx_kv_list *kv_list = (cx_kv_list*)list; |
| 106 // patch the destructors |
106 // patch the destructors |
| 219 // (the first to find the index, the second to get a pointer) |
219 // (the first to find the index, the second to get a pointer) |
| 220 if (list->collection.size == 0) return 0; |
220 if (list->collection.size == 0) return 0; |
| 221 |
221 |
| 222 size_t index; |
222 size_t index; |
| 223 cx_linked_list *ll = &kv_list->list; |
223 cx_linked_list *ll = &kv_list->list; |
| 224 char *node = cx_linked_list_find( |
224 char *node = cx_linked_list_find_c( |
| 225 ll->begin, |
225 ll->begin, |
| 226 ll->loc_next, ll->loc_data, |
226 ll->loc_next, ll->loc_data, |
| 227 list->collection.cmpfunc, elem, |
227 elem, &index, |
| 228 &index |
228 cx_list_compare_wrapper, |
| |
229 list |
| 229 ); |
230 ); |
| 230 if (node == NULL) { |
231 if (node == NULL) { |
| 231 return list->collection.size; |
232 return list->collection.size; |
| 232 } |
233 } |
| 233 if (remove) { |
234 if (remove) { |
| 294 cx_kv_list_update_destructors(kv_list); |
296 cx_kv_list_update_destructors(kv_list); |
| 295 kv_list->list_methods->clear(&kv_list->list.base); |
297 kv_list->list_methods->clear(&kv_list->list.base); |
| 296 kv_list->map_methods->clear(map); |
298 kv_list->map_methods->clear(map); |
| 297 } |
299 } |
| 298 |
300 |
| 299 static void *cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) { |
301 static void *cx_kvl_map_get(const CxMap *map, CxHashKey key) { |
| 300 cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; |
|
| 301 // if the hash has not yet been computed, do it now |
|
| 302 if (key.hash == 0) { |
|
| 303 cx_hash_murmur(&key); |
|
| 304 } |
|
| 305 |
|
| 306 // reserve memory in the map first |
|
| 307 void **map_data = kv_list->map_methods->put(map, key, NULL); |
|
| 308 if (map_data == NULL) return NULL; // LCOV_EXCL_LINE |
|
| 309 |
|
| 310 // insert the data into the list (which most likely destroys the sorted property) |
|
| 311 kv_list->list.base.collection.sorted = false; |
|
| 312 void *node_data = kv_list->list_methods->insert_element( |
|
| 313 &kv_list->list.base, kv_list->list.base.collection.size, |
|
| 314 kv_list->list.base.collection.store_pointer ? &value : value); |
|
| 315 if (node_data == NULL) { // LCOV_EXCL_START |
|
| 316 // non-destructively remove the key again |
|
| 317 kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data); |
|
| 318 return NULL; |
|
| 319 } // LCOV_EXCL_STOP |
|
| 320 |
|
| 321 // write the node pointer to the map entry |
|
| 322 *map_data = node_data; |
|
| 323 |
|
| 324 // copy the key to the node data |
|
| 325 CxHashKey *key_ptr = cx_kv_list_loc_key(kv_list, node_data); |
|
| 326 *key_ptr = key; |
|
| 327 |
|
| 328 // we must return node_data here and not map_data, |
|
| 329 // because the node_data is the actual element of this collection |
|
| 330 return node_data; |
|
| 331 } |
|
| 332 |
|
| 333 void *cx_kvl_map_get(const CxMap *map, CxHashKey key) { |
|
| 334 cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; |
302 cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; |
| 335 void *node_data = kv_list->map_methods->get(map, key); |
303 void *node_data = kv_list->map_methods->get(map, key); |
| 336 if (node_data == NULL) return NULL; // LCOV_EXCL_LINE |
304 if (node_data == NULL) return NULL; // LCOV_EXCL_LINE |
| 337 // return the node data |
305 // return the node data |
| 338 return kv_list->list.base.collection.store_pointer ? *(void**)node_data : node_data; |
306 return kv_list->list.base.collection.store_pointer ? *(void**)node_data : node_data; |
| 339 } |
307 } |
| 340 |
308 |
| 341 int cx_kvl_map_remove(CxMap *map, CxHashKey key, void *targetbuf) { |
309 static int cx_kvl_map_remove(CxMap *map, CxHashKey key, void *targetbuf) { |
| 342 cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; |
310 cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; |
| 343 |
311 |
| 344 void *node_data; |
312 void *node_data; |
| 345 if (kv_list->map_methods->remove(map, key, &node_data)) { |
313 if (kv_list->map_methods->remove(map, key, &node_data)) { |
| 346 return 1; |
314 return 1; |
| 377 |
345 |
| 378 // deallocate the node |
346 // deallocate the node |
| 379 cxFree(kv_list->list.base.collection.allocator, node_ptr); |
347 cxFree(kv_list->list.base.collection.allocator, node_ptr); |
| 380 |
348 |
| 381 return 0; |
349 return 0; |
| |
350 } |
| |
351 |
| |
352 static CxMapEntry cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) { |
| |
353 cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; |
| |
354 // if the hash has not yet been computed, do it now |
| |
355 if (key.hash == 0) { |
| |
356 cx_hash_murmur(&key); |
| |
357 } |
| |
358 |
| |
359 // remove any existing element first |
| |
360 cx_kvl_map_remove(map, key, NULL); |
| |
361 |
| |
362 // now reserve new memory in the map |
| |
363 CxMapEntry map_entry = kv_list->map_methods->put(map, key, NULL); |
| |
364 if (map_entry.key == NULL) return (CxMapEntry){NULL, NULL}; // LCOV_EXCL_LINE |
| |
365 |
| |
366 // insert the data into the list (which most likely destroys the sorted property) |
| |
367 kv_list->list.base.collection.sorted = false; |
| |
368 void *node_data = kv_list->list_methods->insert_element( |
| |
369 &kv_list->list.base, kv_list->list.base.collection.size, |
| |
370 kv_list->list.base.collection.store_pointer ? &value : value); |
| |
371 if (node_data == NULL) { // LCOV_EXCL_START |
| |
372 // non-destructively remove the key again |
| |
373 void *dummy; |
| |
374 kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &dummy); |
| |
375 return (CxMapEntry){NULL, NULL}; |
| |
376 } // LCOV_EXCL_STOP |
| |
377 |
| |
378 // write the node pointer to the map entry |
| |
379 *(void**)map_entry.value = node_data; |
| |
380 |
| |
381 // copy the key to the node data |
| |
382 CxHashKey *key_ptr = cx_kv_list_loc_key(kv_list, node_data); |
| |
383 *key_ptr = *map_entry.key; |
| |
384 |
| |
385 // we must return an entry that points to the node data! |
| |
386 return (CxMapEntry ){key_ptr, node_data}; |
| 382 } |
387 } |
| 383 |
388 |
| 384 static void *cx_kvl_iter_current_entry(const void *it) { |
389 static void *cx_kvl_iter_current_entry(const void *it) { |
| 385 const CxMapIterator *iter = it; |
390 const CxMapIterator *iter = it; |
| 386 return (void*)&iter->entry; |
391 return (void*)&iter->entry; |
| 453 static bool cx_kvl_iter_valid(const void *it) { |
458 static bool cx_kvl_iter_valid(const void *it) { |
| 454 const CxMapIterator *iter = it; |
459 const CxMapIterator *iter = it; |
| 455 return iter->elem != NULL; |
460 return iter->elem != NULL; |
| 456 } |
461 } |
| 457 |
462 |
| 458 CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_type type) { |
463 static CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_type type) { |
| 459 CxMapIterator iter = {0}; |
464 CxMapIterator iter = {0}; |
| 460 |
465 |
| 461 iter.type = type; |
466 iter.type = type; |
| 462 iter.map = (CxMap*)map; |
467 iter.map = (CxMap*)map; |
| 463 // although we iterate over the list, we only report that many elements that have a key in the map |
468 // although we iterate over the list, we only report that many elements that have a key in the map |
| 507 } |
512 } |
| 508 |
513 |
| 509 return iter; |
514 return iter; |
| 510 } |
515 } |
| 511 |
516 |
| |
517 static int cx_kvl_change_capacity(struct cx_list_s *list, |
| |
518 cx_attr_unused size_t cap) { |
| |
519 // since our backing list is a linked list, we don't need to do much here, |
| |
520 // but rehashing the map is quite useful |
| |
521 cx_kv_list *kv_list = (cx_kv_list*)list; |
| |
522 cxMapRehash(&kv_list->map->map_base.base); |
| |
523 return 0; |
| |
524 } |
| |
525 |
| 512 static cx_list_class cx_kv_list_class = { |
526 static cx_list_class cx_kv_list_class = { |
| 513 cx_kvl_deallocate, |
527 cx_kvl_deallocate, |
| 514 cx_kvl_insert_element, |
528 cx_kvl_insert_element, |
| 515 cx_kvl_insert_array, |
529 cx_kvl_insert_array, |
| 516 cx_kvl_insert_sorted, |
530 cx_kvl_insert_sorted, |
| 536 cx_kvl_map_iterator, |
551 cx_kvl_map_iterator, |
| 537 }; |
552 }; |
| 538 |
553 |
| 539 CxList *cxKvListCreate( |
554 CxList *cxKvListCreate( |
| 540 const CxAllocator *allocator, |
555 const CxAllocator *allocator, |
| 541 cx_compare_func comparator, |
|
| 542 size_t elem_size |
556 size_t elem_size |
| 543 ) { |
557 ) { |
| 544 if (allocator == NULL) { |
558 if (allocator == NULL) { |
| 545 allocator = cxDefaultAllocator; |
559 allocator = cxDefaultAllocator; |
| 546 } |
560 } |
| 547 |
561 |
| 548 // create a normal linked list and a normal hash map, first |
562 // create a normal linked list and a normal hash map, first |
| 549 CxList *list = cxLinkedListCreate(allocator, comparator, elem_size); |
563 CxList *list = cxLinkedListCreate(allocator, elem_size); |
| 550 if (list == NULL) return NULL; // LCOV_EXCL_LINE |
564 if (list == NULL) return NULL; // LCOV_EXCL_LINE |
| 551 cx_linked_list *ll = (cx_linked_list*)list; |
565 cx_linked_list *ll = (cx_linked_list*)list; |
| 552 ll->extra_data_len = sizeof(CxHashKey); |
566 cx_linked_list_extra_data(ll, sizeof(CxHashKey)); |
| 553 CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0); |
567 CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0); |
| 554 if (map == NULL) { // LCOV_EXCL_START |
568 if (map == NULL) { // LCOV_EXCL_START |
| 555 cxListFree(list); |
569 cxListFree(list); |
| 556 return NULL; |
570 return NULL; |
| 557 } // LCOV_EXCL_STOP |
571 } // LCOV_EXCL_STOP |
| 584 kv_map->list = kv_list; |
598 kv_map->list = kv_list; |
| 585 |
599 |
| 586 // remember the base methods and override them |
600 // remember the base methods and override them |
| 587 kv_list->map_methods = map->cl; |
601 kv_list->map_methods = map->cl; |
| 588 map->cl = &cx_kv_map_class; |
602 map->cl = &cx_kv_map_class; |
| 589 if (list->climpl == NULL) { |
603 kv_list->list_methods = list->cl; |
| 590 kv_list->list_methods = list->cl; |
604 list->cl = &cx_kv_list_class; |
| 591 list->cl = &cx_kv_list_class; |
|
| 592 } else { |
|
| 593 kv_list->list_methods = list->climpl; |
|
| 594 list->climpl = &cx_kv_list_class; |
|
| 595 } |
|
| 596 |
605 |
| 597 return list; |
606 return list; |
| 598 } |
607 } |
| 599 |
608 |
| 600 CxMap *cxKvListCreateAsMap( |
609 CxMap *cxKvListCreateAsMap( |
| 601 const CxAllocator *allocator, |
610 const CxAllocator *allocator, |
| 602 cx_compare_func comparator, |
|
| 603 size_t elem_size |
611 size_t elem_size |
| 604 ) { |
612 ) { |
| 605 CxList *list = cxKvListCreate(allocator, comparator, elem_size); |
613 CxList *list = cxKvListCreate(allocator, elem_size); |
| 606 return list == NULL ? NULL : cxKvListAsMap(list); |
614 return list == NULL ? NULL : cxKvListAsMap(list); |
| 607 } |
615 } |
| 608 |
616 |
| 609 CxList *cxKvListAsList(CxMap *map) { |
617 CxList *cxKvListAsList(CxMap *map) { |
| 610 return &((struct cx_kv_list_map_s*)map)->list->list.base; |
618 return &((struct cx_kv_list_map_s*)map)->list->list.base; |
| 633 if (existing != NULL) { |
641 if (existing != NULL) { |
| 634 // the key is already assigned to another node, we disallow re-assignment |
642 // the key is already assigned to another node, we disallow re-assignment |
| 635 return 1; |
643 return 1; |
| 636 } |
644 } |
| 637 |
645 |
| 638 // add the key to the map; |
646 // add the key to the map |
| 639 if (NULL == kv_list->map_methods->put(&kv_list->map->map_base.base, key, node_data)) { |
647 const CxMapEntry entry = kv_list->map_methods->put( |
| 640 return 1; // LCOV_EXCL_LINE |
648 &kv_list->map->map_base.base, key, node_data); |
| 641 } |
649 if (entry.key == NULL) return 1; // LCOV_EXCL_LINE |
| 642 |
650 |
| 643 // write the key to the list's node |
651 // write the key to the list's node |
| 644 CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data); |
652 CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data); |
| 645 *loc_key = key; |
653 *loc_key = *entry.key; |
| 646 |
654 |
| 647 return 0; |
655 return 0; |
| 648 } |
656 } |
| 649 |
657 |
| 650 int cxKvListRemoveKey(CxList *list, size_t index) { |
658 int cxKvListRemoveKey(CxList *list, size_t index) { |
| 682 list->collection.sorted = false; |
690 list->collection.sorted = false; |
| 683 |
691 |
| 684 cx_kv_list *kv_list = (cx_kv_list*)list; |
692 cx_kv_list *kv_list = (cx_kv_list*)list; |
| 685 |
693 |
| 686 // reserve memory in the map |
694 // reserve memory in the map |
| 687 void **map_data = kv_list->map_methods->put(&kv_list->map->map_base.base, key, NULL); |
695 CxMapEntry map_entry = kv_list->map_methods->put(&kv_list->map->map_base.base, key, NULL); |
| 688 if (map_data == NULL) return 1; // LCOV_EXCL_LINE |
696 if (map_entry.key == NULL) return 1; // LCOV_EXCL_LINE |
| 689 |
697 |
| 690 // insert the node |
698 // insert the node |
| 691 void *node_data = kv_list->list_methods->insert_element(&kv_list->list.base, index, |
699 void *node_data = kv_list->list_methods->insert_element(&kv_list->list.base, index, |
| 692 kv_list->list.base.collection.store_pointer ? &value : value); |
700 kv_list->list.base.collection.store_pointer ? &value : value); |
| 693 if (node_data == NULL) { // LCOV_EXCL_START |
701 if (node_data == NULL) { // LCOV_EXCL_START |
| 694 // non-destructively remove the key again |
702 // non-destructively remove the key again |
| 695 kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data); |
703 void *dummy; |
| |
704 kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &dummy); |
| 696 return 1; |
705 return 1; |
| 697 } // LCOV_EXCL_STOP |
706 } // LCOV_EXCL_STOP |
| 698 *map_data = node_data; |
707 *(void**)map_entry.value = node_data; |
| 699 |
708 |
| 700 // write the key to the node |
709 // write the key to the node |
| 701 CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data); |
710 CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data); |
| 702 *loc_key = key; |
711 *loc_key = *map_entry.key; |
| 703 |
712 |
| 704 return 0; |
713 return 0; |
| 705 } |
714 } |