ucx/kv_list.c

branch
dav-2
changeset 889
42cdbf9bbd49
equal deleted inserted replaced
887:26541c37b619 889:42cdbf9bbd49
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 }

mercurial