src/array_list.c

changeset 1618
ef7cab6eb131
parent 1611
de21dd0d1426
equal deleted inserted replaced
1617:d4385f35f8b0 1618:ef7cab6eb131
125 array->size += n; 125 array->size += n;
126 126
127 return 0; 127 return 0;
128 } 128 }
129 129
130 int cx_array_insert_sorted_( 130 int cx_array_insert_sorted_s_(
131 const CxAllocator *allocator, 131 const CxAllocator *allocator,
132 CxArray *array, 132 CxArray *array,
133 size_t elem_size, 133 size_t elem_size,
134 cx_compare_func cmp_func,
135 const void *sorted_data, 134 const void *sorted_data,
136 size_t n, 135 size_t n,
137 bool allow_duplicates 136 bool allow_duplicates,
137 cx_compare_func2 cmp_func,
138 void *context
138 ) { 139 ) {
139 // assert pointers 140 // assert pointers
140 assert(allocator != NULL); 141 assert(allocator != NULL);
141 assert(array != NULL); 142 assert(array != NULL);
142 assert(cmp_func != NULL); 143 assert(cmp_func != NULL);
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 {
258 259
259 // when all source elements are in place, we are done 260 // when all source elements are in place, we are done
260 if (si >= n) break; 261 if (si >= n) break;
261 262
262 // determine how many buffered elements need to be restored 263 // determine how many buffered elements need to be restored
263 copy_len = cx_array_binary_search_sup( 264 copy_len = cx_array_binary_search_sup_c(
264 bptr, 265 bptr,
265 new_size - bi, 266 new_size - bi,
266 elem_size, 267 elem_size,
267 src, 268 src,
268 cmp_func 269 cmp_func,
270 context
269 ); 271 );
270 272
271 // restore the buffered elements 273 // restore the buffered elements
272 bytes_copied = copy_len * elem_size; 274 bytes_copied = copy_len * elem_size;
273 memmove(dest, bptr, bytes_copied); 275 memmove(dest, bptr, bytes_copied);
293 size_t copy_len = 1, skip_len = 0; 295 size_t copy_len = 1, skip_len = 0;
294 { 296 {
295 const char *left_src = src; 297 const char *left_src = src;
296 while (si + copy_len + skip_len < n) { 298 while (si + copy_len + skip_len < n) {
297 const char *right_src = left_src + elem_size; 299 const char *right_src = left_src + elem_size;
298 int d = cmp_func(left_src, right_src); 300 int d = cmp_func(left_src, right_src, context);
299 if (d < 0) { 301 if (d < 0) {
300 if (skip_len > 0) { 302 if (skip_len > 0) {
301 // new larger element found; 303 // new larger element found;
302 // handle it in the next cycle 304 // handle it in the next cycle
303 break; 305 break;
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) {
352 static size_t cx_array_binary_search_inf_impl( 368 static size_t cx_array_binary_search_inf_impl(
353 const void *arr, 369 const void *arr,
354 size_t size, 370 size_t size,
355 size_t elem_size, 371 size_t elem_size,
356 const void *elem, 372 const void *elem,
357 cx_compare_func cmp_func 373 cx_compare_func2 cmp_func,
374 void *context
358 ) { 375 ) {
359 // special case: empty array 376 // special case: empty array
360 if (size == 0) return 0; 377 if (size == 0) return 0;
361 378
362 // declare a variable that will contain the compare results 379 // declare a variable that will contain the compare results
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;

mercurial