src/ucx/list.c

changeset 621
956c03c25edd
parent 582
82b60a8dd55c
child 645
0c85c4cd0dd8
equal deleted inserted replaced
620:a202cb1ee175 621:956c03c25edd
27 */ 27 */
28 28
29 #include "cx/list.h" 29 #include "cx/list.h"
30 30
31 #include <string.h> 31 #include <string.h>
32 #include <assert.h>
32 33
33 // <editor-fold desc="Store Pointers Functionality"> 34 // <editor-fold desc="Store Pointers Functionality">
34 35
35 static _Thread_local cx_compare_func cx_pl_cmpfunc_impl; 36 static _Thread_local cx_compare_func cx_pl_cmpfunc_impl;
36 37
37 static int cx_pl_cmpfunc( 38 static int cx_pl_cmpfunc(
38 const void *l, 39 const void *l,
39 const void *r 40 const void *r
40 ) { 41 ) {
42 // l and r are guaranteed to be non-NULL pointing to the list's memory
41 void *const *lptr = l; 43 void *const *lptr = l;
42 void *const *rptr = r; 44 void *const *rptr = r;
43 const void *left = lptr == NULL ? NULL : *lptr; 45 const void *left = *lptr;
44 const void *right = rptr == NULL ? NULL : *rptr; 46 const void *right = *rptr;
47 if (left == NULL) {
48 // NULL is smaller than any value except NULL
49 return right == NULL ? 0 : -1;
50 } else if (right == NULL) {
51 // any value is larger than NULL
52 return 1;
53 }
45 return cx_pl_cmpfunc_impl(left, right); 54 return cx_pl_cmpfunc_impl(left, right);
46 } 55 }
47 56
48 static void cx_pl_hack_cmpfunc(const struct cx_list_s *list) { 57 static void cx_pl_hack_cmpfunc(const struct cx_list_s *list) {
49 // cast away const - this is the hacky thing 58 // cast away const - this is the hacky thing
88 size_t result = list->climpl->insert_sorted(list, array, n); 97 size_t result = list->climpl->insert_sorted(list, array, n);
89 cx_pl_unhack_cmpfunc(list); 98 cx_pl_unhack_cmpfunc(list);
90 return result; 99 return result;
91 } 100 }
92 101
102 static size_t cx_pl_insert_unique(
103 struct cx_list_s *list,
104 const void *array,
105 size_t n
106 ) {
107 cx_pl_hack_cmpfunc(list);
108 size_t result = list->climpl->insert_unique(list, array, n);
109 cx_pl_unhack_cmpfunc(list);
110 return result;
111 }
112
93 static int cx_pl_insert_iter( 113 static int cx_pl_insert_iter(
94 struct cx_iterator_s *iter, 114 struct cx_iterator_s *iter,
95 const void *elem, 115 const void *elem,
96 int prepend 116 int prepend
97 ) { 117 ) {
98 struct cx_list_s *list = iter->src_handle.m; 118 struct cx_list_s *list = iter->src_handle;
99 return list->climpl->insert_iter(iter, &elem, prepend); 119 return list->climpl->insert_iter(iter, &elem, prepend);
100 } 120 }
101 121
102 static size_t cx_pl_remove( 122 static size_t cx_pl_remove(
103 struct cx_list_s *list, 123 struct cx_list_s *list,
179 static cx_list_class cx_pointer_list_class = { 199 static cx_list_class cx_pointer_list_class = {
180 cx_pl_destructor, 200 cx_pl_destructor,
181 cx_pl_insert_element, 201 cx_pl_insert_element,
182 cx_pl_insert_array, 202 cx_pl_insert_array,
183 cx_pl_insert_sorted, 203 cx_pl_insert_sorted,
204 cx_pl_insert_unique,
184 cx_pl_insert_iter, 205 cx_pl_insert_iter,
185 cx_pl_remove, 206 cx_pl_remove,
186 cx_pl_clear, 207 cx_pl_clear,
187 cx_pl_swap, 208 cx_pl_swap,
188 cx_pl_at, 209 cx_pl_at,
223 const struct cx_list_s *list, 244 const struct cx_list_s *list,
224 size_t index, 245 size_t index,
225 cx_attr_unused bool backwards 246 cx_attr_unused bool backwards
226 ) { 247 ) {
227 CxIterator iter = {0}; 248 CxIterator iter = {0};
228 iter.src_handle.c = list; 249 iter.src_handle = (void*) list;
229 iter.index = index; 250 iter.index = index;
230 iter.base.valid = cx_emptyl_iter_valid; 251 iter.base.valid = cx_emptyl_iter_valid;
231 return iter; 252 return iter;
232 } 253 }
233 254
234 static cx_list_class cx_empty_list_class = { 255 static cx_list_class cx_empty_list_class = {
235 cx_emptyl_noop, 256 cx_emptyl_noop,
257 NULL,
236 NULL, 258 NULL,
237 NULL, 259 NULL,
238 NULL, 260 NULL,
239 NULL, 261 NULL,
240 NULL, 262 NULL,
276 struct cx_list_s *list, 298 struct cx_list_s *list,
277 size_t index, 299 size_t index,
278 const void *data, 300 const void *data,
279 size_t n 301 size_t n
280 ) { 302 ) {
281 size_t elem_size = list->collection.elem_size;
282 const char *src = data; 303 const char *src = data;
283 size_t i = 0; 304 size_t i = 0;
284 for (; i < n; i++) { 305 for (; i < n; i++) {
285 if (NULL == invoke_list_func( 306 if (NULL == invoke_list_func(
286 insert_element, list, index + i, 307 insert_element, list, index + i, src)
287 src + (i * elem_size))) return i; 308 ) {
309 return i; // LCOV_EXCL_LINE
310 }
311 if (src != NULL) {
312 src += list->collection.elem_size;
313 }
288 } 314 }
289 return i; 315 return i;
316 }
317
318 static size_t cx_list_default_insert_sorted_impl(
319 struct cx_list_s *list,
320 const void *sorted_data,
321 size_t n,
322 bool allow_duplicates
323 ) {
324 // corner case
325 if (n == 0) return 0;
326
327 size_t elem_size = list->collection.elem_size;
328 cx_compare_func cmp = list->collection.cmpfunc;
329 const char *src = sorted_data;
330
331 // track indices and number of inserted items
332 size_t di = 0, si = 0, processed = 0;
333
334 // search the list for insertion points
335 while (di < list->collection.size) {
336 const void *list_elm = invoke_list_func(at, list, di);
337
338 // compare the current list element with the first source element
339 // if less, skip the list elements
340 // if equal, skip the list elements and optionally the source elements
341 {
342 int d = cmp(list_elm, src);
343 if (d <= 0) {
344 if (!allow_duplicates && d == 0) {
345 src += elem_size;
346 si++;
347 processed++; // we also count duplicates for the return value
348 while (si < n && cmp(list_elm, src) == 0) {
349 src += elem_size;
350 si++;
351 processed++;
352 }
353 if (processed == n) {
354 return processed;
355 }
356 }
357 di++;
358 continue;
359 }
360 }
361
362 // determine the number of consecutive elements that can be inserted
363 size_t ins = 1, skip = 0;
364 const char *next = src;
365 while (++si < n) {
366 if (!allow_duplicates) {
367 // skip duplicates within the source
368 if (cmp(next, next + elem_size) == 0) {
369 next += elem_size;
370 skip++;
371 continue;
372 } else {
373 if (skip > 0) {
374 // if we had to skip something, we must wait for the next run
375 next += elem_size;
376 break;
377 }
378 }
379 }
380 next += elem_size;
381 // once we become larger than the list elem, break
382 if (cmp(list_elm, next) <= 0) {
383 break;
384 }
385 // otherwise, we can insert one more
386 ins++;
387 }
388
389 // insert the elements at location si
390 if (ins == 1) {
391 if (NULL == invoke_list_func(insert_element, list, di, src)) {
392 return processed; // LCOV_EXCL_LINE
393 }
394 } else {
395 size_t r = invoke_list_func(insert_array, list, di, src, ins);
396 if (r < ins) {
397 return processed + r; // LCOV_EXCL_LINE
398 }
399 }
400 processed += ins + skip;
401 di += ins;
402
403 // everything inserted?
404 if (processed == n) {
405 return processed;
406 }
407 src = next;
408 }
409
410 // insert remaining items
411 if (si < n) {
412 if (allow_duplicates) {
413 processed += invoke_list_func(insert_array, list, di, src, n - si);
414 } else {
415 const void *last = di == 0 ? NULL : invoke_list_func(at, list, di - 1);
416 for (; si < n; si++) {
417 // skip duplicates within the source
418 if (last == NULL || cmp(last, src) != 0) {
419 if (NULL == invoke_list_func(insert_element, list, di, src)) {
420 return processed; // LCOV_EXCL_LINE
421 }
422 last = src;
423 di++;
424 }
425 processed++;
426 src += elem_size;
427 }
428 }
429 }
430
431 return processed;
290 } 432 }
291 433
292 size_t cx_list_default_insert_sorted( 434 size_t cx_list_default_insert_sorted(
293 struct cx_list_s *list, 435 struct cx_list_s *list,
294 const void *sorted_data, 436 const void *sorted_data,
295 size_t n 437 size_t n
296 ) { 438 ) {
297 // corner case 439 return cx_list_default_insert_sorted_impl(list, sorted_data, n, true);
298 if (n == 0) return 0; 440 }
299 441
300 size_t elem_size = list->collection.elem_size; 442 size_t cx_list_default_insert_unique(
301 cx_compare_func cmp = list->collection.cmpfunc; 443 struct cx_list_s *list,
302 const char *src = sorted_data; 444 const void *sorted_data,
303 445 size_t n
304 // track indices and number of inserted items 446 ) {
305 size_t di = 0, si = 0, inserted = 0; 447 return cx_list_default_insert_sorted_impl(list, sorted_data, n, false);
306
307 // search the list for insertion points
308 for (; di < list->collection.size; di++) {
309 const void *list_elm = invoke_list_func(at, list, di);
310
311 // compare current list element with first source element
312 // if less or equal, skip
313 if (cmp(list_elm, src) <= 0) {
314 continue;
315 }
316
317 // determine number of consecutive elements that can be inserted
318 size_t ins = 1;
319 const char *next = src;
320 while (++si < n) {
321 next += elem_size;
322 // once we become larger than the list elem, break
323 if (cmp(list_elm, next) <= 0) {
324 break;
325 }
326 // otherwise, we can insert one more
327 ins++;
328 }
329
330 // insert the elements at location si
331 if (ins == 1) {
332 if (NULL == invoke_list_func(
333 insert_element, list, di, src)) return inserted;
334 } else {
335 size_t r = invoke_list_func(insert_array, list, di, src, ins);
336 if (r < ins) return inserted + r;
337 }
338 inserted += ins;
339 di += ins;
340
341 // everything inserted?
342 if (inserted == n) return inserted;
343 src = next;
344 }
345
346 // insert remaining items
347 if (si < n) {
348 inserted += invoke_list_func(insert_array, list, di, src, n - si);
349 }
350
351 return inserted;
352 } 448 }
353 449
354 void cx_list_default_sort(struct cx_list_s *list) { 450 void cx_list_default_sort(struct cx_list_s *list) {
355 size_t elem_size = list->collection.elem_size; 451 size_t elem_size = list->collection.elem_size;
356 size_t list_size = list->collection.size; 452 size_t list_size = list->collection.size;
357 void *tmp = cxMallocDefault(elem_size * list_size); 453 void *tmp = cxMallocDefault(elem_size * list_size);
358 if (tmp == NULL) abort(); 454 if (tmp == NULL) abort(); // LCOV_EXCL_LINE
359 455
360 // copy elements from source array 456 // copy elements from source array
361 char *loc = tmp; 457 char *loc = tmp;
362 for (size_t i = 0; i < list_size; i++) { 458 for (size_t i = 0; i < list_size; i++) {
363 void *src = invoke_list_func(at, list, i); 459 void *src = invoke_list_func(at, list, i);
386 if (j >= list->collection.size) return 1; 482 if (j >= list->collection.size) return 1;
387 483
388 size_t elem_size = list->collection.elem_size; 484 size_t elem_size = list->collection.elem_size;
389 485
390 void *tmp = cxMallocDefault(elem_size); 486 void *tmp = cxMallocDefault(elem_size);
391 if (tmp == NULL) return 1; 487 if (tmp == NULL) return 1; // LCOV_EXCL_LINE
392 488
393 void *ip = invoke_list_func(at, list, i); 489 void *ip = invoke_list_func(at, list, i);
394 void *jp = invoke_list_func(at, list, j); 490 void *jp = invoke_list_func(at, list, j);
395 491
396 memcpy(tmp, ip, elem_size); 492 memcpy(tmp, ip, elem_size);
470 // lists are compatible 566 // lists are compatible
471 return list->cl->compare(list, other); 567 return list->cl->compare(list, other);
472 } 568 }
473 } 569 }
474 570
475 CxIterator cxListMutIteratorAt( 571 size_t cxListSize(const CxList *list) {
476 CxList *list, 572 return list->collection.size;
477 size_t index 573 }
478 ) { 574
479 CxIterator it = list->cl->iterator(list, index, false); 575 int cxListAdd(CxList *list, const void *elem) {
480 it.base.mutating = true; 576 list->collection.sorted = false;
481 return it; 577 return list->cl->insert_element(list, list->collection.size, elem) == NULL;
482 } 578 }
483 579
484 CxIterator cxListMutBackwardsIteratorAt( 580 size_t cxListAddArray(CxList *list, const void *array, size_t n) {
485 CxList *list, 581 list->collection.sorted = false;
486 size_t index 582 return list->cl->insert_array(list, list->collection.size, array, n);
487 ) { 583 }
488 CxIterator it = list->cl->iterator(list, index, true); 584
489 it.base.mutating = true; 585 int cxListInsert(CxList *list, size_t index, const void *elem) {
490 return it; 586 list->collection.sorted = false;
491 } 587 return list->cl->insert_element(list, index, elem) == NULL;
492 588 }
493 void cxListFree(CxList *list) { 589
494 if (list == NULL) return; 590 void *cxListEmplaceAt(CxList *list, size_t index) {
495 list->cl->deallocate(list); 591 list->collection.sorted = false;
496 } 592 return list->cl->insert_element(list, index, NULL);
497 593 }
498 int cxListSet( 594
499 CxList *list, 595 void *cxListEmplace(CxList *list) {
500 size_t index, 596 list->collection.sorted = false;
501 const void *elem 597 return list->cl->insert_element(list, list->collection.size, NULL);
502 ) { 598 }
599
600 static bool cx_list_emplace_iterator_valid(const void *it) {
601 const CxIterator *iter = it;
602 return iter->index < iter->elem_count;
603 }
604
605 CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n) {
606 list->collection.sorted = false;
607 size_t c = list->cl->insert_array(list, index, NULL, n);
608 CxIterator iter = list->cl->iterator(list, index, false);
609 // tweak the fields of this iterator
610 iter.elem_count = c;
611 iter.index = 0;
612 // replace the valid function to abort iteration when c is reached
613 iter.base.valid = cx_list_emplace_iterator_valid;
614 // if we are storing pointers, we want to return the pure pointers.
615 // therefore, we must unwrap the "current" method
616 if (list->collection.store_pointer) {
617 iter.base.current = iter.base.current_impl;
618 }
619 return iter;
620 }
621
622 CxIterator cxListEmplaceArray(CxList *list, size_t n) {
623 return cxListEmplaceArrayAt(list, list->collection.size, n);
624 }
625
626 int cxListInsertSorted(CxList *list, const void *elem) {
627 assert(cxCollectionSorted(list));
628 list->collection.sorted = true;
629 const void *data = list->collection.store_pointer ? &elem : elem;
630 return list->cl->insert_sorted(list, data, 1) == 0;
631 }
632
633 int cxListInsertUnique(CxList *list, const void *elem) {
634 if (cxCollectionSorted(list)) {
635 list->collection.sorted = true;
636 const void *data = list->collection.store_pointer ? &elem : elem;
637 return list->cl->insert_unique(list, data, 1) == 0;
638 } else {
639 if (cxListContains(list, elem)) {
640 return 0;
641 } else {
642 return cxListAdd(list, elem);
643 }
644 }
645 }
646
647 size_t cxListInsertArray(CxList *list, size_t index, const void *array, size_t n) {
648 list->collection.sorted = false;
649 return list->cl->insert_array(list, index, array, n);
650 }
651
652 size_t cxListInsertSortedArray(CxList *list, const void *array, size_t n) {
653 assert(cxCollectionSorted(list));
654 list->collection.sorted = true;
655 return list->cl->insert_sorted(list, array, n);
656 }
657
658 size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n) {
659 if (cxCollectionSorted(list)) {
660 list->collection.sorted = true;
661 return list->cl->insert_unique(list, array, n);
662 } else {
663 const char *source = array;
664 for (size_t i = 0 ; i < n; i++) {
665 // note: this also checks elements added in a previous iteration
666 const void *data = list->collection.store_pointer ?
667 *((const void**)source) : source;
668 if (!cxListContains(list, data)) {
669 if (cxListAdd(list, data)) {
670 return i; // LCOV_EXCL_LINE
671 }
672 }
673 source += list->collection.elem_size;
674 }
675 return n;
676 }
677 }
678
679 int cxListInsertAfter(CxIterator *iter, const void *elem) {
680 CxList* list = (CxList*)iter->src_handle;
681 list->collection.sorted = false;
682 return list->cl->insert_iter(iter, elem, 0);
683 }
684
685 int cxListInsertBefore(CxIterator *iter, const void *elem) {
686 CxList* list = (CxList*)iter->src_handle;
687 list->collection.sorted = false;
688 return list->cl->insert_iter(iter, elem, 1);
689 }
690
691 int cxListRemove(CxList *list, size_t index) {
692 return list->cl->remove(list, index, 1, NULL) == 0;
693 }
694
695 int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf) {
696 return list->cl->remove(list, index, 1, targetbuf) == 0;
697 }
698
699 int cxListRemoveAndGetFirst(CxList *list, void *targetbuf) {
700 return list->cl->remove(list, 0, 1, targetbuf) == 0;
701 }
702
703 int cxListRemoveAndGetLast(CxList *list, void *targetbuf) {
704 // note: index may wrap - member function will catch that
705 return list->cl->remove(list, list->collection.size - 1, 1, targetbuf) == 0;
706 }
707
708 size_t cxListRemoveArray(CxList *list, size_t index, size_t num) {
709 return list->cl->remove(list, index, num, NULL);
710 }
711
712 size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num, void *targetbuf) {
713 return list->cl->remove(list, index, num, targetbuf);
714 }
715
716 void cxListClear(CxList *list) {
717 list->cl->clear(list);
718 list->collection.sorted = true; // empty lists are always sorted
719 }
720
721 int cxListSwap(CxList *list, size_t i, size_t j) {
722 list->collection.sorted = false;
723 return list->cl->swap(list, i, j);
724 }
725
726 void *cxListAt(const CxList *list, size_t index) {
727 return list->cl->at(list, index);
728 }
729
730 void *cxListFirst(const CxList *list) {
731 return list->cl->at(list, 0);
732 }
733
734 void *cxListLast(const CxList *list) {
735 return list->cl->at(list, list->collection.size - 1);
736 }
737
738 int cxListSet(CxList *list, size_t index, const void *elem) {
503 if (index >= list->collection.size) { 739 if (index >= list->collection.size) {
504 return 1; 740 return 1;
505 } 741 }
506 742
507 if (list->collection.store_pointer) { 743 if (list->collection.store_pointer) {
513 memcpy(target, elem, list->collection.elem_size); 749 memcpy(target, elem, list->collection.elem_size);
514 } 750 }
515 751
516 return 0; 752 return 0;
517 } 753 }
754
755 CxIterator cxListIteratorAt(const CxList *list, size_t index) {
756 if (list == NULL) list = cxEmptyList;
757 return list->cl->iterator(list, index, false);
758 }
759
760 CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index) {
761 if (list == NULL) list = cxEmptyList;
762 return list->cl->iterator(list, index, true);
763 }
764
765 CxIterator cxListIterator(const CxList *list) {
766 if (list == NULL) list = cxEmptyList;
767 return list->cl->iterator(list, 0, false);
768 }
769
770 CxIterator cxListBackwardsIterator(const CxList *list) {
771 if (list == NULL) list = cxEmptyList;
772 return list->cl->iterator(list, list->collection.size - 1, true);
773 }
774
775 size_t cxListFind(const CxList *list, const void *elem) {
776 return list->cl->find_remove((CxList*)list, elem, false);
777 }
778
779 bool cxListContains(const CxList* list, const void* elem) {
780 return list->cl->find_remove((CxList*)list, elem, false) < list->collection.size;
781 }
782
783 bool cxListIndexValid(const CxList *list, size_t index) {
784 return index < list->collection.size;
785 }
786
787 size_t cxListFindRemove(CxList *list, const void *elem) {
788 return list->cl->find_remove(list, elem, true);
789 }
790
791 void cxListSort(CxList *list) {
792 if (list->collection.sorted) return;
793 list->cl->sort(list);
794 list->collection.sorted = true;
795 }
796
797 void cxListReverse(CxList *list) {
798 // still sorted, but not according to the cmp_func
799 list->collection.sorted = false;
800 list->cl->reverse(list);
801 }
802
803 void cxListFree(CxList *list) {
804 if (list == NULL) return;
805 list->cl->deallocate(list);
806 }
807
808 static void cx_list_pop_uninitialized_elements(CxList *list, size_t n) {
809 cx_destructor_func destr_bak = list->collection.simple_destructor;
810 cx_destructor_func2 destr2_bak = list->collection.advanced_destructor;
811 list->collection.simple_destructor = NULL;
812 list->collection.advanced_destructor = NULL;
813 if (n == 1) {
814 cxListRemove(list, list->collection.size - 1);
815 } else {
816 cxListRemoveArray(list,list->collection.size - n, n);
817 }
818 list->collection.simple_destructor = destr_bak;
819 list->collection.advanced_destructor = destr2_bak;
820 }
821
822 static void* cx_list_simple_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) {
823 size_t elem_size = *(size_t*)data;
824 if (dst == NULL) dst = cxMalloc(al, elem_size);
825 if (dst != NULL) memcpy(dst, src, elem_size);
826 return dst;
827 }
828
829 #define use_simple_clone_func(list) cx_list_simple_clone_func, NULL, (void*)&((list)->collection.elem_size)
830
831 int cxListClone(CxList *dst, const CxList *src, cx_clone_func clone_func,
832 const CxAllocator *clone_allocator, void *data) {
833 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
834
835 // remember the original size
836 size_t orig_size = dst->collection.size;
837
838 // first, try to allocate the memory in the new list
839 CxIterator empl_iter = cxListEmplaceArray(dst, src->collection.size);
840
841 // get an iterator over the source elements
842 CxIterator src_iter = cxListIterator(src);
843
844 // now clone the elements
845 size_t cloned = empl_iter.elem_count;
846 for (size_t i = 0 ; i < empl_iter.elem_count; i++) {
847 void *src_elem = cxIteratorCurrent(src_iter);
848 void **dest_memory = cxIteratorCurrent(empl_iter);
849 void *target = cxCollectionStoresPointers(dst) ? NULL : dest_memory;
850 void *dest_ptr = clone_func(target, src_elem, clone_allocator, data);
851 if (dest_ptr == NULL) {
852 cloned = i;
853 break;
854 }
855 if (cxCollectionStoresPointers(dst)) {
856 *dest_memory = dest_ptr;
857 }
858 cxIteratorNext(src_iter);
859 cxIteratorNext(empl_iter);
860 }
861
862 // if we could not clone everything, free the allocated memory
863 // (disable the destructors!)
864 if (cloned < src->collection.size) {
865 cx_list_pop_uninitialized_elements(dst,
866 dst->collection.size - cloned - orig_size);
867 return 1;
868 }
869
870 // set the sorted flag when we know it's sorted
871 if (orig_size == 0 && src->collection.sorted) {
872 dst->collection.sorted = true;
873 }
874
875 return 0;
876 }
877
878 int cxListDifference(CxList *dst,
879 const CxList *minuend, const CxList *subtrahend,
880 cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {
881 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
882
883 // optimize for sorted collections
884 if (cxCollectionSorted(minuend) && cxCollectionSorted(subtrahend)) {
885 bool dst_was_empty = cxCollectionSize(dst) == 0;
886
887 CxIterator min_iter = cxListIterator(minuend);
888 CxIterator sub_iter = cxListIterator(subtrahend);
889 while (cxIteratorValid(min_iter)) {
890 void *min_elem = cxIteratorCurrent(min_iter);
891 void *sub_elem;
892 int d;
893 if (cxIteratorValid(sub_iter)) {
894 sub_elem = cxIteratorCurrent(sub_iter);
895 cx_compare_func cmp = subtrahend->collection.cmpfunc;
896 d = cmp(sub_elem, min_elem);
897 } else {
898 // no more elements in the subtrahend,
899 // i.e., the min_elem is larger than any elem of the subtrahend
900 d = 1;
901 }
902 if (d == 0) {
903 // is contained, so skip it
904 cxIteratorNext(min_iter);
905 } else if (d < 0) {
906 // subtrahend is smaller than minuend,
907 // check the next element
908 cxIteratorNext(sub_iter);
909 } else {
910 // subtrahend is larger than the dst element,
911 // clone the minuend and advance
912 void **dst_mem = cxListEmplace(dst);
913 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
914 void* dst_ptr = clone_func(target, min_elem, clone_allocator, data);
915 if (dst_ptr == NULL) {
916 cx_list_pop_uninitialized_elements(dst, 1);
917 return 1;
918 }
919 if (cxCollectionStoresPointers(dst)) {
920 *dst_mem = dst_ptr;
921 }
922 cxIteratorNext(min_iter);
923 }
924 }
925
926 // if dst was empty, it is now guaranteed to be sorted
927 dst->collection.sorted = dst_was_empty;
928 } else {
929 CxIterator min_iter = cxListIterator(minuend);
930 cx_foreach(void *, elem, min_iter) {
931 if (cxListContains(subtrahend, elem)) {
932 continue;
933 }
934 void **dst_mem = cxListEmplace(dst);
935 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
936 void* dst_ptr = clone_func(target, elem, clone_allocator, data);
937 if (dst_ptr == NULL) {
938 cx_list_pop_uninitialized_elements(dst, 1);
939 return 1;
940 }
941 if (cxCollectionStoresPointers(dst)) {
942 *dst_mem = dst_ptr;
943 }
944 }
945 }
946
947 return 0;
948 }
949
950 int cxListIntersection(CxList *dst,
951 const CxList *src, const CxList *other,
952 cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {
953 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
954
955 // optimize for sorted collections
956 if (cxCollectionSorted(src) && cxCollectionSorted(other)) {
957 bool dst_was_empty = cxCollectionSize(dst) == 0;
958
959 CxIterator src_iter = cxListIterator(src);
960 CxIterator other_iter = cxListIterator(other);
961 while (cxIteratorValid(src_iter) && cxIteratorValid(other_iter)) {
962 void *src_elem = cxIteratorCurrent(src_iter);
963 void *other_elem = cxIteratorCurrent(other_iter);
964 int d = src->collection.cmpfunc(src_elem, other_elem);
965 if (d == 0) {
966 // is contained, clone it
967 void **dst_mem = cxListEmplace(dst);
968 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
969 void* dst_ptr = clone_func(target, src_elem, clone_allocator, data);
970 if (dst_ptr == NULL) {
971 cx_list_pop_uninitialized_elements(dst, 1);
972 return 1;
973 }
974 if (cxCollectionStoresPointers(dst)) {
975 *dst_mem = dst_ptr;
976 }
977 cxIteratorNext(src_iter);
978 } else if (d < 0) {
979 // the other element is larger, skip the source element
980 cxIteratorNext(src_iter);
981 } else {
982 // the source element is larger, try to find it in the other list
983 cxIteratorNext(other_iter);
984 }
985 }
986
987 // if dst was empty, it is now guaranteed to be sorted
988 dst->collection.sorted = dst_was_empty;
989 } else {
990 CxIterator src_iter = cxListIterator(src);
991 cx_foreach(void *, elem, src_iter) {
992 if (!cxListContains(other, elem)) {
993 continue;
994 }
995 void **dst_mem = cxListEmplace(dst);
996 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
997 void* dst_ptr = clone_func(target, elem, clone_allocator, data);
998 if (dst_ptr == NULL) {
999 cx_list_pop_uninitialized_elements(dst, 1);
1000 return 1;
1001 }
1002 if (cxCollectionStoresPointers(dst)) {
1003 *dst_mem = dst_ptr;
1004 }
1005 }
1006 }
1007
1008 return 0;
1009 }
1010
1011 int cxListUnion(CxList *dst,
1012 const CxList *src, const CxList *other,
1013 cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {
1014 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
1015
1016 // optimize for sorted collections
1017 if (cxCollectionSorted(src) && cxCollectionSorted(other)) {
1018 bool dst_was_empty = cxCollectionSize(dst) == 0;
1019
1020 CxIterator src_iter = cxListIterator(src);
1021 CxIterator other_iter = cxListIterator(other);
1022 while (cxIteratorValid(src_iter) || cxIteratorValid(other_iter)) {
1023 void *src_elem, *other_elem;
1024 int d;
1025 if (!cxIteratorValid(src_iter)) {
1026 other_elem = cxIteratorCurrent(other_iter);
1027 d = 1;
1028 } else if (!cxIteratorValid(other_iter)) {
1029 src_elem = cxIteratorCurrent(src_iter);
1030 d = -1;
1031 } else {
1032 src_elem = cxIteratorCurrent(src_iter);
1033 other_elem = cxIteratorCurrent(other_iter);
1034 d = src->collection.cmpfunc(src_elem, other_elem);
1035 }
1036 void *clone_from;
1037 if (d < 0) {
1038 // source element is smaller clone it
1039 clone_from = src_elem;
1040 cxIteratorNext(src_iter);
1041 } else if (d == 0) {
1042 // both elements are equal, clone from the source, skip other
1043 clone_from = src_elem;
1044 cxIteratorNext(src_iter);
1045 cxIteratorNext(other_iter);
1046 } else {
1047 // the other element is smaller, clone it
1048 clone_from = other_elem;
1049 cxIteratorNext(other_iter);
1050 }
1051 void **dst_mem = cxListEmplace(dst);
1052 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
1053 void* dst_ptr = clone_func(target, clone_from, clone_allocator, data);
1054 if (dst_ptr == NULL) {
1055 cx_list_pop_uninitialized_elements(dst, 1);
1056 return 1;
1057 }
1058 if (cxCollectionStoresPointers(dst)) {
1059 *dst_mem = dst_ptr;
1060 }
1061 }
1062
1063 // if dst was empty, it is now guaranteed to be sorted
1064 dst->collection.sorted = dst_was_empty;
1065 } else {
1066 if (cxListClone(dst, src, clone_func, clone_allocator, data)) {
1067 return 1;
1068 }
1069 CxIterator other_iter = cxListIterator(other);
1070 cx_foreach(void *, elem, other_iter) {
1071 if (cxListContains(src, elem)) {
1072 continue;
1073 }
1074 void **dst_mem = cxListEmplace(dst);
1075 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
1076 void* dst_ptr = clone_func(target, elem, clone_allocator, data);
1077 if (dst_ptr == NULL) {
1078 cx_list_pop_uninitialized_elements(dst, 1);
1079 return 1;
1080 }
1081 if (cxCollectionStoresPointers(dst)) {
1082 *dst_mem = dst_ptr;
1083 }
1084 }
1085 }
1086
1087 return 0;
1088 }
1089
1090 int cxListCloneSimple(CxList *dst, const CxList *src) {
1091 return cxListClone(dst, src, use_simple_clone_func(src));
1092 }
1093
1094 int cxListDifferenceSimple(CxList *dst, const CxList *minuend, const CxList *subtrahend) {
1095 return cxListDifference(dst, minuend, subtrahend, use_simple_clone_func(minuend));
1096 }
1097
1098 int cxListIntersectionSimple(CxList *dst, const CxList *src, const CxList *other) {
1099 return cxListIntersection(dst, src, other, use_simple_clone_func(src));
1100 }
1101
1102 int cxListUnionSimple(CxList *dst, const CxList *src, const CxList *other) {
1103 return cxListUnion(dst, src, other, use_simple_clone_func(src));
1104 }

mercurial