ucx/linked_list.c

changeset 888
af685cc9d623
parent 854
1c8401ece69e
equal deleted inserted replaced
877:b60487c3ec36 888:af685cc9d623
242 assert(ll_next(new_node) == NULL); 242 assert(ll_next(new_node) == NULL);
243 cx_linked_list_insert_sorted_chain( 243 cx_linked_list_insert_sorted_chain(
244 begin, end, loc_prev, loc_next, new_node, cmp_func); 244 begin, end, loc_prev, loc_next, new_node, cmp_func);
245 } 245 }
246 246
247 void cx_linked_list_insert_sorted_chain( 247 static void *cx_linked_list_insert_sorted_chain_impl(
248 void **begin, 248 void **begin,
249 void **end, 249 void **end,
250 ptrdiff_t loc_prev, 250 ptrdiff_t loc_prev,
251 ptrdiff_t loc_next, 251 ptrdiff_t loc_next,
252 void *insert_begin, 252 void *insert_begin,
253 cx_compare_func cmp_func 253 cx_compare_func cmp_func,
254 bool allow_duplicates
254 ) { 255 ) {
255 assert(begin != NULL); 256 assert(begin != NULL);
256 assert(loc_next >= 0); 257 assert(loc_next >= 0);
257 assert(insert_begin != NULL); 258 assert(insert_begin != NULL);
258 259
259 // track currently observed nodes 260 // strategy: build completely new chains from scratch
260 void *dest_prev = NULL; 261 void *source_original = *begin;
261 void *dest = *begin; 262 void *source_argument = insert_begin;
262 void *src = insert_begin; 263 void *new_begin = NULL;
263 264 void *new_end = NULL;
264 // special case: list is empty 265 void *dup_begin = NULL;
265 if (dest == NULL) { 266 void *dup_end = NULL;
266 *begin = src; 267
267 if (end != NULL) { 268 // determine the new start
268 *end = cx_linked_list_last(src, loc_next); 269 {
269 } 270 int d = source_original == NULL ? 1 : cmp_func(source_original, source_argument);
270 return; 271 if (d <= 0) {
271 } 272 // the new chain starts with the original chain
272 273 new_begin = new_end = source_original;
273 // search the list for insertion points 274 source_original = ll_next(source_original);
274 while (dest != NULL && src != NULL) { 275 if (d == 0) {
275 // compare current list node with source node 276 if (allow_duplicates) {
276 // if less or equal, skip 277 // duplicate allowed, append it to the chain
277 if (cmp_func(dest, src) <= 0) { 278 cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
278 dest_prev = dest; 279 new_end = source_argument;
279 dest = ll_next(dest); 280 } else {
280 continue; 281 // duplicate is not allowed, start a duplicate chain with the argument
281 } 282 dup_begin = dup_end = source_argument;
282 283 }
283 // determine chain of elements that can be inserted 284 source_argument = ll_next(source_argument);
284 void *end_of_chain = src;
285 void *next_in_chain = ll_next(src);
286 while (next_in_chain != NULL) {
287 // once we become larger than the list elem, break
288 if (cmp_func(dest, next_in_chain) <= 0) {
289 break;
290 } 285 }
291 // otherwise, we can insert one more
292 end_of_chain = next_in_chain;
293 next_in_chain = ll_next(next_in_chain);
294 }
295
296 // insert the elements
297 if (dest_prev == NULL) {
298 // new begin
299 *begin = src;
300 } else { 286 } else {
301 cx_linked_list_link(dest_prev, src, loc_prev, loc_next); 287 // input is smaller, or there is no original chain;
302 } 288 // start the new chain with the source argument
303 cx_linked_list_link(end_of_chain, dest, loc_prev, loc_next); 289 new_begin = new_end = source_argument;
304 290 source_argument = ll_next(source_argument);
305 // continue with next 291 }
306 src = next_in_chain; 292 }
307 dest_prev = dest; 293
308 dest = ll_next(dest); 294 // now successively compare the elements and add them to the correct chains
309 } 295 while (source_original != NULL && source_argument != NULL) {
310 296 int d = cmp_func(source_original, source_argument);
311 // insert remaining items 297 if (d <= 0) {
312 if (src != NULL) { 298 // the original is not larger, add it to the chain
313 cx_linked_list_link(dest_prev, src, loc_prev, loc_next); 299 cx_linked_list_link(new_end, source_original, loc_prev, loc_next);
314 } 300 new_end = source_original;
315 301 source_original = ll_next(source_original);
316 // determine new end of list, if requested 302 if (d == 0) {
303 if (allow_duplicates) {
304 // duplicate allowed, append it to the chain
305 cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
306 new_end = source_argument;
307 } else {
308 // duplicate is not allowed, append it to the duplicate chain
309 if (dup_end == NULL) {
310 dup_begin = dup_end = source_argument;
311 } else {
312 cx_linked_list_link(dup_end, source_argument, loc_prev, loc_next);
313 dup_end = source_argument;
314 }
315 }
316 source_argument = ll_next(source_argument);
317 }
318 } else {
319 // the original is larger, append the source argument to the chain
320 // check if we must discard the source argument as duplicate
321 if (!allow_duplicates && cmp_func(new_end, source_argument) == 0) {
322 if (dup_end == NULL) {
323 dup_begin = dup_end = source_argument;
324 } else {
325 cx_linked_list_link(dup_end, source_argument, loc_prev, loc_next);
326 dup_end = source_argument;
327 }
328 } else {
329 // no duplicate or duplicates allowed
330 cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
331 new_end = source_argument;
332 }
333 source_argument = ll_next(source_argument);
334 }
335 }
336
337 if (source_original != NULL) {
338 // something is left from the original chain, append it
339 cx_linked_list_link(new_end, source_original, loc_prev, loc_next);
340 new_end = cx_linked_list_last(source_original, loc_next);
341 } else if (source_argument != NULL) {
342 // something is left from the input chain;
343 // when we allow duplicates, append it
344 if (allow_duplicates) {
345 cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
346 new_end = cx_linked_list_last(source_argument, loc_next);
347 } else {
348 // otherwise we must check one-by-one
349 while (source_argument != NULL) {
350 if (cmp_func(new_end, source_argument) == 0) {
351 if (dup_end == NULL) {
352 dup_begin = dup_end = source_argument;
353 } else {
354 cx_linked_list_link(dup_end, source_argument, loc_prev, loc_next);
355 dup_end = source_argument;
356 }
357 } else {
358 cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
359 new_end = source_argument;
360 }
361 source_argument = ll_next(source_argument);
362 }
363 }
364 }
365
366 // null the next pointers at the end of the chain
367 ll_next(new_end) = NULL;
368 if (dup_end != NULL) {
369 ll_next(dup_end) = NULL;
370 }
371
372 // null the optional prev pointers
373 if (loc_prev >= 0) {
374 ll_prev(new_begin) = NULL;
375 if (dup_begin != NULL) {
376 ll_prev(dup_begin) = NULL;
377 }
378 }
379
380 // output
381 *begin = new_begin;
317 if (end != NULL) { 382 if (end != NULL) {
318 *end = cx_linked_list_last( 383 *end = new_end;
319 dest != NULL ? dest : dest_prev, loc_next); 384 }
320 } 385 return dup_begin;
386 }
387
388 void cx_linked_list_insert_sorted_chain(
389 void **begin,
390 void **end,
391 ptrdiff_t loc_prev,
392 ptrdiff_t loc_next,
393 void *insert_begin,
394 cx_compare_func cmp_func
395 ) {
396 cx_linked_list_insert_sorted_chain_impl(
397 begin, end, loc_prev, loc_next,
398 insert_begin, cmp_func, true);
399 }
400
401 int cx_linked_list_insert_unique(
402 void **begin,
403 void **end,
404 ptrdiff_t loc_prev,
405 ptrdiff_t loc_next,
406 void *new_node,
407 cx_compare_func cmp_func
408 ) {
409 assert(ll_next(new_node) == NULL);
410 return NULL != cx_linked_list_insert_unique_chain(
411 begin, end, loc_prev, loc_next, new_node, cmp_func);
412 }
413
414 void *cx_linked_list_insert_unique_chain(
415 void **begin,
416 void **end,
417 ptrdiff_t loc_prev,
418 ptrdiff_t loc_next,
419 void *insert_begin,
420 cx_compare_func cmp_func
421 ) {
422 return cx_linked_list_insert_sorted_chain_impl(
423 begin, end, loc_prev, loc_next,
424 insert_begin, cmp_func, false);
321 } 425 }
322 426
323 size_t cx_linked_list_remove_chain( 427 size_t cx_linked_list_remove_chain(
324 void **begin, 428 void **begin,
325 void **end, 429 void **end,
366 } else if (loc_prev >= 0) { 470 } else if (loc_prev >= 0) {
367 ll_prev(next) = prev; 471 ll_prev(next) = prev;
368 } 472 }
369 473
370 return removed; 474 return removed;
475 }
476
477 void cx_linked_list_remove(
478 void **begin,
479 void **end,
480 ptrdiff_t loc_prev,
481 ptrdiff_t loc_next,
482 void *node
483 ) {
484 cx_linked_list_remove_chain(begin, end, loc_prev, loc_next, node, 1);
371 } 485 }
372 486
373 size_t cx_linked_list_size( 487 size_t cx_linked_list_size(
374 const void *node, 488 const void *node,
375 ptrdiff_t loc_next 489 ptrdiff_t loc_next
399 void **begin, 513 void **begin,
400 void **end 514 void **end
401 ) { 515 ) {
402 void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE]; 516 void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE];
403 void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ? 517 void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ?
404 malloc(sizeof(void *) * length) : sbo; 518 cxMallocDefault(sizeof(void *) * length) : sbo;
405 if (sorted == NULL) abort(); 519 if (sorted == NULL) abort();
406 void *rc, *lc; 520 void *rc, *lc;
407 521
408 lc = ls; 522 lc = ls;
409 rc = le; 523 rc = le;
437 ll_next(sorted[length - 1]) = NULL; 551 ll_next(sorted[length - 1]) = NULL;
438 552
439 *begin = sorted[0]; 553 *begin = sorted[0];
440 *end = sorted[length - 1]; 554 *end = sorted[length - 1];
441 if (sorted != sbo) { 555 if (sorted != sbo) {
442 free(sorted); 556 cxFreeDefault(sorted);
443 } 557 }
444 } 558 }
445 559
446 void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive function 560 void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive function
447 void **begin, 561 void **begin,
562 *begin = prev; 676 *begin = prev;
563 } 677 }
564 678
565 // HIGH LEVEL LINKED LIST IMPLEMENTATION 679 // HIGH LEVEL LINKED LIST IMPLEMENTATION
566 680
567 typedef struct cx_linked_list_node cx_linked_list_node; 681 static void *cx_ll_node_at(
568 struct cx_linked_list_node {
569 cx_linked_list_node *prev;
570 cx_linked_list_node *next;
571 char payload[];
572 };
573
574 #define CX_LL_LOC_PREV offsetof(cx_linked_list_node, prev)
575 #define CX_LL_LOC_NEXT offsetof(cx_linked_list_node, next)
576 #define CX_LL_LOC_DATA offsetof(cx_linked_list_node, payload)
577
578 typedef struct {
579 struct cx_list_s base;
580 cx_linked_list_node *begin;
581 cx_linked_list_node *end;
582 } cx_linked_list;
583
584 static cx_linked_list_node *cx_ll_node_at(
585 const cx_linked_list *list, 682 const cx_linked_list *list,
586 size_t index 683 size_t index
587 ) { 684 ) {
588 if (index >= list->base.collection.size) { 685 if (index >= list->base.collection.size) {
589 return NULL; 686 return NULL;
590 } else if (index > list->base.collection.size / 2) { 687 } else if (index > list->base.collection.size / 2) {
591 return cx_linked_list_at(list->end, list->base.collection.size - 1, CX_LL_LOC_PREV, index); 688 return cx_linked_list_at(list->end, list->base.collection.size - 1, list->loc_prev, index);
592 } else { 689 } else {
593 return cx_linked_list_at(list->begin, 0, CX_LL_LOC_NEXT, index); 690 return cx_linked_list_at(list->begin, 0, list->loc_next, index);
594 } 691 }
595 } 692 }
596 693
597 static cx_linked_list_node *cx_ll_malloc_node(const struct cx_list_s *list) { 694 static void *cx_ll_malloc_node(const cx_linked_list *list) {
598 return cxMalloc(list->collection.allocator, 695 return cxZalloc(list->base.collection.allocator,
599 sizeof(cx_linked_list_node) + list->collection.elem_size); 696 list->loc_data + list->base.collection.elem_size + list->extra_data_len);
600 } 697 }
601 698
602 static int cx_ll_insert_at( 699 static int cx_ll_insert_at(
603 struct cx_list_s *list, 700 struct cx_list_s *list,
604 cx_linked_list_node *node, 701 void *node,
605 const void *elem 702 const void *elem
606 ) { 703 ) {
704 cx_linked_list *ll = (cx_linked_list *) list;
607 705
608 // create the new new_node 706 // create the new new_node
609 cx_linked_list_node *new_node = cx_ll_malloc_node(list); 707 void *new_node = cx_ll_malloc_node(ll);
610 708
611 // sortir if failed 709 // sortir if failed
612 if (new_node == NULL) return 1; 710 if (new_node == NULL) return 1;
613 711
614 // initialize new new_node 712 // copy the data
615 new_node->prev = new_node->next = NULL; 713 if (elem != NULL) {
616 memcpy(new_node->payload, elem, list->collection.elem_size); 714 memcpy((char*)new_node + ll->loc_data, elem, list->collection.elem_size);
715 }
617 716
618 // insert 717 // insert
619 cx_linked_list *ll = (cx_linked_list *) list;
620 cx_linked_list_insert_chain( 718 cx_linked_list_insert_chain(
621 (void **) &ll->begin, (void **) &ll->end, 719 &ll->begin, &ll->end,
622 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, 720 ll->loc_prev, ll->loc_next,
623 node, new_node, new_node 721 node, new_node, new_node
624 ); 722 );
625 723
626 // increase the size and return 724 // increase the size and return
627 list->collection.size++; 725 list->collection.size++;
636 ) { 734 ) {
637 // out-of bounds and corner case check 735 // out-of bounds and corner case check
638 if (index > list->collection.size || n == 0) return 0; 736 if (index > list->collection.size || n == 0) return 0;
639 737
640 // find position efficiently 738 // find position efficiently
641 cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); 739 void *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
642 740
643 // perform first insert 741 // perform first insert
644 if (0 != cx_ll_insert_at(list, node, array)) return 1; 742 if (0 != cx_ll_insert_at(list, node, array)) return 1;
645 743
646 // is there more? 744 // is there more?
647 if (n == 1) return 1; 745 if (n == 1) return 1;
648 746
649 // we now know exactly where we are 747 // we now know exactly where we are
650 node = node == NULL ? ((cx_linked_list *) list)->begin : node->next; 748 cx_linked_list *ll = (cx_linked_list *) list;
749 node = node == NULL ? ((cx_linked_list *) list)->begin : CX_LL_PTR(node, ll->loc_next);
651 750
652 // we can add the remaining nodes and immediately advance to the inserted node 751 // we can add the remaining nodes and immediately advance to the inserted node
653 const char *source = array; 752 const char *source = array;
654 for (size_t i = 1; i < n; i++) { 753 for (size_t i = 1; i < n; i++) {
655 source += list->collection.elem_size; 754 if (source != NULL) {
755 source += list->collection.elem_size;
756 }
656 if (0 != cx_ll_insert_at(list, node, source)) return i; 757 if (0 != cx_ll_insert_at(list, node, source)) return i;
657 node = node->next; 758 node = CX_LL_PTR(node, ll->loc_next);
658 } 759 }
659 return n; 760 return n;
660 } 761 }
661 762
662 static int cx_ll_insert_element( 763 static void *cx_ll_insert_element(
663 struct cx_list_s *list, 764 struct cx_list_s *list,
664 size_t index, 765 size_t index,
665 const void *element 766 const void *element
666 ) { 767 ) {
667 return 1 != cx_ll_insert_array(list, index, element, 1); 768 // out-of-bounds check
769 if (index > list->collection.size) return NULL;
770
771 // find position efficiently
772 void *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
773
774 // perform first insert
775 if (cx_ll_insert_at(list, node, element)) return NULL;
776
777 // return a pointer to the data of the inserted node
778 cx_linked_list *ll = (cx_linked_list *) list;
779 if (node == NULL) {
780 return (char*)(ll->begin) + ll->loc_data;
781 } else {
782 char *next = CX_LL_PTR(node, ll->loc_next);
783 return next + ll->loc_data;
784 }
668 } 785 }
669 786
670 static _Thread_local cx_compare_func cx_ll_insert_sorted_cmp_func; 787 static _Thread_local cx_compare_func cx_ll_insert_sorted_cmp_func;
788 static _Thread_local off_t cx_ll_insert_sorted_loc_data;
671 789
672 static int cx_ll_insert_sorted_cmp_helper(const void *l, const void *r) { 790 static int cx_ll_insert_sorted_cmp_helper(const void *l, const void *r) {
673 const cx_linked_list_node *left = l; 791 const char *left = (const char*)l + cx_ll_insert_sorted_loc_data;
674 const cx_linked_list_node *right = r; 792 const char *right = (const char*)r + cx_ll_insert_sorted_loc_data;
675 return cx_ll_insert_sorted_cmp_func(left->payload, right->payload); 793 return cx_ll_insert_sorted_cmp_func(left, right);
794 }
795
796 static size_t cx_ll_insert_sorted_impl(
797 struct cx_list_s *list,
798 const void *array,
799 size_t n,
800 bool allow_duplicates
801 ) {
802 cx_linked_list *ll = (cx_linked_list *) list;
803
804 // special case
805 if (n == 0) return 0;
806
807 // create a new chain of nodes
808 void *chain = cx_ll_malloc_node(ll);
809 if (chain == NULL) return 0;
810
811 memcpy((char*)chain + ll->loc_data, array, list->collection.elem_size);
812
813 // add all elements from the array to that chain
814 void *prev = chain;
815 const char *src = array;
816 size_t inserted = 1;
817 for (; inserted < n; inserted++) {
818 void *next = cx_ll_malloc_node(ll);
819 if (next == NULL) break;
820 src += list->collection.elem_size;
821 memcpy((char*)next + ll->loc_data, src, list->collection.elem_size);
822 CX_LL_PTR(prev, ll->loc_next) = next;
823 CX_LL_PTR(next, ll->loc_prev) = prev;
824 prev = next;
825 }
826 CX_LL_PTR(prev, ll->loc_next) = NULL;
827
828 // invoke the low level function
829 cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc;
830 cx_ll_insert_sorted_loc_data = ll->loc_data;
831 if (allow_duplicates) {
832 cx_linked_list_insert_sorted_chain(
833 &ll->begin,
834 &ll->end,
835 ll->loc_prev,
836 ll->loc_next,
837 chain,
838 cx_ll_insert_sorted_cmp_helper
839 );
840 list->collection.size += inserted;
841 } else {
842 void *duplicates = cx_linked_list_insert_unique_chain(
843 &ll->begin,
844 &ll->end,
845 ll->loc_prev,
846 ll->loc_next,
847 chain,
848 cx_ll_insert_sorted_cmp_helper
849 );
850 list->collection.size += inserted;
851 // free the nodes that did not make it into the list
852 while (duplicates != NULL) {
853 void *next = CX_LL_PTR(duplicates, ll->loc_next);
854 cxFree(list->collection.allocator, duplicates);
855 duplicates = next;
856 list->collection.size--;
857 }
858 }
859
860 return inserted;
676 } 861 }
677 862
678 static size_t cx_ll_insert_sorted( 863 static size_t cx_ll_insert_sorted(
679 struct cx_list_s *list, 864 struct cx_list_s *list,
680 const void *array, 865 const void *array,
681 size_t n 866 size_t n
682 ) { 867 ) {
683 // special case 868 return cx_ll_insert_sorted_impl(list, array, n, true);
684 if (n == 0) return 0; 869 }
685 870
686 // create a new chain of nodes 871 static size_t cx_ll_insert_unique(
687 cx_linked_list_node *chain = cx_ll_malloc_node(list); 872 struct cx_list_s *list,
688 if (chain == NULL) return 0; 873 const void *array,
689 874 size_t n
690 memcpy(chain->payload, array, list->collection.elem_size); 875 ) {
691 chain->prev = NULL; 876 return cx_ll_insert_sorted_impl(list, array, n, false);
692 chain->next = NULL;
693
694 // add all elements from the array to that chain
695 cx_linked_list_node *prev = chain;
696 const char *src = array;
697 size_t inserted = 1;
698 for (; inserted < n; inserted++) {
699 cx_linked_list_node *next = cx_ll_malloc_node(list);
700 if (next == NULL) break;
701 src += list->collection.elem_size;
702 memcpy(next->payload, src, list->collection.elem_size);
703 prev->next = next;
704 next->prev = prev;
705 prev = next;
706 }
707 prev->next = NULL;
708
709 // invoke the low level function
710 cx_linked_list *ll = (cx_linked_list *) list;
711 cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc;
712 cx_linked_list_insert_sorted_chain(
713 (void **) &ll->begin,
714 (void **) &ll->end,
715 CX_LL_LOC_PREV,
716 CX_LL_LOC_NEXT,
717 chain,
718 cx_ll_insert_sorted_cmp_helper
719 );
720
721 // adjust the list metadata
722 list->collection.size += inserted;
723
724 return inserted;
725 } 877 }
726 878
727 static size_t cx_ll_remove( 879 static size_t cx_ll_remove(
728 struct cx_list_s *list, 880 struct cx_list_s *list,
729 size_t index, 881 size_t index,
730 size_t num, 882 size_t num,
731 void *targetbuf 883 void *targetbuf
732 ) { 884 ) {
733 cx_linked_list *ll = (cx_linked_list *) list; 885 cx_linked_list *ll = (cx_linked_list *) list;
734 cx_linked_list_node *node = cx_ll_node_at(ll, index); 886 void *node = cx_ll_node_at(ll, index);
735 887
736 // out-of-bounds check 888 // out-of-bounds check
737 if (node == NULL) return 0; 889 if (node == NULL) return 0;
738 890
739 // remove 891 // remove
740 size_t removed = cx_linked_list_remove_chain( 892 size_t removed = cx_linked_list_remove_chain(
741 (void **) &ll->begin, 893 (void **) &ll->begin,
742 (void **) &ll->end, 894 (void **) &ll->end,
743 CX_LL_LOC_PREV, 895 ll->loc_prev,
744 CX_LL_LOC_NEXT, 896 ll->loc_next,
745 node, 897 node,
746 num 898 num
747 ); 899 );
748 900
749 // adjust size 901 // adjust size
750 list->collection.size -= removed; 902 list->collection.size -= removed;
751 903
752 // copy or destroy the removed chain 904 // copy or destroy the removed chain
753 if (targetbuf == NULL) { 905 if (targetbuf == NULL) {
754 cx_linked_list_node *n = node; 906 char *n = node;
755 for (size_t i = 0; i < removed; i++) { 907 for (size_t i = 0; i < removed; i++) {
756 // element destruction 908 // element destruction
757 cx_invoke_destructor(list, n->payload); 909 cx_invoke_destructor(list, n + ll->loc_data);
758 910
759 // free the node and advance 911 // free the node and advance
760 void *next = n->next; 912 void *next = CX_LL_PTR(n, ll->loc_next);
761 cxFree(list->collection.allocator, n); 913 cxFree(list->collection.allocator, n);
762 n = next; 914 n = next;
763 } 915 }
764 } else { 916 } else {
765 char *dest = targetbuf; 917 char *dest = targetbuf;
766 cx_linked_list_node *n = node; 918 char *n = node;
767 for (size_t i = 0; i < removed; i++) { 919 for (size_t i = 0; i < removed; i++) {
768 // copy payload 920 // copy payload
769 memcpy(dest, n->payload, list->collection.elem_size); 921 memcpy(dest, n + ll->loc_data, list->collection.elem_size);
770 922
771 // advance target buffer 923 // advance target buffer
772 dest += list->collection.elem_size; 924 dest += list->collection.elem_size;
773 925
774 // free the node and advance 926 // free the node and advance
775 void *next = n->next; 927 void *next = CX_LL_PTR(n, ll->loc_next);
776 cxFree(list->collection.allocator, n); 928 cxFree(list->collection.allocator, n);
777 n = next; 929 n = next;
778 } 930 }
779 } 931 }
780 932
783 935
784 static void cx_ll_clear(struct cx_list_s *list) { 936 static void cx_ll_clear(struct cx_list_s *list) {
785 if (list->collection.size == 0) return; 937 if (list->collection.size == 0) return;
786 938
787 cx_linked_list *ll = (cx_linked_list *) list; 939 cx_linked_list *ll = (cx_linked_list *) list;
788 cx_linked_list_node *node = ll->begin; 940 char *node = ll->begin;
789 while (node != NULL) { 941 while (node != NULL) {
790 cx_invoke_destructor(list, node->payload); 942 cx_invoke_destructor(list, node + ll->loc_data);
791 cx_linked_list_node *next = node->next; 943 void *next = CX_LL_PTR(node, ll->loc_next);
792 cxFree(list->collection.allocator, node); 944 cxFree(list->collection.allocator, node);
793 node = next; 945 node = next;
794 } 946 }
795 ll->begin = ll->end = NULL; 947 ll->begin = ll->end = NULL;
796 list->collection.size = 0; 948 list->collection.size = 0;
813 right = j; 965 right = j;
814 } else { 966 } else {
815 left = j; 967 left = j;
816 right = i; 968 right = i;
817 } 969 }
818 cx_linked_list_node *nleft = NULL, *nright = NULL; 970 void *nleft = NULL, *nright = NULL;
819 if (left < mid && right < mid) { 971 if (left < mid && right < mid) {
820 // case 1: both items left from mid 972 // case 1: both items left from mid
821 nleft = cx_ll_node_at(ll, left); 973 nleft = cx_ll_node_at(ll, left);
822 assert(nleft != NULL); 974 assert(nleft != NULL);
823 nright = nleft; 975 nright = nleft;
824 for (size_t c = left; c < right; c++) { 976 for (size_t c = left; c < right; c++) {
825 nright = nright->next; 977 nright = CX_LL_PTR(nright, ll->loc_next);
826 } 978 }
827 } else if (left >= mid && right >= mid) { 979 } else if (left >= mid && right >= mid) {
828 // case 2: both items right from mid 980 // case 2: both items right from mid
829 nright = cx_ll_node_at(ll, right); 981 nright = cx_ll_node_at(ll, right);
830 assert(nright != NULL); 982 assert(nright != NULL);
831 nleft = nright; 983 nleft = nright;
832 for (size_t c = right; c > left; c--) { 984 for (size_t c = right; c > left; c--) {
833 nleft = nleft->prev; 985 nleft = CX_LL_PTR(nleft, ll->loc_prev);
834 } 986 }
835 } else { 987 } else {
836 // case 3: one item left, one item right 988 // case 3: one item left, one item right
837 989
838 // chose the closest to begin / end 990 // chose the closest to begin / end
854 if (right - left <= diff2boundary) { 1006 if (right - left <= diff2boundary) {
855 // search other element starting from already found element 1007 // search other element starting from already found element
856 if (closest == left) { 1008 if (closest == left) {
857 nright = nleft; 1009 nright = nleft;
858 for (size_t c = left; c < right; c++) { 1010 for (size_t c = left; c < right; c++) {
859 nright = nright->next; 1011 nright = CX_LL_PTR(nright, ll->loc_next);
860 } 1012 }
861 } else { 1013 } else {
862 nleft = nright; 1014 nleft = nright;
863 for (size_t c = right; c > left; c--) { 1015 for (size_t c = right; c > left; c--) {
864 nleft = nleft->prev; 1016 nleft = CX_LL_PTR(nleft, ll->loc_prev);
865 } 1017 }
866 } 1018 }
867 } else { 1019 } else {
868 // search other element starting at the boundary 1020 // search other element starting at the boundary
869 if (closest == left) { 1021 if (closest == left) {
872 nleft = cx_ll_node_at(ll, other); 1024 nleft = cx_ll_node_at(ll, other);
873 } 1025 }
874 } 1026 }
875 } 1027 }
876 1028
877 cx_linked_list_node *prev = nleft->prev; 1029 void *prev = CX_LL_PTR(nleft, ll->loc_prev);
878 cx_linked_list_node *next = nright->next; 1030 void *next = CX_LL_PTR(nright, ll->loc_next);
879 cx_linked_list_node *midstart = nleft->next; 1031 void *midstart = CX_LL_PTR(nleft, ll->loc_next);
880 cx_linked_list_node *midend = nright->prev; 1032 void *midend = CX_LL_PTR(nright, ll->loc_prev);
881 1033
882 if (prev == NULL) { 1034 if (prev == NULL) {
883 ll->begin = nright; 1035 ll->begin = nright;
884 } else { 1036 } else {
885 prev->next = nright; 1037 CX_LL_PTR(prev, ll->loc_next) = nright;
886 } 1038 }
887 nright->prev = prev; 1039 CX_LL_PTR(nright, ll->loc_prev) = prev;
888 if (midstart == nright) { 1040 if (midstart == nright) {
889 // special case: both nodes are adjacent 1041 // special case: both nodes are adjacent
890 nright->next = nleft; 1042 CX_LL_PTR(nright, ll->loc_next) = nleft;
891 nleft->prev = nright; 1043 CX_LL_PTR(nleft, ll->loc_prev) = nright;
892 } else { 1044 } else {
893 // likely case: a chain is between the two nodes 1045 // likely case: a chain is between the two nodes
894 nright->next = midstart; 1046 CX_LL_PTR(nright, ll->loc_next) = midstart;
895 midstart->prev = nright; 1047 CX_LL_PTR(midstart, ll->loc_prev) = nright;
896 midend->next = nleft; 1048 CX_LL_PTR(midend, ll->loc_next) = nleft;
897 nleft->prev = midend; 1049 CX_LL_PTR(nleft, ll->loc_prev) = midend;
898 } 1050 }
899 nleft->next = next; 1051 CX_LL_PTR(nleft, ll->loc_next) = next;
900 if (next == NULL) { 1052 if (next == NULL) {
901 ll->end = nleft; 1053 ll->end = nleft;
902 } else { 1054 } else {
903 next->prev = nleft; 1055 CX_LL_PTR(next, ll->loc_prev) = nleft;
904 } 1056 }
905 1057
906 return 0; 1058 return 0;
907 } 1059 }
908 1060
909 static void *cx_ll_at( 1061 static void *cx_ll_at(
910 const struct cx_list_s *list, 1062 const struct cx_list_s *list,
911 size_t index 1063 size_t index
912 ) { 1064 ) {
913 cx_linked_list *ll = (cx_linked_list *) list; 1065 cx_linked_list *ll = (cx_linked_list *) list;
914 cx_linked_list_node *node = cx_ll_node_at(ll, index); 1066 char *node = cx_ll_node_at(ll, index);
915 return node == NULL ? NULL : node->payload; 1067 return node == NULL ? NULL : node + ll->loc_data;
916 } 1068 }
917 1069
918 static size_t cx_ll_find_remove( 1070 static size_t cx_ll_find_remove(
919 struct cx_list_s *list, 1071 struct cx_list_s *list,
920 const void *elem, 1072 const void *elem,
921 bool remove 1073 bool remove
922 ) { 1074 ) {
1075 if (list->collection.size == 0) return 0;
1076
923 size_t index; 1077 size_t index;
924 cx_linked_list *ll = ((cx_linked_list *) list); 1078 cx_linked_list *ll = (cx_linked_list *) list;
925 cx_linked_list_node *node = cx_linked_list_find( 1079 char *node = cx_linked_list_find(
926 ll->begin, 1080 ll->begin,
927 CX_LL_LOC_NEXT, CX_LL_LOC_DATA, 1081 ll->loc_next, ll->loc_data,
928 list->collection.cmpfunc, elem, 1082 list->collection.cmpfunc, elem,
929 &index 1083 &index
930 ); 1084 );
931 if (node == NULL) { 1085 if (node == NULL) {
932 return list->collection.size; 1086 return list->collection.size;
933 } 1087 }
934 if (remove) { 1088 if (remove) {
935 cx_invoke_destructor(list, node->payload); 1089 cx_invoke_destructor(list, node + ll->loc_data);
936 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, 1090 cx_linked_list_remove(&ll->begin, &ll->end,
937 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); 1091 ll->loc_prev, ll->loc_next, node);
938 list->collection.size--; 1092 list->collection.size--;
939 cxFree(list->collection.allocator, node); 1093 cxFree(list->collection.allocator, node);
940 } 1094 }
941 return index; 1095 return index;
942 } 1096 }
943 1097
944 static void cx_ll_sort(struct cx_list_s *list) { 1098 static void cx_ll_sort(struct cx_list_s *list) {
945 cx_linked_list *ll = (cx_linked_list *) list; 1099 cx_linked_list *ll = (cx_linked_list *) list;
946 cx_linked_list_sort((void **) &ll->begin, (void **) &ll->end, 1100 cx_linked_list_sort(&ll->begin, &ll->end,
947 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, CX_LL_LOC_DATA, 1101 ll->loc_prev, ll->loc_next, ll->loc_data,
948 list->collection.cmpfunc); 1102 list->collection.cmpfunc);
949 } 1103 }
950 1104
951 static void cx_ll_reverse(struct cx_list_s *list) { 1105 static void cx_ll_reverse(struct cx_list_s *list) {
952 cx_linked_list *ll = (cx_linked_list *) list; 1106 cx_linked_list *ll = (cx_linked_list *) list;
953 cx_linked_list_reverse((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT); 1107 cx_linked_list_reverse(&ll->begin, &ll->end, ll->loc_prev, ll->loc_next);
954 } 1108 }
955 1109
956 static int cx_ll_compare( 1110 static int cx_ll_compare(
957 const struct cx_list_s *list, 1111 const struct cx_list_s *list,
958 const struct cx_list_s *other 1112 const struct cx_list_s *other
959 ) { 1113 ) {
960 cx_linked_list *left = (cx_linked_list *) list; 1114 cx_linked_list *left = (cx_linked_list *) list;
961 cx_linked_list *right = (cx_linked_list *) other; 1115 cx_linked_list *right = (cx_linked_list *) other;
1116 assert(left->loc_next == right->loc_next);
1117 assert(left->loc_data == right->loc_data);
962 return cx_linked_list_compare(left->begin, right->begin, 1118 return cx_linked_list_compare(left->begin, right->begin,
963 CX_LL_LOC_NEXT, CX_LL_LOC_DATA, 1119 left->loc_next, left->loc_data,
964 list->collection.cmpfunc); 1120 list->collection.cmpfunc);
965 } 1121 }
966 1122
967 static bool cx_ll_iter_valid(const void *it) { 1123 static bool cx_ll_iter_valid(const void *it) {
968 const struct cx_iterator_s *iter = it; 1124 const struct cx_iterator_s *iter = it;
969 return iter->elem_handle != NULL; 1125 return iter->elem_handle != NULL;
970 } 1126 }
971 1127
972 static void cx_ll_iter_next(void *it) { 1128 static void cx_ll_iter_next(void *it) {
973 struct cx_iterator_s *iter = it; 1129 struct cx_iterator_s *iter = it;
1130 cx_linked_list *ll = iter->src_handle;
974 if (iter->base.remove) { 1131 if (iter->base.remove) {
975 iter->base.remove = false; 1132 iter->base.remove = false;
976 struct cx_list_s *list = iter->src_handle.m; 1133 struct cx_list_s *list = iter->src_handle;
977 cx_linked_list *ll = iter->src_handle.m; 1134 char *node = iter->elem_handle;
978 cx_linked_list_node *node = iter->elem_handle; 1135 iter->elem_handle = CX_LL_PTR(node, ll->loc_next);
979 iter->elem_handle = node->next; 1136 cx_invoke_destructor(list, node + ll->loc_data);
980 cx_invoke_destructor(list, node->payload); 1137 cx_linked_list_remove(&ll->begin, &ll->end,
981 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, 1138 ll->loc_prev, ll->loc_next, node);
982 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
983 list->collection.size--; 1139 list->collection.size--;
1140 iter->elem_count--;
984 cxFree(list->collection.allocator, node); 1141 cxFree(list->collection.allocator, node);
985 } else { 1142 } else {
986 iter->index++; 1143 iter->index++;
987 cx_linked_list_node *node = iter->elem_handle; 1144 void *node = iter->elem_handle;
988 iter->elem_handle = node->next; 1145 iter->elem_handle = CX_LL_PTR(node, ll->loc_next);
989 } 1146 }
990 } 1147 }
991 1148
992 static void cx_ll_iter_prev(void *it) { 1149 static void cx_ll_iter_prev(void *it) {
993 struct cx_iterator_s *iter = it; 1150 struct cx_iterator_s *iter = it;
1151 cx_linked_list *ll = iter->src_handle;
994 if (iter->base.remove) { 1152 if (iter->base.remove) {
995 iter->base.remove = false; 1153 iter->base.remove = false;
996 struct cx_list_s *list = iter->src_handle.m; 1154 struct cx_list_s *list = iter->src_handle;
997 cx_linked_list *ll = iter->src_handle.m; 1155 char *node = iter->elem_handle;
998 cx_linked_list_node *node = iter->elem_handle; 1156 iter->elem_handle = CX_LL_PTR(node, ll->loc_prev);
999 iter->elem_handle = node->prev;
1000 iter->index--; 1157 iter->index--;
1001 cx_invoke_destructor(list, node->payload); 1158 cx_invoke_destructor(list, node + ll->loc_data);
1002 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, 1159 cx_linked_list_remove(&ll->begin, &ll->end,
1003 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); 1160 ll->loc_prev, ll->loc_next, node);
1004 list->collection.size--; 1161 list->collection.size--;
1162 iter->elem_count--;
1005 cxFree(list->collection.allocator, node); 1163 cxFree(list->collection.allocator, node);
1006 } else { 1164 } else {
1007 iter->index--; 1165 iter->index--;
1008 cx_linked_list_node *node = iter->elem_handle; 1166 char *node = iter->elem_handle;
1009 iter->elem_handle = node->prev; 1167 iter->elem_handle = CX_LL_PTR(node, ll->loc_prev);
1010 } 1168 }
1011 } 1169 }
1012 1170
1013 static void *cx_ll_iter_current(const void *it) { 1171 static void *cx_ll_iter_current(const void *it) {
1014 const struct cx_iterator_s *iter = it; 1172 const struct cx_iterator_s *iter = it;
1015 cx_linked_list_node *node = iter->elem_handle; 1173 const cx_linked_list *ll = iter->src_handle;
1016 return node->payload; 1174 char *node = iter->elem_handle;
1175 return node + ll->loc_data;
1017 } 1176 }
1018 1177
1019 static CxIterator cx_ll_iterator( 1178 static CxIterator cx_ll_iterator(
1020 const struct cx_list_s *list, 1179 const struct cx_list_s *list,
1021 size_t index, 1180 size_t index,
1022 bool backwards 1181 bool backwards
1023 ) { 1182 ) {
1024 CxIterator iter; 1183 CxIterator iter;
1025 iter.index = index; 1184 iter.index = index;
1026 iter.src_handle.c = list; 1185 iter.src_handle = (void*)list;
1027 iter.elem_handle = cx_ll_node_at((const cx_linked_list *) list, index); 1186 iter.elem_handle = cx_ll_node_at((const cx_linked_list *) list, index);
1028 iter.elem_size = list->collection.elem_size; 1187 iter.elem_size = list->collection.elem_size;
1029 iter.elem_count = list->collection.size; 1188 iter.elem_count = list->collection.size;
1030 iter.base.valid = cx_ll_iter_valid; 1189 iter.base.valid = cx_ll_iter_valid;
1031 iter.base.current = cx_ll_iter_current; 1190 iter.base.current = cx_ll_iter_current;
1032 iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next; 1191 iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next;
1033 iter.base.mutating = false; 1192 iter.base.allow_remove = true;
1034 iter.base.remove = false; 1193 iter.base.remove = false;
1035 return iter; 1194 return iter;
1036 } 1195 }
1037 1196
1038 static int cx_ll_insert_iter( 1197 static int cx_ll_insert_iter(
1039 CxIterator *iter, 1198 CxIterator *iter,
1040 const void *elem, 1199 const void *elem,
1041 int prepend 1200 int prepend
1042 ) { 1201 ) {
1043 struct cx_list_s *list = iter->src_handle.m; 1202 struct cx_list_s *list = iter->src_handle;
1044 cx_linked_list_node *node = iter->elem_handle; 1203 cx_linked_list *ll = iter->src_handle;
1204 void *node = iter->elem_handle;
1045 if (node != NULL) { 1205 if (node != NULL) {
1046 assert(prepend >= 0 && prepend <= 1); 1206 assert(prepend >= 0 && prepend <= 1);
1047 cx_linked_list_node *choice[2] = {node, node->prev}; 1207 void *choice[2] = {node, CX_LL_PTR(node, ll->loc_prev)};
1048 int result = cx_ll_insert_at(list, choice[prepend], elem); 1208 int result = cx_ll_insert_at(list, choice[prepend], elem);
1049 if (result == 0) { 1209 if (result == 0) {
1050 iter->elem_count++; 1210 iter->elem_count++;
1051 if (prepend) { 1211 if (prepend) {
1052 iter->index++; 1212 iter->index++;
1053 } 1213 }
1054 } 1214 }
1055 return result; 1215 return result;
1056 } else { 1216 } else {
1057 int result = cx_ll_insert_element(list, list->collection.size, elem); 1217 if (cx_ll_insert_element(list, list->collection.size, elem) == NULL) {
1058 if (result == 0) { 1218 return 1;
1059 iter->elem_count++; 1219 }
1060 iter->index = list->collection.size; 1220 iter->elem_count++;
1061 } 1221 iter->index = list->collection.size;
1062 return result; 1222 return 0;
1063 } 1223 }
1064 } 1224 }
1065 1225
1066 static void cx_ll_destructor(CxList *list) { 1226 static void cx_ll_destructor(CxList *list) {
1067 cx_linked_list *ll = (cx_linked_list *) list; 1227 cx_linked_list *ll = (cx_linked_list *) list;
1068 1228
1069 cx_linked_list_node *node = ll->begin; 1229 char *node = ll->begin;
1070 while (node) { 1230 while (node) {
1071 cx_invoke_destructor(list, node->payload); 1231 cx_invoke_destructor(list, node + ll->loc_data);
1072 void *next = node->next; 1232 void *next = CX_LL_PTR(node, ll->loc_next);
1073 cxFree(list->collection.allocator, node); 1233 cxFree(list->collection.allocator, node);
1074 node = next; 1234 node = next;
1075 } 1235 }
1076 1236
1077 cxFree(list->collection.allocator, list); 1237 cxFree(list->collection.allocator, list);
1080 static cx_list_class cx_linked_list_class = { 1240 static cx_list_class cx_linked_list_class = {
1081 cx_ll_destructor, 1241 cx_ll_destructor,
1082 cx_ll_insert_element, 1242 cx_ll_insert_element,
1083 cx_ll_insert_array, 1243 cx_ll_insert_array,
1084 cx_ll_insert_sorted, 1244 cx_ll_insert_sorted,
1245 cx_ll_insert_unique,
1085 cx_ll_insert_iter, 1246 cx_ll_insert_iter,
1086 cx_ll_remove, 1247 cx_ll_remove,
1087 cx_ll_clear, 1248 cx_ll_clear,
1088 cx_ll_swap, 1249 cx_ll_swap,
1089 cx_ll_at, 1250 cx_ll_at,
1103 allocator = cxDefaultAllocator; 1264 allocator = cxDefaultAllocator;
1104 } 1265 }
1105 1266
1106 cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list)); 1267 cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list));
1107 if (list == NULL) return NULL; 1268 if (list == NULL) return NULL;
1269 list->extra_data_len = 0;
1270 list->loc_prev = 0;
1271 list->loc_next = sizeof(void*);
1272 list->loc_data = sizeof(void*)*2;
1108 cx_list_init((CxList*)list, &cx_linked_list_class, 1273 cx_list_init((CxList*)list, &cx_linked_list_class,
1109 allocator, comparator, elem_size); 1274 allocator, comparator, elem_size);
1110 1275
1111 return (CxList *) list; 1276 return (CxList *) list;
1112 } 1277 }

mercurial