333 |
333 |
334 // return successfully |
334 // return successfully |
335 return 0; |
335 return 0; |
336 } |
336 } |
337 |
337 |
338 int cx_array_insert_sorted( |
338 static int cx_array_insert_sorted_impl( |
339 void **target, |
339 void **target, |
340 size_t *size, |
340 size_t *size, |
341 size_t *capacity, |
341 size_t *capacity, |
342 cx_compare_func cmp_func, |
342 cx_compare_func cmp_func, |
343 const void *sorted_data, |
343 const void *sorted_data, |
344 size_t elem_size, |
344 size_t elem_size, |
345 size_t elem_count, |
345 size_t elem_count, |
346 CxArrayReallocator *reallocator |
346 CxArrayReallocator *reallocator, |
|
347 bool allow_duplicates |
347 ) { |
348 ) { |
348 // assert pointers |
349 // assert pointers |
349 assert(target != NULL); |
350 assert(target != NULL); |
350 assert(size != NULL); |
351 assert(size != NULL); |
351 assert(capacity != NULL); |
352 assert(capacity != NULL); |
367 } |
368 } |
368 |
369 |
369 // store some counts |
370 // store some counts |
370 size_t old_size = *size; |
371 size_t old_size = *size; |
371 size_t old_capacity = *capacity; |
372 size_t old_capacity = *capacity; |
|
373 // the necessary capacity is the worst case assumption, including duplicates |
372 size_t needed_capacity = old_size + elem_count; |
374 size_t needed_capacity = old_size + elem_count; |
373 |
375 |
374 // if we need more than we have, try a reallocation |
376 // if we need more than we have, try a reallocation |
375 if (needed_capacity > old_capacity) { |
377 if (needed_capacity > old_capacity) { |
376 size_t new_capacity = cx_array_align_capacity(needed_capacity, 16, SIZE_MAX); |
378 size_t new_capacity = cx_array_align_capacity(needed_capacity, 16, SIZE_MAX); |
416 elem_count - si, |
418 elem_count - si, |
417 elem_size, |
419 elem_size, |
418 bptr, |
420 bptr, |
419 cmp_func |
421 cmp_func |
420 ); |
422 ); |
|
423 // handle the identical elements |
|
424 size_t skip_len = 0; |
|
425 while (copy_len < elem_count - si |
|
426 && cmp_func(bptr, src+copy_len*elem_size) == 0) { |
|
427 copy_len++; |
|
428 if (!allow_duplicates) { |
|
429 skip_len++; |
|
430 } |
|
431 } |
|
432 copy_len -= skip_len; |
421 |
433 |
422 // copy the source elements |
434 // copy the source elements |
423 bytes_copied = copy_len * elem_size; |
435 bytes_copied = copy_len * elem_size; |
424 memcpy(dest, src, bytes_copied); |
436 memcpy(dest, src, bytes_copied); |
425 dest += bytes_copied; |
437 dest += bytes_copied; |
426 src += bytes_copied; |
438 src += bytes_copied; |
427 si += copy_len; |
439 si += copy_len; |
|
440 di += copy_len; |
|
441 |
|
442 // skip duplicates when they are not allowed |
|
443 src += skip_len * elem_size; |
|
444 si += skip_len; |
|
445 *size -= skip_len; |
428 |
446 |
429 // when all source elements are in place, we are done |
447 // when all source elements are in place, we are done |
430 if (si >= elem_count) break; |
448 if (si >= elem_count) break; |
431 |
449 |
432 // determine how many buffered elements need to be restored |
450 // determine how many buffered elements need to be restored |
441 // restore the buffered elements |
459 // restore the buffered elements |
442 bytes_copied = copy_len * elem_size; |
460 bytes_copied = copy_len * elem_size; |
443 memmove(dest, bptr, bytes_copied); |
461 memmove(dest, bptr, bytes_copied); |
444 dest += bytes_copied; |
462 dest += bytes_copied; |
445 bptr += bytes_copied; |
463 bptr += bytes_copied; |
|
464 di += copy_len; |
446 bi += copy_len; |
465 bi += copy_len; |
|
466 |
|
467 // check if the last restored element is a duplicate of the next source |
|
468 if (!allow_duplicates && copy_len > 0) { |
|
469 char *last = dest - elem_size; |
|
470 while (si < elem_count && cmp_func(last, src) == 0) { |
|
471 src += elem_size; |
|
472 si++; |
|
473 (*size)--; |
|
474 } |
|
475 } |
447 } |
476 } |
448 |
477 |
449 // still source elements left? simply append them |
478 // still source elements left? simply append them |
450 if (si < elem_count) { |
479 if (si < elem_count) { |
451 memcpy(dest, src, elem_size * (elem_count - si)); |
480 memcpy(dest, src, elem_size * (elem_count - si)); |
452 } |
481 } |
453 |
482 |
454 // still buffer elements left? |
483 // buffered elements need to be moved when we skipped duplicates |
455 // don't worry, we already moved them to the correct place |
484 size_t total_skipped = new_size - *size; |
|
485 if (bi < new_size && total_skipped > 0) { |
|
486 // move the remaining buffer to the end of the array |
|
487 memmove(dest, bptr, elem_size * (new_size - bi)); |
|
488 } |
456 |
489 |
457 return 0; |
490 return 0; |
|
491 } |
|
492 |
|
493 int cx_array_insert_sorted( |
|
494 void **target, |
|
495 size_t *size, |
|
496 size_t *capacity, |
|
497 cx_compare_func cmp_func, |
|
498 const void *sorted_data, |
|
499 size_t elem_size, |
|
500 size_t elem_count, |
|
501 CxArrayReallocator *reallocator |
|
502 ) { |
|
503 return cx_array_insert_sorted_impl(target, size, capacity, |
|
504 cmp_func, sorted_data, elem_size, elem_count, reallocator, true); |
|
505 } |
|
506 |
|
507 int cx_array_insert_unique( |
|
508 void **target, |
|
509 size_t *size, |
|
510 size_t *capacity, |
|
511 cx_compare_func cmp_func, |
|
512 const void *sorted_data, |
|
513 size_t elem_size, |
|
514 size_t elem_count, |
|
515 CxArrayReallocator *reallocator |
|
516 ) { |
|
517 return cx_array_insert_sorted_impl(target, size, capacity, |
|
518 cmp_func, sorted_data, elem_size, elem_count, reallocator, false); |
458 } |
519 } |
459 |
520 |
460 size_t cx_array_binary_search_inf( |
521 size_t cx_array_binary_search_inf( |
461 const void *arr, |
522 const void *arr, |
462 size_t size, |
523 size_t size, |
500 pivot_index = left_index + (right_index - left_index) / 2; |
561 pivot_index = left_index + (right_index - left_index) / 2; |
501 const char *arr_elem = array + pivot_index * elem_size; |
562 const char *arr_elem = array + pivot_index * elem_size; |
502 result = cmp_func(elem, arr_elem); |
563 result = cmp_func(elem, arr_elem); |
503 if (result == 0) { |
564 if (result == 0) { |
504 // found it! |
565 // found it! |
|
566 // check previous elements; |
|
567 // when they are equal, report the smallest index |
|
568 arr_elem -= elem_size; |
|
569 while (pivot_index > 0 && cmp_func(elem, arr_elem) == 0) { |
|
570 pivot_index--; |
|
571 arr_elem -= elem_size; |
|
572 } |
505 return pivot_index; |
573 return pivot_index; |
506 } else if (result < 0) { |
574 } else if (result < 0) { |
507 // element is smaller than pivot, continue search left |
575 // element is smaller than pivot, continue search left |
508 right_index = pivot_index - 1; |
576 right_index = pivot_index - 1; |
509 } else { |
577 } else { |
698 } else { |
766 } else { |
699 return n; |
767 return n; |
700 } |
768 } |
701 } |
769 } |
702 |
770 |
|
771 static size_t cx_arl_insert_unique( |
|
772 struct cx_list_s *list, |
|
773 const void *sorted_data, |
|
774 size_t n |
|
775 ) { |
|
776 // get a correctly typed pointer to the list |
|
777 cx_array_list *arl = (cx_array_list *) list; |
|
778 |
|
779 if (cx_array_insert_unique( |
|
780 &arl->data, |
|
781 &list->collection.size, |
|
782 &arl->capacity, |
|
783 list->collection.cmpfunc, |
|
784 sorted_data, |
|
785 list->collection.elem_size, |
|
786 n, |
|
787 &arl->reallocator |
|
788 )) { |
|
789 // array list implementation is "all or nothing" |
|
790 return 0; |
|
791 } else { |
|
792 return n; |
|
793 } |
|
794 } |
|
795 |
703 static void *cx_arl_insert_element( |
796 static void *cx_arl_insert_element( |
704 struct cx_list_s *list, |
797 struct cx_list_s *list, |
705 size_t index, |
798 size_t index, |
706 const void *element |
799 const void *element |
707 ) { |
800 ) { |