| 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); |
| 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 if (list == NULL) list = cxEmptyList; |
575 int cxListAdd(CxList *list, const void *elem) { |
| 480 CxIterator it = list->cl->iterator(list, index, false); |
576 list->collection.sorted = false; |
| 481 it.base.mutating = true; |
577 return list->cl->insert_element(list, list->collection.size, elem) == NULL; |
| 482 return it; |
578 } |
| 483 } |
579 |
| 484 |
580 size_t cxListAddArray(CxList *list, const void *array, size_t n) { |
| 485 CxIterator cxListMutBackwardsIteratorAt( |
581 list->collection.sorted = false; |
| 486 CxList *list, |
582 return list->cl->insert_array(list, list->collection.size, array, n); |
| 487 size_t index |
583 } |
| 488 ) { |
584 |
| 489 if (list == NULL) list = cxEmptyList; |
585 int cxListInsert(CxList *list, size_t index, const void *elem) { |
| 490 CxIterator it = list->cl->iterator(list, index, true); |
586 list->collection.sorted = false; |
| 491 it.base.mutating = true; |
587 return list->cl->insert_element(list, index, elem) == NULL; |
| 492 return it; |
588 } |
| 493 } |
589 |
| 494 |
590 void *cxListEmplaceAt(CxList *list, size_t index) { |
| 495 void cxListFree(CxList *list) { |
591 list->collection.sorted = false; |
| 496 if (list == NULL) return; |
592 return list->cl->insert_element(list, index, NULL); |
| 497 list->cl->deallocate(list); |
593 } |
| 498 } |
594 |
| 499 |
595 void *cxListEmplace(CxList *list) { |
| 500 int cxListSet( |
596 list->collection.sorted = false; |
| 501 CxList *list, |
597 return list->cl->insert_element(list, list->collection.size, NULL); |
| 502 size_t index, |
598 } |
| 503 const void *elem |
599 |
| 504 ) { |
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) { |
| 505 if (index >= list->collection.size) { |
739 if (index >= list->collection.size) { |
| 506 return 1; |
740 return 1; |
| 507 } |
741 } |
| 508 |
742 |
| 509 if (list->collection.store_pointer) { |
743 if (list->collection.store_pointer) { |
| 515 memcpy(target, elem, list->collection.elem_size); |
749 memcpy(target, elem, list->collection.elem_size); |
| 516 } |
750 } |
| 517 |
751 |
| 518 return 0; |
752 return 0; |
| 519 } |
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 int cxListClone(CxList *dst, const CxList *src, cx_clone_func clone_func, |
| |
823 const CxAllocator *clone_allocator, void *data) { |
| |
824 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; |
| |
825 |
| |
826 // remember the original size |
| |
827 size_t orig_size = dst->collection.size; |
| |
828 |
| |
829 // first, try to allocate the memory in the new list |
| |
830 CxIterator empl_iter = cxListEmplaceArray(dst, src->collection.size); |
| |
831 |
| |
832 // get an iterator over the source elements |
| |
833 CxIterator src_iter = cxListIterator(src); |
| |
834 |
| |
835 // now clone the elements |
| |
836 size_t cloned = empl_iter.elem_count; |
| |
837 for (size_t i = 0 ; i < empl_iter.elem_count; i++) { |
| |
838 void *src_elem = cxIteratorCurrent(src_iter); |
| |
839 void **dest_memory = cxIteratorCurrent(empl_iter); |
| |
840 void *target = cxCollectionStoresPointers(dst) ? NULL : dest_memory; |
| |
841 void *dest_ptr = clone_func(target, src_elem, clone_allocator, data); |
| |
842 if (dest_ptr == NULL) { |
| |
843 cloned = i; |
| |
844 break; |
| |
845 } |
| |
846 if (cxCollectionStoresPointers(dst)) { |
| |
847 *dest_memory = dest_ptr; |
| |
848 } |
| |
849 cxIteratorNext(src_iter); |
| |
850 cxIteratorNext(empl_iter); |
| |
851 } |
| |
852 |
| |
853 // if we could not clone everything, free the allocated memory |
| |
854 // (disable the destructors!) |
| |
855 if (cloned < src->collection.size) { |
| |
856 cx_list_pop_uninitialized_elements(dst, |
| |
857 dst->collection.size - cloned - orig_size); |
| |
858 return 1; |
| |
859 } |
| |
860 |
| |
861 return 0; |
| |
862 } |
| |
863 |
| |
864 int cxListDifference(CxList *dst, |
| |
865 const CxList *minuend, const CxList *subtrahend, |
| |
866 cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { |
| |
867 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; |
| |
868 |
| |
869 // optimize for sorted collections |
| |
870 if (cxCollectionSorted(minuend) && cxCollectionSorted(subtrahend)) { |
| |
871 bool dst_was_empty = cxCollectionSize(dst) == 0; |
| |
872 |
| |
873 CxIterator min_iter = cxListIterator(minuend); |
| |
874 CxIterator sub_iter = cxListIterator(subtrahend); |
| |
875 while (cxIteratorValid(min_iter)) { |
| |
876 void *min_elem = cxIteratorCurrent(min_iter); |
| |
877 void *sub_elem; |
| |
878 int d; |
| |
879 if (cxIteratorValid(sub_iter)) { |
| |
880 sub_elem = cxIteratorCurrent(sub_iter); |
| |
881 cx_compare_func cmp = subtrahend->collection.cmpfunc; |
| |
882 d = cmp(sub_elem, min_elem); |
| |
883 } else { |
| |
884 // no more elements in the subtrahend, |
| |
885 // i.e., the min_elem is larger than any elem of the subtrahend |
| |
886 d = 1; |
| |
887 } |
| |
888 if (d == 0) { |
| |
889 // is contained, so skip it |
| |
890 cxIteratorNext(min_iter); |
| |
891 } else if (d < 0) { |
| |
892 // subtrahend is smaller than minuend, |
| |
893 // check the next element |
| |
894 cxIteratorNext(sub_iter); |
| |
895 } else { |
| |
896 // subtrahend is larger than the dst element, |
| |
897 // clone the minuend and advance |
| |
898 void **dst_mem = cxListEmplace(dst); |
| |
899 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; |
| |
900 void* dst_ptr = clone_func(target, min_elem, clone_allocator, data); |
| |
901 if (dst_ptr == NULL) { |
| |
902 cx_list_pop_uninitialized_elements(dst, 1); |
| |
903 return 1; |
| |
904 } |
| |
905 if (cxCollectionStoresPointers(dst)) { |
| |
906 *dst_mem = dst_ptr; |
| |
907 } |
| |
908 cxIteratorNext(min_iter); |
| |
909 } |
| |
910 } |
| |
911 |
| |
912 // if dst was empty, it is now guaranteed to be sorted |
| |
913 dst->collection.sorted = dst_was_empty; |
| |
914 } else { |
| |
915 CxIterator min_iter = cxListIterator(minuend); |
| |
916 cx_foreach(void *, elem, min_iter) { |
| |
917 if (cxListContains(subtrahend, elem)) { |
| |
918 continue; |
| |
919 } |
| |
920 void **dst_mem = cxListEmplace(dst); |
| |
921 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; |
| |
922 void* dst_ptr = clone_func(target, elem, clone_allocator, data); |
| |
923 if (dst_ptr == NULL) { |
| |
924 cx_list_pop_uninitialized_elements(dst, 1); |
| |
925 return 1; |
| |
926 } |
| |
927 if (cxCollectionStoresPointers(dst)) { |
| |
928 *dst_mem = dst_ptr; |
| |
929 } |
| |
930 } |
| |
931 } |
| |
932 |
| |
933 return 0; |
| |
934 } |
| |
935 |
| |
936 int cxListIntersection(CxList *dst, |
| |
937 const CxList *src, const CxList *other, |
| |
938 cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { |
| |
939 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; |
| |
940 |
| |
941 // optimize for sorted collections |
| |
942 if (cxCollectionSorted(src) && cxCollectionSorted(other)) { |
| |
943 bool dst_was_empty = cxCollectionSize(dst) == 0; |
| |
944 |
| |
945 CxIterator src_iter = cxListIterator(src); |
| |
946 CxIterator other_iter = cxListIterator(other); |
| |
947 while (cxIteratorValid(src_iter) && cxIteratorValid(other_iter)) { |
| |
948 void *src_elem = cxIteratorCurrent(src_iter); |
| |
949 void *other_elem = cxIteratorCurrent(other_iter); |
| |
950 int d = src->collection.cmpfunc(src_elem, other_elem); |
| |
951 if (d == 0) { |
| |
952 // is contained, clone it |
| |
953 void **dst_mem = cxListEmplace(dst); |
| |
954 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; |
| |
955 void* dst_ptr = clone_func(target, src_elem, clone_allocator, data); |
| |
956 if (dst_ptr == NULL) { |
| |
957 cx_list_pop_uninitialized_elements(dst, 1); |
| |
958 return 1; |
| |
959 } |
| |
960 if (cxCollectionStoresPointers(dst)) { |
| |
961 *dst_mem = dst_ptr; |
| |
962 } |
| |
963 cxIteratorNext(src_iter); |
| |
964 } else if (d < 0) { |
| |
965 // the other element is larger, skip the source element |
| |
966 cxIteratorNext(src_iter); |
| |
967 } else { |
| |
968 // the source element is larger, try to find it in the other list |
| |
969 cxIteratorNext(other_iter); |
| |
970 } |
| |
971 } |
| |
972 |
| |
973 // if dst was empty, it is now guaranteed to be sorted |
| |
974 dst->collection.sorted = dst_was_empty; |
| |
975 } else { |
| |
976 CxIterator src_iter = cxListIterator(src); |
| |
977 cx_foreach(void *, elem, src_iter) { |
| |
978 if (!cxListContains(other, elem)) { |
| |
979 continue; |
| |
980 } |
| |
981 void **dst_mem = cxListEmplace(dst); |
| |
982 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; |
| |
983 void* dst_ptr = clone_func(target, elem, clone_allocator, data); |
| |
984 if (dst_ptr == NULL) { |
| |
985 cx_list_pop_uninitialized_elements(dst, 1); |
| |
986 return 1; |
| |
987 } |
| |
988 if (cxCollectionStoresPointers(dst)) { |
| |
989 *dst_mem = dst_ptr; |
| |
990 } |
| |
991 } |
| |
992 } |
| |
993 |
| |
994 return 0; |
| |
995 } |
| |
996 |
| |
997 int cxListUnion(CxList *dst, |
| |
998 const CxList *src, const CxList *other, |
| |
999 cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { |
| |
1000 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; |
| |
1001 |
| |
1002 // optimize for sorted collections |
| |
1003 if (cxCollectionSorted(src) && cxCollectionSorted(other)) { |
| |
1004 bool dst_was_empty = cxCollectionSize(dst) == 0; |
| |
1005 |
| |
1006 CxIterator src_iter = cxListIterator(src); |
| |
1007 CxIterator other_iter = cxListIterator(other); |
| |
1008 while (cxIteratorValid(src_iter) || cxIteratorValid(other_iter)) { |
| |
1009 void *src_elem, *other_elem; |
| |
1010 int d; |
| |
1011 if (!cxIteratorValid(src_iter)) { |
| |
1012 other_elem = cxIteratorCurrent(other_iter); |
| |
1013 d = 1; |
| |
1014 } else if (!cxIteratorValid(other_iter)) { |
| |
1015 src_elem = cxIteratorCurrent(src_iter); |
| |
1016 d = -1; |
| |
1017 } else { |
| |
1018 src_elem = cxIteratorCurrent(src_iter); |
| |
1019 other_elem = cxIteratorCurrent(other_iter); |
| |
1020 d = src->collection.cmpfunc(src_elem, other_elem); |
| |
1021 } |
| |
1022 if (d <= 0) { |
| |
1023 // source element is smaller or equal, clone it |
| |
1024 void **dst_mem = cxListEmplace(dst); |
| |
1025 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; |
| |
1026 void* dst_ptr = clone_func(target, src_elem, clone_allocator, data); |
| |
1027 if (dst_ptr == NULL) { |
| |
1028 cx_list_pop_uninitialized_elements(dst, 1); |
| |
1029 return 1; |
| |
1030 } |
| |
1031 if (cxCollectionStoresPointers(dst)) { |
| |
1032 *dst_mem = dst_ptr; |
| |
1033 } |
| |
1034 cxIteratorNext(src_iter); |
| |
1035 // if the other element was equal, skip it |
| |
1036 if (d == 0) { |
| |
1037 cxIteratorNext(other_iter); |
| |
1038 } |
| |
1039 } else { |
| |
1040 // the other element is smaller, clone it |
| |
1041 void **dst_mem = cxListEmplace(dst); |
| |
1042 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; |
| |
1043 void* dst_ptr = clone_func(target, other_elem, clone_allocator, data); |
| |
1044 if (dst_ptr == NULL) { |
| |
1045 cx_list_pop_uninitialized_elements(dst, 1); |
| |
1046 return 1; |
| |
1047 } |
| |
1048 if (cxCollectionStoresPointers(dst)) { |
| |
1049 *dst_mem = dst_ptr; |
| |
1050 } |
| |
1051 cxIteratorNext(other_iter); |
| |
1052 } |
| |
1053 } |
| |
1054 |
| |
1055 // if dst was empty, it is now guaranteed to be sorted |
| |
1056 dst->collection.sorted = dst_was_empty; |
| |
1057 } else { |
| |
1058 if (cxListClone(dst, src, clone_func, clone_allocator, data)) { |
| |
1059 return 1; |
| |
1060 } |
| |
1061 CxIterator other_iter = cxListIterator(other); |
| |
1062 cx_foreach(void *, elem, other_iter) { |
| |
1063 if (cxListContains(src, elem)) { |
| |
1064 continue; |
| |
1065 } |
| |
1066 void **dst_mem = cxListEmplace(dst); |
| |
1067 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; |
| |
1068 void* dst_ptr = clone_func(target, elem, clone_allocator, data); |
| |
1069 if (dst_ptr == NULL) { |
| |
1070 cx_list_pop_uninitialized_elements(dst, 1); |
| |
1071 return 1; |
| |
1072 } |
| |
1073 if (cxCollectionStoresPointers(dst)) { |
| |
1074 *dst_mem = dst_ptr; |
| |
1075 } |
| |
1076 } |
| |
1077 } |
| |
1078 |
| |
1079 return 0; |
| |
1080 } |