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