289 // perform reallocation |
289 // perform reallocation |
290 void *newmem = reallocator->realloc( |
290 void *newmem = reallocator->realloc( |
291 *target, oldcap, newcap, elem_size, reallocator |
291 *target, oldcap, newcap, elem_size, reallocator |
292 ); |
292 ); |
293 if (newmem == NULL) { |
293 if (newmem == NULL) { |
294 return 1; |
294 return 1; // LCOV_EXCL_LINE |
295 } |
295 } |
296 |
296 |
297 // repair src pointer, if necessary |
297 // repair src pointer, if necessary |
298 if (repairsrc) { |
298 if (repairsrc) { |
299 src = ((char *) newmem) + (srcaddr - targetaddr); |
299 src = ((char *) newmem) + (srcaddr - targetaddr); |
418 elem_count - si, |
418 elem_count - si, |
419 elem_size, |
419 elem_size, |
420 bptr, |
420 bptr, |
421 cmp_func |
421 cmp_func |
422 ); |
422 ); |
423 // handle the identical elements |
423 // binary search gives us the smallest index; |
424 size_t skip_len = 0; |
424 // we also want to include equal elements here |
425 while (copy_len < elem_count - si |
425 while (si + copy_len < elem_count |
426 && cmp_func(bptr, src+copy_len*elem_size) == 0) { |
426 && cmp_func(bptr, src+copy_len*elem_size) == 0) { |
427 copy_len++; |
427 copy_len++; |
428 if (!allow_duplicates) { |
428 } |
429 skip_len++; |
429 |
|
430 // copy the source elements |
|
431 if (copy_len > 0) { |
|
432 if (allow_duplicates) { |
|
433 // we can copy the entire chunk |
|
434 bytes_copied = copy_len * elem_size; |
|
435 memcpy(dest, src, bytes_copied); |
|
436 dest += bytes_copied; |
|
437 src += bytes_copied; |
|
438 si += copy_len; |
|
439 di += copy_len; |
|
440 } else { |
|
441 // first, check the end of the source chunk |
|
442 // for being a duplicate of the bptr |
|
443 const char *end_of_src = src + (copy_len - 1) * elem_size; |
|
444 size_t skip_len = 0; |
|
445 while (copy_len > 0 && cmp_func(bptr, end_of_src) == 0) { |
|
446 end_of_src -= elem_size; |
|
447 skip_len++; |
|
448 copy_len--; |
|
449 } |
|
450 char *last = dest == *target ? NULL : dest - elem_size; |
|
451 // then iterate through the source chunk |
|
452 // and skip all duplicates with the last element in the array |
|
453 size_t more_skipped = 0; |
|
454 for (unsigned j = 0; j < copy_len; j++) { |
|
455 if (last != NULL && cmp_func(last, src) == 0) { |
|
456 // duplicate - skip |
|
457 src += elem_size; |
|
458 si++; |
|
459 more_skipped++; |
|
460 } else { |
|
461 memcpy(dest, src, elem_size); |
|
462 src += elem_size; |
|
463 last = dest; |
|
464 dest += elem_size; |
|
465 si++; |
|
466 di++; |
|
467 } |
|
468 } |
|
469 // skip the previously identified elements as well |
|
470 src += skip_len * elem_size; |
|
471 si += skip_len; |
|
472 skip_len += more_skipped; |
|
473 // reduce the actual size by the number of skipped elements |
|
474 *size -= skip_len; |
430 } |
475 } |
431 } |
476 } |
432 copy_len -= skip_len; |
|
433 |
|
434 // copy the source elements |
|
435 bytes_copied = copy_len * elem_size; |
|
436 memcpy(dest, src, bytes_copied); |
|
437 dest += bytes_copied; |
|
438 src += bytes_copied; |
|
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; |
|
446 |
477 |
447 // when all source elements are in place, we are done |
478 // when all source elements are in place, we are done |
448 if (si >= elem_count) break; |
479 if (si >= elem_count) break; |
449 |
480 |
450 // determine how many buffered elements need to be restored |
481 // determine how many buffered elements need to be restored |
461 memmove(dest, bptr, bytes_copied); |
492 memmove(dest, bptr, bytes_copied); |
462 dest += bytes_copied; |
493 dest += bytes_copied; |
463 bptr += bytes_copied; |
494 bptr += bytes_copied; |
464 di += copy_len; |
495 di += copy_len; |
465 bi += copy_len; |
496 bi += copy_len; |
466 |
497 } |
467 // check if the last restored element is a duplicate of the next source |
498 |
468 if (!allow_duplicates && copy_len > 0) { |
499 // still source elements left? |
469 char *last = dest - elem_size; |
500 if (si < elem_count) { |
470 while (si < elem_count && cmp_func(last, src) == 0) { |
501 if (allow_duplicates) { |
471 src += elem_size; |
502 // duplicates allowed or nothing inserted yet: simply copy everything |
472 si++; |
503 memcpy(dest, src, elem_size * (elem_count - si)); |
473 (*size)--; |
504 } else { |
|
505 if (dest != *target) { |
|
506 // skip all source elements that equal the last element |
|
507 char *last = dest - elem_size; |
|
508 while (si < elem_count) { |
|
509 if (last != NULL && cmp_func(last, src) == 0) { |
|
510 src += elem_size; |
|
511 si++; |
|
512 (*size)--; |
|
513 } else { |
|
514 break; |
|
515 } |
|
516 } |
474 } |
517 } |
475 } |
518 // we must check the elements in the chunk one by one |
476 } |
519 while (si < elem_count) { |
477 |
520 // find a chain of elements that can be copied |
478 // still source elements left? simply append them |
521 size_t copy_len = 1, skip_len = 0; |
479 if (si < elem_count) { |
522 { |
480 memcpy(dest, src, elem_size * (elem_count - si)); |
523 const char *left_src = src; |
|
524 while (si + copy_len < elem_count) { |
|
525 const char *right_src = left_src + elem_size; |
|
526 int d = cmp_func(left_src, right_src); |
|
527 if (d < 0) { |
|
528 if (skip_len > 0) { |
|
529 // new larger element found; |
|
530 // handle it in the next cycle |
|
531 break; |
|
532 } |
|
533 left_src += elem_size; |
|
534 copy_len++; |
|
535 } else if (d == 0) { |
|
536 left_src += elem_size; |
|
537 skip_len++; |
|
538 } else { |
|
539 break; |
|
540 } |
|
541 } |
|
542 } |
|
543 size_t bytes_copied = copy_len * elem_size; |
|
544 memcpy(dest, src, bytes_copied); |
|
545 dest += bytes_copied; |
|
546 src += bytes_copied + skip_len * elem_size; |
|
547 si += copy_len + skip_len; |
|
548 di += copy_len; |
|
549 *size -= skip_len; |
|
550 } |
|
551 } |
481 } |
552 } |
482 |
553 |
483 // buffered elements need to be moved when we skipped duplicates |
554 // buffered elements need to be moved when we skipped duplicates |
484 size_t total_skipped = new_size - *size; |
555 size_t total_skipped = new_size - *size; |
485 if (bi < new_size && total_skipped > 0) { |
556 if (bi < new_size && total_skipped > 0) { |