| 175 size_t si = 0, di = 0; |
176 size_t si = 0, di = 0; |
| 176 const char *src = sorted_data; |
177 const char *src = sorted_data; |
| 177 char *dest = array->data; |
178 char *dest = array->data; |
| 178 |
179 |
| 179 // find the first insertion point |
180 // find the first insertion point |
| 180 di = cx_array_binary_search_sup(dest, old_size, elem_size, src, cmp_func); |
181 di = cx_array_binary_search_sup_c(dest, old_size, elem_size, src, cmp_func, context); |
| 181 dest += di * elem_size; |
182 dest += di * elem_size; |
| 182 |
183 |
| 183 // move the remaining elements in the array completely to the right |
184 // move the remaining elements in the array completely to the right |
| 184 // we will call it the "buffer" for parked elements |
185 // we will call it the "buffer" for parked elements |
| 185 size_t buf_size = old_size - di; |
186 size_t buf_size = old_size - di; |
| 201 // - when all src elements that are smaller are copied, the second part |
202 // - when all src elements that are smaller are copied, the second part |
| 202 // of this loop body will copy the remaining buffer (emptying it) |
203 // of this loop body will copy the remaining buffer (emptying it) |
| 203 // Therefore, the buffer can never contain an element that is smaller |
204 // Therefore, the buffer can never contain an element that is smaller |
| 204 // than any element in the source and the infimum exists. |
205 // than any element in the source and the infimum exists. |
| 205 size_t copy_len, bytes_copied; |
206 size_t copy_len, bytes_copied; |
| 206 copy_len = cx_array_binary_search_inf( |
207 copy_len = cx_array_binary_search_inf_c( |
| 207 src, n - si, elem_size, bptr, cmp_func |
208 src, n - si, elem_size, bptr, cmp_func, context |
| 208 ); |
209 ); |
| 209 copy_len++; |
210 copy_len++; |
| 210 |
211 |
| 211 // copy the source elements |
212 // copy the source elements |
| 212 if (copy_len > 0) { |
213 if (copy_len > 0) { |
| 221 } else { |
222 } else { |
| 222 // first, check the end of the source chunk |
223 // first, check the end of the source chunk |
| 223 // for being a duplicate of the bptr |
224 // for being a duplicate of the bptr |
| 224 const char *end_of_src = src + (copy_len - 1) * elem_size; |
225 const char *end_of_src = src + (copy_len - 1) * elem_size; |
| 225 size_t skip_len = 0; |
226 size_t skip_len = 0; |
| 226 while (copy_len > 0 && cmp_func(bptr, end_of_src) == 0) { |
227 while (copy_len > 0 && cmp_func(bptr, end_of_src, context) == 0) { |
| 227 end_of_src -= elem_size; |
228 end_of_src -= elem_size; |
| 228 skip_len++; |
229 skip_len++; |
| 229 copy_len--; |
230 copy_len--; |
| 230 } |
231 } |
| 231 char *last = dest == array->data ? NULL : dest - elem_size; |
232 char *last = dest == array->data ? NULL : dest - elem_size; |
| 232 // then iterate through the source chunk |
233 // then iterate through the source chunk |
| 233 // and skip all duplicates with the last element in the array |
234 // and skip all duplicates with the last element in the array |
| 234 size_t more_skipped = 0; |
235 size_t more_skipped = 0; |
| 235 for (unsigned j = 0; j < copy_len; j++) { |
236 for (unsigned j = 0; j < copy_len; j++) { |
| 236 if (last != NULL && cmp_func(last, src) == 0) { |
237 if (last != NULL && cmp_func(last, src, context) == 0) { |
| 237 // duplicate - skip |
238 // duplicate - skip |
| 238 src += elem_size; |
239 src += elem_size; |
| 239 si++; |
240 si++; |
| 240 more_skipped++; |
241 more_skipped++; |
| 241 } else { |
242 } else { |
| 331 } |
333 } |
| 332 |
334 |
| 333 return 0; |
335 return 0; |
| 334 } |
336 } |
| 335 |
337 |
| |
338 int cx_array_insert_sorted_( |
| |
339 const CxAllocator *allocator, |
| |
340 CxArray *array, |
| |
341 size_t elem_size, |
| |
342 cx_compare_func cmp_func, |
| |
343 const void *sorted_data, |
| |
344 size_t n, |
| |
345 bool allow_duplicates |
| |
346 ) { |
| |
347 cx_compare_func_wrapper wrapper = {cmp_func}; |
| |
348 return cx_array_insert_sorted_s_(allocator, array, elem_size, sorted_data, |
| |
349 n, allow_duplicates, cx_acmp_wrap, &wrapper); |
| |
350 } |
| |
351 |
| 336 CxIterator cx_array_iterator_(CxArray *array, size_t elem_size) { |
352 CxIterator cx_array_iterator_(CxArray *array, size_t elem_size) { |
| 337 return cxIterator(array->data, elem_size, array->size); |
353 return cxIterator(array->data, elem_size, array->size); |
| 338 } |
354 } |
| 339 |
355 |
| 340 CxIterator cx_array_iterator_ptr_(CxArray *array) { |
356 CxIterator cx_array_iterator_ptr_(CxArray *array) { |
| 364 |
381 |
| 365 // cast the array pointer to something we can use offsets with |
382 // cast the array pointer to something we can use offsets with |
| 366 const char *array = arr; |
383 const char *array = arr; |
| 367 |
384 |
| 368 // check the first array element |
385 // check the first array element |
| 369 result = cmp_func(elem, array); |
386 result = cmp_func(elem, array, context); |
| 370 if (result < 0) { |
387 if (result < 0) { |
| 371 return size; |
388 return size; |
| 372 } else if (result == 0) { |
389 } else if (result == 0) { |
| 373 return 0; |
390 return 0; |
| 374 } |
391 } |
| 375 |
392 |
| 376 // special case: there is only one element and that is smaller |
393 // special case: there is only one element and that is smaller |
| 377 if (size == 1) return 0; |
394 if (size == 1) return 0; |
| 378 |
395 |
| 379 // check the last array element |
396 // check the last array element |
| 380 result = cmp_func(elem, array + elem_size * (size - 1)); |
397 result = cmp_func(elem, array + elem_size * (size - 1), context); |
| 381 if (result >= 0) { |
398 if (result >= 0) { |
| 382 return size - 1; |
399 return size - 1; |
| 383 } |
400 } |
| 384 |
401 |
| 385 // the element is now guaranteed to be somewhere in the list |
402 // the element is now guaranteed to be somewhere in the list |
| 386 // so start the binary search |
403 // so start the binary search |
| 387 size_t left_index = 1; |
404 size_t left_index = 1; |
| 388 size_t right_index = size - 1; |
405 size_t right_index = size - 1; |
| 389 size_t pivot_index; |
406 size_t pivot_index = 0; |
| 390 |
407 |
| 391 while (left_index <= right_index) { |
408 while (left_index <= right_index) { |
| 392 pivot_index = left_index + (right_index - left_index) / 2; |
409 pivot_index = left_index + (right_index - left_index) / 2; |
| 393 const char *arr_elem = array + pivot_index * elem_size; |
410 const char *arr_elem = array + pivot_index * elem_size; |
| 394 result = cmp_func(elem, arr_elem); |
411 result = cmp_func(elem, arr_elem, context); |
| 395 if (result == 0) { |
412 if (result == 0) { |
| 396 // found it! |
413 // found it! |
| 397 return pivot_index; |
414 return pivot_index; |
| 398 } else if (result < 0) { |
415 } else if (result < 0) { |
| 399 // element is smaller than pivot, continue search left |
416 // element is smaller than pivot, continue search left |
| 406 |
423 |
| 407 // report the largest upper bound |
424 // report the largest upper bound |
| 408 return result < 0 ? (pivot_index - 1) : pivot_index; |
425 return result < 0 ? (pivot_index - 1) : pivot_index; |
| 409 } |
426 } |
| 410 |
427 |
| |
428 size_t cx_array_binary_search_inf_c( |
| |
429 const void *arr, |
| |
430 size_t size, |
| |
431 size_t elem_size, |
| |
432 const void *elem, |
| |
433 cx_compare_func2 cmp_func, |
| |
434 void *context |
| |
435 ) { |
| |
436 size_t index = cx_array_binary_search_inf_impl( |
| |
437 arr, size, elem_size, elem, cmp_func, context); |
| |
438 // in case of equality, report the largest index |
| |
439 const char *e = ((const char *) arr) + (index + 1) * elem_size; |
| |
440 while (index + 1 < size && cmp_func(e, elem, context) == 0) { |
| |
441 e += elem_size; |
| |
442 index++; |
| |
443 } |
| |
444 return index; |
| |
445 } |
| |
446 |
| |
447 size_t cx_array_binary_search_c( |
| |
448 const void *arr, |
| |
449 size_t size, |
| |
450 size_t elem_size, |
| |
451 const void *elem, |
| |
452 cx_compare_func2 cmp_func, |
| |
453 void *context |
| |
454 ) { |
| |
455 size_t index = cx_array_binary_search_inf_c( |
| |
456 arr, size, elem_size, elem, cmp_func, context |
| |
457 ); |
| |
458 if (index < size && cmp_func(((const char *) arr) + index * elem_size, |
| |
459 elem, context) == 0) { |
| |
460 return index; |
| |
461 } else { |
| |
462 return size; |
| |
463 } |
| |
464 } |
| |
465 |
| |
466 size_t cx_array_binary_search_sup_c( |
| |
467 const void *arr, |
| |
468 size_t size, |
| |
469 size_t elem_size, |
| |
470 const void *elem, |
| |
471 cx_compare_func2 cmp_func, |
| |
472 void *context |
| |
473 ) { |
| |
474 size_t index = cx_array_binary_search_inf_impl( |
| |
475 arr, size, elem_size, elem, cmp_func, context |
| |
476 ); |
| |
477 const char *e = ((const char *) arr) + index * elem_size; |
| |
478 if (index == size) { |
| |
479 // no infimum means the first element is supremum |
| |
480 return 0; |
| |
481 } else if (cmp_func(e, elem, context) == 0) { |
| |
482 // found an equal element, search the smallest index |
| |
483 e -= elem_size; // e now contains the element at index-1 |
| |
484 while (index > 0 && cmp_func(e, elem, context) == 0) { |
| |
485 e -= elem_size; |
| |
486 index--; |
| |
487 } |
| |
488 return index; |
| |
489 } else { |
| |
490 // we already have the largest index of the infimum (by design) |
| |
491 // the next element is the supremum (or there is no supremum) |
| |
492 return index + 1; |
| |
493 } |
| |
494 } |
| |
495 |
| 411 size_t cx_array_binary_search_inf( |
496 size_t cx_array_binary_search_inf( |
| 412 const void *arr, |
497 const void *arr, |
| 413 size_t size, |
498 size_t size, |
| 414 size_t elem_size, |
499 size_t elem_size, |
| 415 const void *elem, |
500 const void *elem, |
| 416 cx_compare_func cmp_func |
501 cx_compare_func cmp_func |
| 417 ) { |
502 ) { |
| 418 size_t index = cx_array_binary_search_inf_impl( |
503 cx_compare_func_wrapper wrapper = {cmp_func}; |
| 419 arr, size, elem_size, elem, cmp_func); |
504 return cx_array_binary_search_inf_c(arr, size, elem_size, elem, cx_acmp_wrap, &wrapper); |
| 420 // in case of equality, report the largest index |
|
| 421 const char *e = ((const char *) arr) + (index + 1) * elem_size; |
|
| 422 while (index + 1 < size && cmp_func(e, elem) == 0) { |
|
| 423 e += elem_size; |
|
| 424 index++; |
|
| 425 } |
|
| 426 return index; |
|
| 427 } |
505 } |
| 428 |
506 |
| 429 size_t cx_array_binary_search( |
507 size_t cx_array_binary_search( |
| 430 const void *arr, |
508 const void *arr, |
| 431 size_t size, |
509 size_t size, |
| 432 size_t elem_size, |
510 size_t elem_size, |
| 433 const void *elem, |
511 const void *elem, |
| 434 cx_compare_func cmp_func |
512 cx_compare_func cmp_func |
| 435 ) { |
513 ) { |
| 436 size_t index = cx_array_binary_search_inf( |
514 cx_compare_func_wrapper wrapper = {cmp_func}; |
| 437 arr, size, elem_size, elem, cmp_func |
515 return cx_array_binary_search_c(arr, size, elem_size, elem, cx_acmp_wrap, &wrapper); |
| 438 ); |
|
| 439 if (index < size && |
|
| 440 cmp_func(((const char *) arr) + index * elem_size, elem) == 0) { |
|
| 441 return index; |
|
| 442 } else { |
|
| 443 return size; |
|
| 444 } |
|
| 445 } |
516 } |
| 446 |
517 |
| 447 size_t cx_array_binary_search_sup( |
518 size_t cx_array_binary_search_sup( |
| 448 const void *arr, |
519 const void *arr, |
| 449 size_t size, |
520 size_t size, |
| 450 size_t elem_size, |
521 size_t elem_size, |
| 451 const void *elem, |
522 const void *elem, |
| 452 cx_compare_func cmp_func |
523 cx_compare_func cmp_func |
| 453 ) { |
524 ) { |
| 454 size_t index = cx_array_binary_search_inf_impl( |
525 cx_compare_func_wrapper wrapper = {cmp_func}; |
| 455 arr, size, elem_size, elem, cmp_func |
526 return cx_array_binary_search_sup_c(arr, size, elem_size, elem, cx_acmp_wrap, &wrapper); |
| 456 ); |
|
| 457 const char *e = ((const char *) arr) + index * elem_size; |
|
| 458 if (index == size) { |
|
| 459 // no infimum means the first element is supremum |
|
| 460 return 0; |
|
| 461 } else if (cmp_func(e, elem) == 0) { |
|
| 462 // found an equal element, search the smallest index |
|
| 463 e -= elem_size; // e now contains the element at index-1 |
|
| 464 while (index > 0 && cmp_func(e, elem) == 0) { |
|
| 465 e -= elem_size; |
|
| 466 index--; |
|
| 467 } |
|
| 468 return index; |
|
| 469 } else { |
|
| 470 // we already have the largest index of the infimum (by design) |
|
| 471 // the next element is the supremum (or there is no supremum) |
|
| 472 return index + 1; |
|
| 473 } |
|
| 474 } |
527 } |
| 475 |
528 |
| 476 #ifndef CX_ARRAY_SWAP_SBO_SIZE |
529 #ifndef CX_ARRAY_SWAP_SBO_SIZE |
| 477 #define CX_ARRAY_SWAP_SBO_SIZE 128 |
530 #define CX_ARRAY_SWAP_SBO_SIZE 128 |
| 478 #endif |
531 #endif |
| 576 cx_array_list *arl = (cx_array_list *) list; |
629 cx_array_list *arl = (cx_array_list *) list; |
| 577 CxArray wrap = { |
630 CxArray wrap = { |
| 578 arl->data, list->collection.size, arl->capacity |
631 arl->data, list->collection.size, arl->capacity |
| 579 }; |
632 }; |
| 580 |
633 |
| 581 if (cx_array_insert_sorted_( |
634 if (cx_array_insert_sorted_s_( |
| 582 list->collection.allocator, |
635 list->collection.allocator, |
| 583 &wrap, |
636 &wrap, |
| 584 list->collection.elem_size, |
637 list->collection.elem_size, |
| 585 list->collection.cmpfunc, |
|
| 586 sorted_data, |
638 sorted_data, |
| 587 n, |
639 n, |
| 588 allow_duplicates |
640 allow_duplicates, |
| |
641 cx_list_compare_wrapper, |
| |
642 list |
| 589 )) { |
643 )) { |
| 590 // array list implementation is "all or nothing" |
644 // array list implementation is "all or nothing" |
| 591 return 0; // LCOV_EXCL_LINE |
645 return 0; // LCOV_EXCL_LINE |
| 592 } |
646 } |
| 593 arl->data = wrap.data; |
647 arl->data = wrap.data; |
| 761 struct cx_list_s *list, |
815 struct cx_list_s *list, |
| 762 const void *elem, |
816 const void *elem, |
| 763 bool remove |
817 bool remove |
| 764 ) { |
818 ) { |
| 765 assert(list != NULL); |
819 assert(list != NULL); |
| 766 assert(list->collection.cmpfunc != NULL); |
|
| 767 if (list->collection.size == 0) return 0; |
820 if (list->collection.size == 0) return 0; |
| 768 char *cur = ((const cx_array_list *) list)->data; |
821 char *cur = ((const cx_array_list *) list)->data; |
| 769 |
822 |
| 770 // optimize with binary search, when sorted |
823 // optimize with binary search, when sorted |
| 771 if (list->collection.sorted) { |
824 if (list->collection.sorted) { |
| 772 size_t i = cx_array_binary_search( |
825 size_t i = cx_array_binary_search_c( |
| 773 cur, |
826 cur, |
| 774 list->collection.size, |
827 list->collection.size, |
| 775 list->collection.elem_size, |
828 list->collection.elem_size, |
| 776 elem, |
829 elem, |
| 777 list->collection.cmpfunc |
830 cx_list_compare_wrapper, |
| |
831 list |
| 778 ); |
832 ); |
| 779 if (remove && i < list->collection.size) { |
833 if (remove && i < list->collection.size) { |
| 780 cx_arl_remove(list, i, 1, NULL); |
834 cx_arl_remove(list, i, 1, NULL); |
| 781 } |
835 } |
| 782 return i; |
836 return i; |
| 783 } |
837 } |
| 784 |
838 |
| 785 // fallback: linear search |
839 // fallback: linear search |
| 786 for (size_t i = 0; i < list->collection.size; i++) { |
840 for (size_t i = 0; i < list->collection.size; i++) { |
| 787 if (0 == list->collection.cmpfunc(elem, cur)) { |
841 if (0 == cx_list_compare_wrapper(elem, cur, list)) { |
| 788 if (remove) { |
842 if (remove) { |
| 789 cx_arl_remove(list, i, 1, NULL); |
843 cx_arl_remove(list, i, 1, NULL); |
| 790 } |
844 } |
| 791 return i; |
845 return i; |
| 792 } |
846 } |
| 793 cur += list->collection.elem_size; |
847 cur += list->collection.elem_size; |
| 794 } |
848 } |
| 795 return list->collection.size; |
849 return list->collection.size; |
| 796 } |
850 } |
| 797 |
851 |
| |
852 // TODO: remove this hack once we have a solution for qsort() / qsort_s() |
| |
853 static _Thread_local CxList *cx_hack_for_qsort_list; |
| |
854 static int cx_hack_cmp_for_qsort(const void *l, const void *r) { |
| |
855 return cx_list_compare_wrapper(l, r, cx_hack_for_qsort_list); |
| |
856 } |
| |
857 |
| 798 static void cx_arl_sort(struct cx_list_s *list) { |
858 static void cx_arl_sort(struct cx_list_s *list) { |
| 799 assert(list->collection.cmpfunc != NULL); |
859 // TODO: think about if we can somehow use qsort()_s |
| |
860 cx_hack_for_qsort_list = list; |
| 800 qsort(((cx_array_list *) list)->data, |
861 qsort(((cx_array_list *) list)->data, |
| 801 list->collection.size, |
862 list->collection.size, |
| 802 list->collection.elem_size, |
863 list->collection.elem_size, |
| 803 list->collection.cmpfunc |
864 cx_hack_cmp_for_qsort |
| 804 ); |
865 ); |
| 805 } |
866 } |
| 806 |
867 |
| 807 static int cx_arl_compare( |
868 static int cx_arl_compare( |
| 808 const struct cx_list_s *list, |
869 const struct cx_list_s *list, |
| 809 const struct cx_list_s *other |
870 const struct cx_list_s *other |
| 810 ) { |
871 ) { |
| 811 assert(list->collection.cmpfunc != NULL); |
|
| 812 if (list->collection.size == other->collection.size) { |
872 if (list->collection.size == other->collection.size) { |
| 813 const char *left = ((const cx_array_list *) list)->data; |
873 const char *left = ((const cx_array_list *) list)->data; |
| 814 const char *right = ((const cx_array_list *) other)->data; |
874 const char *right = ((const cx_array_list *) other)->data; |
| 815 for (size_t i = 0; i < list->collection.size; i++) { |
875 for (size_t i = 0; i < list->collection.size; i++) { |
| 816 int d = list->collection.cmpfunc(left, right); |
876 int d = cx_list_compare_wrapper(left, right, (void*)list); |
| 817 if (d != 0) { |
877 if (d != 0) { |
| 818 return d; |
878 return d; |
| 819 } |
879 } |
| 820 left += list->collection.elem_size; |
880 left += list->collection.elem_size; |
| 821 right += other->collection.elem_size; |
881 right += other->collection.elem_size; |