UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2025 Mike Becker, Olaf Wintermann All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * 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 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "cx/kv_list.h" 30 #include "cx/hash_map.h" 31 #include "cx/linked_list.h" 32 33 #include <string.h> 34 #include <assert.h> 35 36 typedef struct cx_kv_list_s cx_kv_list; 37 38 struct cx_kv_list_map_s { 39 struct cx_hash_map_s map_base; 40 /** Back-reference to the list. */ 41 cx_kv_list *list; 42 }; 43 44 struct cx_kv_list_s { 45 struct cx_linked_list_s list; 46 /** The lookup map - stores pointers to the nodes. */ 47 struct cx_kv_list_map_s *map; 48 const cx_list_class *list_methods; 49 const cx_map_class *map_methods; 50 cx_destructor_func list_destr; 51 cx_destructor_func2 list_destr2; 52 void *list_destr_data; 53 cx_destructor_func map_destr; 54 cx_destructor_func2 map_destr2; 55 void *map_destr_data; 56 }; 57 58 static void cx_kv_list_destructor_wrapper(void *list_ptr, void *elem) { 59 const cx_kv_list *kv_list = list_ptr; 60 // list destructor is already called with proper deref of the elem 61 if (kv_list->list_destr) { 62 kv_list->list_destr(elem); 63 } 64 if (kv_list->list_destr2) { 65 kv_list->list_destr2(kv_list->list_destr_data, elem); 66 } 67 if (kv_list->map_destr) { 68 kv_list->map_destr(elem); 69 } 70 if (kv_list->map_destr2) { 71 kv_list->map_destr2(kv_list->map_destr_data, elem); 72 } 73 } 74 75 static void cx_kv_list_update_destructors(cx_kv_list *list) { 76 // we copy the destructors to our custom fields and register 77 // an own destructor function which invokes all these 78 if (list->list.base.collection.simple_destructor != NULL) { 79 list->list_destr = list->list.base.collection.simple_destructor; 80 list->list.base.collection.simple_destructor = NULL; 81 } 82 if (list->list.base.collection.advanced_destructor != cx_kv_list_destructor_wrapper) { 83 list->list_destr2 = list->list.base.collection.advanced_destructor; 84 list->list_destr_data = list->list.base.collection.destructor_data; 85 list->list.base.collection.advanced_destructor = cx_kv_list_destructor_wrapper; 86 list->list.base.collection.destructor_data = list; 87 } 88 if (list->map->map_base.base.collection.simple_destructor != NULL) { 89 list->map_destr = list->map->map_base.base.collection.simple_destructor; 90 list->map->map_base.base.collection.simple_destructor = NULL; 91 } 92 if (list->map->map_base.base.collection.advanced_destructor != NULL) { 93 list->map_destr2 = list->map->map_base.base.collection.advanced_destructor; 94 list->map_destr_data = list->map->map_base.base.collection.destructor_data; 95 list->map->map_base.base.collection.advanced_destructor = NULL; 96 list->map->map_base.base.collection.destructor_data = NULL; 97 } 98 } 99 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); 102 } 103 104 static void cx_kvl_deallocate(struct cx_list_s *list) { 105 cx_kv_list *kv_list = (cx_kv_list*)list; 106 // patch the destructors 107 cx_kv_list_update_destructors(kv_list); 108 kv_list->map_methods->deallocate(&kv_list->map->map_base.base); 109 // then free the list, now the destructors may be called 110 kv_list->list_methods->deallocate(list); 111 } 112 113 static void *cx_kvl_insert_element( 114 struct cx_list_s *list, 115 size_t index, 116 const void *data 117 ) { 118 cx_kv_list *kv_list = (cx_kv_list*)list; 119 return kv_list->list_methods->insert_element(list, index, data); 120 } 121 122 static size_t cx_kvl_insert_array( 123 struct cx_list_s *list, 124 size_t index, 125 const void *data, 126 size_t n 127 ) { 128 cx_kv_list *kv_list = (cx_kv_list*)list; 129 return kv_list->list_methods->insert_array(list, index, data, n); 130 } 131 132 static size_t cx_kvl_insert_sorted( 133 struct cx_list_s *list, 134 const void *sorted_data, 135 size_t n 136 ) { 137 cx_kv_list *kv_list = (cx_kv_list*)list; 138 return kv_list->list_methods->insert_sorted(list, sorted_data, n); 139 } 140 141 static size_t cx_kvl_insert_unique( 142 struct cx_list_s *list, 143 const void *sorted_data, 144 size_t n 145 ) { 146 cx_kv_list *kv_list = (cx_kv_list*)list; 147 return kv_list->list_methods->insert_unique(list, sorted_data, n); 148 } 149 150 static int cx_kvl_insert_iter( 151 struct cx_iterator_s *iter, 152 const void *elem, 153 int prepend 154 ) { 155 cx_kv_list *kv_list = iter->src_handle; 156 return kv_list->list_methods->insert_iter(iter, elem, prepend); 157 } 158 159 static size_t cx_kvl_remove( 160 struct cx_list_s *list, 161 size_t index, 162 size_t num, 163 void *targetbuf 164 ) { 165 cx_kv_list *kv_list = (cx_kv_list*)list; 166 // patch the destructors 167 // we also have to do that when targetbuf is NULL, 168 // because we do not want wrong destructors to be called when we remove keys from the map 169 cx_kv_list_update_destructors(kv_list); 170 // iterate through the elements first and remove their keys from the map 171 CxIterator iter = kv_list->list_methods->iterator(list, index, false); 172 for (size_t i = 0; i < num && cxIteratorValid(iter); i++) { 173 void *node_data = cxIteratorCurrent(iter); 174 CxHashKey *key = cx_kv_list_loc_key(kv_list, node_data); 175 // when the hash is zero, there is no key assigned to that element 176 if (key->hash != 0) { 177 kv_list->map_methods->remove(&kv_list->map->map_base.base, *key, NULL); 178 } 179 cxIteratorNext(iter); 180 } 181 return kv_list->list_methods->remove(list, index, num, targetbuf); 182 } 183 184 static void cx_kvl_clear(struct cx_list_s *list) { 185 cx_kv_list *kv_list = (cx_kv_list*)list; 186 // patch the destructors 187 cx_kv_list_update_destructors(kv_list); 188 // clear the list 189 kv_list->list_methods->clear(list); 190 // then clear the map 191 kv_list->map_methods->clear(&kv_list->map->map_base.base); 192 } 193 194 static int cx_kvl_swap( 195 struct cx_list_s *list, 196 size_t i, 197 size_t j 198 ) { 199 cx_kv_list *kv_list = (cx_kv_list*)list; 200 return kv_list->list_methods->swap(list, i, j); 201 } 202 203 static void *cx_kvl_at( 204 const struct cx_list_s *list, 205 size_t index 206 ) { 207 const cx_kv_list *kv_list = (const cx_kv_list*)list; 208 return kv_list->list_methods->at(list, index); 209 } 210 211 static size_t cx_kvl_find_remove( 212 struct cx_list_s *list, 213 const void *elem, 214 bool remove 215 ) { 216 cx_kv_list *kv_list = (cx_kv_list*)list; 217 // we do not use the original list methods, 218 // because that would need two passes for removal 219 // (the first to find the index, the second to get a pointer) 220 if (list->collection.size == 0) return 0; 221 222 size_t index; 223 cx_linked_list *ll = &kv_list->list; 224 char *node = cx_linked_list_find( 225 ll->begin, 226 ll->loc_next, ll->loc_data, 227 list->collection.cmpfunc, elem, 228 &index 229 ); 230 if (node == NULL) { 231 return list->collection.size; 232 } 233 if (remove) { 234 cx_kv_list_update_destructors(kv_list); 235 cx_invoke_advanced_destructor(list, node + ll->loc_data); 236 cx_linked_list_remove(&ll->begin, &ll->end, 237 ll->loc_prev, ll->loc_next, node); 238 CxHashKey *key = cx_kv_list_loc_key(kv_list, node + ll->loc_data); 239 if (key->hash != 0) { 240 kv_list->map_methods->remove(&kv_list->map->map_base.base, *key, NULL); 241 } 242 list->collection.size--; 243 cxFree(list->collection.allocator, node); 244 } 245 return index; 246 } 247 248 static void cx_kvl_sort(struct cx_list_s *list) { 249 cx_kv_list *kv_list = (cx_kv_list*)list; 250 kv_list->list_methods->sort(list); 251 } 252 253 static void cx_kvl_reverse(struct cx_list_s *list) { 254 cx_kv_list *kv_list = (cx_kv_list*)list; 255 kv_list->list_methods->reverse(list); 256 } 257 258 static void cx_kvl_list_iter_next(void *it) { 259 struct cx_iterator_s *iter = it; 260 if (iter->base.remove) { 261 // remove the assigned key from the map before calling the actual function 262 cx_kv_list *kv_list = iter->src_handle; 263 cx_kv_list_update_destructors(kv_list); 264 char *node = iter->elem_handle; 265 CxHashKey *key = cx_kv_list_loc_key(kv_list, node + kv_list->list.loc_data); 266 if (key->hash != 0) { 267 kv_list->map_methods->remove(&kv_list->map->map_base.base, *key, NULL); 268 } 269 } 270 // note that we do not clear the remove flag, because the next_impl will do that 271 iter->base.next_impl(it); 272 } 273 274 static struct cx_iterator_s cx_kvl_iterator( 275 const struct cx_list_s *list, 276 size_t index, 277 bool backward 278 ) { 279 const cx_kv_list *kv_list = (const cx_kv_list*)list; 280 struct cx_iterator_s iter = kv_list->list_methods->iterator(list, index, backward); 281 iter.base.next_impl = iter.base.next; 282 iter.base.next = cx_kvl_list_iter_next; 283 return iter; 284 } 285 286 static void cx_kvl_map_deallocate(struct cx_map_s *map) { 287 cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; 288 kv_list->map_methods->deallocate(map); 289 kv_list->list_methods->deallocate(&kv_list->list.base); 290 } 291 292 static void cx_kvl_map_clear(struct cx_map_s *map) { 293 cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; 294 cx_kv_list_update_destructors(kv_list); 295 kv_list->list_methods->clear(&kv_list->list.base); 296 kv_list->map_methods->clear(map); 297 } 298 299 static void *cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) { 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; 335 void *node_data = kv_list->map_methods->get(map, key); 336 if (node_data == NULL) return NULL; // LCOV_EXCL_LINE 337 // return the node data 338 return kv_list->list.base.collection.store_pointer ? *(void**)node_data : node_data; 339 } 340 341 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; 343 344 void *node_data; 345 if (kv_list->map_methods->remove(map, key, &node_data)) { 346 return 1; 347 } 348 // we cannot just call a list method (because we don't have the index) 349 // and tbh. we also don't want to (because it's not performant when we 350 // can have the node ptr directly instead) 351 // therefore, we re-implement the logic ourselves 352 353 // check if the outside caller want's us to return or to destroy the element 354 if (targetbuf == NULL) { 355 // patch the destructors and invoke them through the wrapper 356 cx_kv_list_update_destructors(kv_list); 357 cx_invoke_advanced_destructor(&kv_list->list.base, node_data); 358 } else { 359 // copy the element to the target buffer 360 memcpy(targetbuf, node_data, kv_list->list.base.collection.elem_size); 361 } 362 363 // calculate the address of the node 364 void *node_ptr = (char*)node_data - kv_list->list.loc_data; 365 366 // unlink the node 367 cx_linked_list_remove( 368 &kv_list->list.begin, 369 &kv_list->list.end, 370 kv_list->list.loc_prev, 371 kv_list->list.loc_next, 372 node_ptr 373 ); 374 375 // decrement the list's size 376 kv_list->list.base.collection.size--; 377 378 // deallocate the node 379 cxFree(kv_list->list.base.collection.allocator, node_ptr); 380 381 return 0; 382 } 383 384 static void *cx_kvl_iter_current_entry(const void *it) { 385 const CxMapIterator *iter = it; 386 return (void*)&iter->entry; 387 } 388 389 static void *cx_kvl_iter_current_key(const void *it) { 390 const CxMapEntry *entry = cx_kvl_iter_current_entry(it); 391 return (void*)entry->key; 392 } 393 394 static void *cx_kvl_iter_current_value(const void *it) { 395 const CxMapEntry *entry = cx_kvl_iter_current_entry(it); 396 return entry->value; 397 } 398 399 static void cx_kvl_iter_next(void *it) { 400 CxMapIterator *iter = it; 401 cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)iter->map)->list; 402 403 // find the next list entry that has a key assigned 404 CxHashKey *key = NULL; 405 char *next = iter->elem; 406 while (true) { 407 next = *(char**)(next + kv_list->list.loc_next); 408 if (next == NULL) break; 409 key = cx_kv_list_loc_key(kv_list, next + kv_list->list.loc_data); 410 if (key->hash != 0) break; 411 } 412 413 // remove previous element if requested 414 if (iter->base.remove) { 415 iter->base.remove = false; 416 cx_kv_list_update_destructors(kv_list); 417 char *elem = iter->elem; 418 char *elem_data = elem + kv_list->list.loc_data; 419 CxHashKey *elem_key = cx_kv_list_loc_key(kv_list, elem_data); 420 // key is guaranteed to exist because iterator only iterates over elems with a key 421 kv_list->map_methods->remove(&kv_list->map->map_base.base, *elem_key, NULL); 422 cx_invoke_advanced_destructor(&kv_list->list.base, elem_data); 423 cx_linked_list_remove( 424 &kv_list->list.begin, 425 &kv_list->list.end, 426 kv_list->list.loc_prev, 427 kv_list->list.loc_next, 428 elem 429 ); 430 cxFree(kv_list->list.base.collection.allocator, elem); 431 kv_list->list.base.collection.size--; 432 iter->index--; 433 iter->elem_count--; 434 } 435 436 // advance to the next element, if any 437 if (next == NULL) { 438 iter->index = kv_list->list.base.collection.size; 439 iter->elem = NULL; 440 iter->entry = (CxMapEntry){NULL, NULL}; 441 return; 442 } 443 iter->index++; 444 iter->elem = next; 445 iter->entry.key = key; 446 if (kv_list->list.base.collection.store_pointer) { 447 iter->entry.value = *(void**)(next + kv_list->list.loc_data); 448 } else { 449 iter->entry.value = (void*)(next + kv_list->list.loc_data); 450 } 451 } 452 453 static bool cx_kvl_iter_valid(const void *it) { 454 const CxMapIterator *iter = it; 455 return iter->elem != NULL; 456 } 457 458 CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_type type) { 459 CxMapIterator iter = {0}; 460 461 iter.type = type; 462 iter.map = (CxMap*)map; 463 // although we iterate over the list, we only report that many elements that have a key in the map 464 iter.elem_count = map->collection.size; 465 466 switch (type) { 467 case CX_MAP_ITERATOR_PAIRS: 468 iter.elem_size = sizeof(CxMapEntry); 469 iter.base.current = cx_kvl_iter_current_entry; 470 break; 471 case CX_MAP_ITERATOR_KEYS: 472 iter.elem_size = sizeof(CxHashKey); 473 iter.base.current = cx_kvl_iter_current_key; 474 break; 475 case CX_MAP_ITERATOR_VALUES: 476 iter.elem_size = map->collection.elem_size; 477 iter.base.current = cx_kvl_iter_current_value; 478 break; 479 default: 480 assert(false); // LCOV_EXCL_LINE 481 } 482 483 iter.base.allow_remove = true; 484 iter.base.next = cx_kvl_iter_next; 485 iter.base.valid = cx_kvl_iter_valid; 486 487 // find the first list entry that has a key assigned 488 cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; 489 CxHashKey *key = NULL; 490 char *next = kv_list->list.begin; 491 while (next != NULL) { 492 key = cx_kv_list_loc_key(kv_list, next + kv_list->list.loc_data); 493 if (key->hash != 0) break; 494 next = *(char**)(next + kv_list->list.loc_next); 495 } 496 if (next == NULL) { 497 iter.elem = NULL; 498 iter.entry = (CxMapEntry){NULL, NULL}; 499 } else { 500 iter.elem = next; 501 iter.entry.key = key; 502 if (kv_list->list.base.collection.store_pointer) { 503 iter.entry.value = *(void**)(next + kv_list->list.loc_data); 504 } else { 505 iter.entry.value = (void*)(next + kv_list->list.loc_data); 506 } 507 } 508 509 return iter; 510 } 511 512 static cx_list_class cx_kv_list_class = { 513 cx_kvl_deallocate, 514 cx_kvl_insert_element, 515 cx_kvl_insert_array, 516 cx_kvl_insert_sorted, 517 cx_kvl_insert_unique, 518 cx_kvl_insert_iter, 519 cx_kvl_remove, 520 cx_kvl_clear, 521 cx_kvl_swap, 522 cx_kvl_at, 523 cx_kvl_find_remove, 524 cx_kvl_sort, 525 NULL, 526 cx_kvl_reverse, 527 cx_kvl_iterator, 528 }; 529 530 static cx_map_class cx_kv_map_class = { 531 cx_kvl_map_deallocate, 532 cx_kvl_map_clear, 533 cx_kvl_map_put, 534 cx_kvl_map_get, 535 cx_kvl_map_remove, 536 cx_kvl_map_iterator, 537 }; 538 539 CxList *cxKvListCreate( 540 const CxAllocator *allocator, 541 cx_compare_func comparator, 542 size_t elem_size 543 ) { 544 if (allocator == NULL) { 545 allocator = cxDefaultAllocator; 546 } 547 548 // create a normal linked list and a normal hash map, first 549 CxList *list = cxLinkedListCreate(allocator, comparator, elem_size); 550 if (list == NULL) return NULL; // LCOV_EXCL_LINE 551 cx_linked_list *ll = (cx_linked_list*)list; 552 ll->extra_data_len = sizeof(CxHashKey); 553 CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0); 554 if (map == NULL) { // LCOV_EXCL_START 555 cxListFree(list); 556 return NULL; 557 } // LCOV_EXCL_STOP 558 559 // patch the kv-list class with the compare function of the linked list 560 // this allows cxListCompare() to optimize comparisons between linked lists and kv-list 561 cx_kv_list_class.compare = list->cl->compare; 562 563 // reallocate the map to add memory for the list back-reference 564 struct cx_kv_list_map_s *kv_map = cxRealloc(allocator, map, sizeof(struct cx_kv_list_map_s)); 565 566 // reallocate the list to add memory for storing the metadata 567 cx_kv_list *kv_list = cxRealloc(allocator, list, sizeof(struct cx_kv_list_s)); 568 569 // if any of the reallocations failed, we bail out 570 if (kv_map != NULL && kv_list != NULL) { 571 map = (CxMap*) kv_map; 572 list = (CxList*) kv_list; 573 } else { // LCOV_EXCL_START 574 cxListFree(list); 575 cxMapFree(map); 576 return NULL; 577 } // LCOV_EXCL_STOP 578 579 // zero the custom destructor information 580 memset((char*)kv_list + offsetof(cx_kv_list, list_destr), 0, sizeof(void*)*6); 581 582 // combine the list and the map aspect 583 kv_list->map = kv_map; 584 kv_map->list = kv_list; 585 586 // remember the base methods and override them 587 kv_list->map_methods = map->cl; 588 map->cl = &cx_kv_map_class; 589 if (list->climpl == NULL) { 590 kv_list->list_methods = list->cl; 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 597 return list; 598 } 599 600 CxMap *cxKvListCreateAsMap( 601 const CxAllocator *allocator, 602 cx_compare_func comparator, 603 size_t elem_size 604 ) { 605 CxList *list = cxKvListCreate(allocator, comparator, elem_size); 606 return list == NULL ? NULL : cxKvListAsMap(list); 607 } 608 609 CxList *cxKvListAsList(CxMap *map) { 610 return &((struct cx_kv_list_map_s*)map)->list->list.base; 611 } 612 613 CxMap *cxKvListAsMap(CxList *list) { 614 return &((cx_kv_list*)list)->map->map_base.base; 615 } 616 617 int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key) { 618 cx_kv_list *kv_list = (cx_kv_list*)list; 619 void *node_data = kv_list->list_methods->at(list, index); 620 if (node_data == NULL) { 621 return 1; 622 } 623 // if the hash has not yet been computed, do it now 624 if (key.hash == 0) { 625 cx_hash_murmur(&key); 626 } 627 628 // check if the key is already assigned 629 void *existing = kv_list->map_methods->get(&kv_list->map->map_base.base, key); 630 if (existing == node_data) { 631 return 0; // nothing to do 632 } 633 if (existing != NULL) { 634 // the key is already assigned to another node, we disallow re-assignment 635 return 1; 636 } 637 638 // add the key to the map; 639 if (NULL == kv_list->map_methods->put(&kv_list->map->map_base.base, key, node_data)) { 640 return 1; // LCOV_EXCL_LINE 641 } 642 643 // write the key to the list's node 644 CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data); 645 *loc_key = key; 646 647 return 0; 648 } 649 650 int cxKvListRemoveKey(CxList *list, size_t index) { 651 cx_kv_list *kv_list = (cx_kv_list*)list; 652 void *node_data = kv_list->list_methods->at(list, index); 653 if (node_data == NULL) { 654 return 1; 655 } 656 CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data); 657 if (loc_key->hash == 0) { 658 return 0; 659 } 660 kv_list->map_methods->remove(&kv_list->map->map_base.base, *loc_key, NULL); 661 // also zero the memory in the list node, 662 // but don't free the key data (that was done by the map remove) 663 memset(loc_key, 0, sizeof(CxHashKey)); 664 return 0; 665 } 666 667 const CxHashKey *cxKvListGetKey(CxList *list, size_t index) { 668 cx_kv_list *kv_list = (cx_kv_list*)list; 669 void *node_data = kv_list->list_methods->at(list, index); 670 if (node_data == NULL) { 671 return NULL; 672 } 673 CxHashKey *key = cx_kv_list_loc_key(kv_list, node_data); 674 if (key->hash == 0) { 675 return NULL; 676 } 677 return key; 678 } 679 680 int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value) { 681 // assume we are losing the sorted property 682 list->collection.sorted = false; 683 684 cx_kv_list *kv_list = (cx_kv_list*)list; 685 686 // reserve memory in the map 687 void **map_data = kv_list->map_methods->put(&kv_list->map->map_base.base, key, NULL); 688 if (map_data == NULL) return 1; // LCOV_EXCL_LINE 689 690 // insert the node 691 void *node_data = kv_list->list_methods->insert_element(&kv_list->list.base, index, 692 kv_list->list.base.collection.store_pointer ? &value : value); 693 if (node_data == NULL) { // LCOV_EXCL_START 694 // non-destructively remove the key again 695 kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data); 696 return 1; 697 } // LCOV_EXCL_STOP 698 *map_data = node_data; 699 700 // write the key to the node 701 CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data); 702 *loc_key = key; 703 704 return 0; 705 } 706