src/ucx/linked_list.c

changeset 621
956c03c25edd
parent 582
82b60a8dd55c
child 645
0c85c4cd0dd8
equal deleted inserted replaced
620:a202cb1ee175 621:956c03c25edd
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
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;
616 if (elem != NULL) { 713 if (elem != NULL) {
617 memcpy(new_node->payload, elem, list->collection.elem_size); 714 memcpy((char*)new_node + ll->loc_data, elem, list->collection.elem_size);
618 } 715 }
619 716
620 // insert 717 // insert
621 cx_linked_list *ll = (cx_linked_list *) list;
622 cx_linked_list_insert_chain( 718 cx_linked_list_insert_chain(
623 (void **) &ll->begin, (void **) &ll->end, 719 &ll->begin, &ll->end,
624 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, 720 ll->loc_prev, ll->loc_next,
625 node, new_node, new_node 721 node, new_node, new_node
626 ); 722 );
627 723
628 // increase the size and return 724 // increase the size and return
629 list->collection.size++; 725 list->collection.size++;
638 ) { 734 ) {
639 // out-of bounds and corner case check 735 // out-of bounds and corner case check
640 if (index > list->collection.size || n == 0) return 0; 736 if (index > list->collection.size || n == 0) return 0;
641 737
642 // find position efficiently 738 // find position efficiently
643 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);
644 740
645 // perform first insert 741 // perform first insert
646 if (0 != cx_ll_insert_at(list, node, array)) return 1; 742 if (0 != cx_ll_insert_at(list, node, array)) return 1;
647 743
648 // is there more? 744 // is there more?
649 if (n == 1) return 1; 745 if (n == 1) return 1;
650 746
651 // we now know exactly where we are 747 // we now know exactly where we are
652 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);
653 750
654 // 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
655 const char *source = array; 752 const char *source = array;
656 for (size_t i = 1; i < n; i++) { 753 for (size_t i = 1; i < n; i++) {
657 source += list->collection.elem_size; 754 if (source != NULL) {
755 source += list->collection.elem_size;
756 }
658 if (0 != cx_ll_insert_at(list, node, source)) return i; 757 if (0 != cx_ll_insert_at(list, node, source)) return i;
659 node = node->next; 758 node = CX_LL_PTR(node, ll->loc_next);
660 } 759 }
661 return n; 760 return n;
662 } 761 }
663 762
664 static void *cx_ll_insert_element( 763 static void *cx_ll_insert_element(
668 ) { 767 ) {
669 // out-of-bounds check 768 // out-of-bounds check
670 if (index > list->collection.size) return NULL; 769 if (index > list->collection.size) return NULL;
671 770
672 // find position efficiently 771 // find position efficiently
673 cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); 772 void *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
674 773
675 // perform first insert 774 // perform first insert
676 if (cx_ll_insert_at(list, node, element)) return NULL; 775 if (cx_ll_insert_at(list, node, element)) return NULL;
677 776
678 // return a pointer to the data of the inserted node 777 // return a pointer to the data of the inserted node
778 cx_linked_list *ll = (cx_linked_list *) list;
679 if (node == NULL) { 779 if (node == NULL) {
680 return ((cx_linked_list *) list)->begin->payload; 780 return (char*)(ll->begin) + ll->loc_data;
681 } else { 781 } else {
682 return node->next->payload; 782 char *next = CX_LL_PTR(node, ll->loc_next);
783 return next + ll->loc_data;
683 } 784 }
684 } 785 }
685 786
686 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;
687 789
688 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) {
689 const cx_linked_list_node *left = l; 791 const char *left = (const char*)l + cx_ll_insert_sorted_loc_data;
690 const cx_linked_list_node *right = r; 792 const char *right = (const char*)r + cx_ll_insert_sorted_loc_data;
691 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;
692 } 861 }
693 862
694 static size_t cx_ll_insert_sorted( 863 static size_t cx_ll_insert_sorted(
695 struct cx_list_s *list, 864 struct cx_list_s *list,
696 const void *array, 865 const void *array,
697 size_t n 866 size_t n
698 ) { 867 ) {
699 // special case 868 return cx_ll_insert_sorted_impl(list, array, n, true);
700 if (n == 0) return 0; 869 }
701 870
702 // create a new chain of nodes 871 static size_t cx_ll_insert_unique(
703 cx_linked_list_node *chain = cx_ll_malloc_node(list); 872 struct cx_list_s *list,
704 if (chain == NULL) return 0; 873 const void *array,
705 874 size_t n
706 memcpy(chain->payload, array, list->collection.elem_size); 875 ) {
707 chain->prev = NULL; 876 return cx_ll_insert_sorted_impl(list, array, n, false);
708 chain->next = NULL;
709
710 // add all elements from the array to that chain
711 cx_linked_list_node *prev = chain;
712 const char *src = array;
713 size_t inserted = 1;
714 for (; inserted < n; inserted++) {
715 cx_linked_list_node *next = cx_ll_malloc_node(list);
716 if (next == NULL) break;
717 src += list->collection.elem_size;
718 memcpy(next->payload, src, list->collection.elem_size);
719 prev->next = next;
720 next->prev = prev;
721 prev = next;
722 }
723 prev->next = NULL;
724
725 // invoke the low level function
726 cx_linked_list *ll = (cx_linked_list *) list;
727 cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc;
728 cx_linked_list_insert_sorted_chain(
729 (void **) &ll->begin,
730 (void **) &ll->end,
731 CX_LL_LOC_PREV,
732 CX_LL_LOC_NEXT,
733 chain,
734 cx_ll_insert_sorted_cmp_helper
735 );
736
737 // adjust the list metadata
738 list->collection.size += inserted;
739
740 return inserted;
741 } 877 }
742 878
743 static size_t cx_ll_remove( 879 static size_t cx_ll_remove(
744 struct cx_list_s *list, 880 struct cx_list_s *list,
745 size_t index, 881 size_t index,
746 size_t num, 882 size_t num,
747 void *targetbuf 883 void *targetbuf
748 ) { 884 ) {
749 cx_linked_list *ll = (cx_linked_list *) list; 885 cx_linked_list *ll = (cx_linked_list *) list;
750 cx_linked_list_node *node = cx_ll_node_at(ll, index); 886 void *node = cx_ll_node_at(ll, index);
751 887
752 // out-of-bounds check 888 // out-of-bounds check
753 if (node == NULL) return 0; 889 if (node == NULL) return 0;
754 890
755 // remove 891 // remove
756 size_t removed = cx_linked_list_remove_chain( 892 size_t removed = cx_linked_list_remove_chain(
757 (void **) &ll->begin, 893 (void **) &ll->begin,
758 (void **) &ll->end, 894 (void **) &ll->end,
759 CX_LL_LOC_PREV, 895 ll->loc_prev,
760 CX_LL_LOC_NEXT, 896 ll->loc_next,
761 node, 897 node,
762 num 898 num
763 ); 899 );
764 900
765 // adjust size 901 // adjust size
766 list->collection.size -= removed; 902 list->collection.size -= removed;
767 903
768 // copy or destroy the removed chain 904 // copy or destroy the removed chain
769 if (targetbuf == NULL) { 905 if (targetbuf == NULL) {
770 cx_linked_list_node *n = node; 906 char *n = node;
771 for (size_t i = 0; i < removed; i++) { 907 for (size_t i = 0; i < removed; i++) {
772 // element destruction 908 // element destruction
773 cx_invoke_destructor(list, n->payload); 909 cx_invoke_destructor(list, n + ll->loc_data);
774 910
775 // free the node and advance 911 // free the node and advance
776 void *next = n->next; 912 void *next = CX_LL_PTR(n, ll->loc_next);
777 cxFree(list->collection.allocator, n); 913 cxFree(list->collection.allocator, n);
778 n = next; 914 n = next;
779 } 915 }
780 } else { 916 } else {
781 char *dest = targetbuf; 917 char *dest = targetbuf;
782 cx_linked_list_node *n = node; 918 char *n = node;
783 for (size_t i = 0; i < removed; i++) { 919 for (size_t i = 0; i < removed; i++) {
784 // copy payload 920 // copy payload
785 memcpy(dest, n->payload, list->collection.elem_size); 921 memcpy(dest, n + ll->loc_data, list->collection.elem_size);
786 922
787 // advance target buffer 923 // advance target buffer
788 dest += list->collection.elem_size; 924 dest += list->collection.elem_size;
789 925
790 // free the node and advance 926 // free the node and advance
791 void *next = n->next; 927 void *next = CX_LL_PTR(n, ll->loc_next);
792 cxFree(list->collection.allocator, n); 928 cxFree(list->collection.allocator, n);
793 n = next; 929 n = next;
794 } 930 }
795 } 931 }
796 932
799 935
800 static void cx_ll_clear(struct cx_list_s *list) { 936 static void cx_ll_clear(struct cx_list_s *list) {
801 if (list->collection.size == 0) return; 937 if (list->collection.size == 0) return;
802 938
803 cx_linked_list *ll = (cx_linked_list *) list; 939 cx_linked_list *ll = (cx_linked_list *) list;
804 cx_linked_list_node *node = ll->begin; 940 char *node = ll->begin;
805 while (node != NULL) { 941 while (node != NULL) {
806 cx_invoke_destructor(list, node->payload); 942 cx_invoke_destructor(list, node + ll->loc_data);
807 cx_linked_list_node *next = node->next; 943 void *next = CX_LL_PTR(node, ll->loc_next);
808 cxFree(list->collection.allocator, node); 944 cxFree(list->collection.allocator, node);
809 node = next; 945 node = next;
810 } 946 }
811 ll->begin = ll->end = NULL; 947 ll->begin = ll->end = NULL;
812 list->collection.size = 0; 948 list->collection.size = 0;
829 right = j; 965 right = j;
830 } else { 966 } else {
831 left = j; 967 left = j;
832 right = i; 968 right = i;
833 } 969 }
834 cx_linked_list_node *nleft = NULL, *nright = NULL; 970 void *nleft = NULL, *nright = NULL;
835 if (left < mid && right < mid) { 971 if (left < mid && right < mid) {
836 // case 1: both items left from mid 972 // case 1: both items left from mid
837 nleft = cx_ll_node_at(ll, left); 973 nleft = cx_ll_node_at(ll, left);
838 assert(nleft != NULL); 974 assert(nleft != NULL);
839 nright = nleft; 975 nright = nleft;
840 for (size_t c = left; c < right; c++) { 976 for (size_t c = left; c < right; c++) {
841 nright = nright->next; 977 nright = CX_LL_PTR(nright, ll->loc_next);
842 } 978 }
843 } else if (left >= mid && right >= mid) { 979 } else if (left >= mid && right >= mid) {
844 // case 2: both items right from mid 980 // case 2: both items right from mid
845 nright = cx_ll_node_at(ll, right); 981 nright = cx_ll_node_at(ll, right);
846 assert(nright != NULL); 982 assert(nright != NULL);
847 nleft = nright; 983 nleft = nright;
848 for (size_t c = right; c > left; c--) { 984 for (size_t c = right; c > left; c--) {
849 nleft = nleft->prev; 985 nleft = CX_LL_PTR(nleft, ll->loc_prev);
850 } 986 }
851 } else { 987 } else {
852 // case 3: one item left, one item right 988 // case 3: one item left, one item right
853 989
854 // chose the closest to begin / end 990 // chose the closest to begin / end
870 if (right - left <= diff2boundary) { 1006 if (right - left <= diff2boundary) {
871 // search other element starting from already found element 1007 // search other element starting from already found element
872 if (closest == left) { 1008 if (closest == left) {
873 nright = nleft; 1009 nright = nleft;
874 for (size_t c = left; c < right; c++) { 1010 for (size_t c = left; c < right; c++) {
875 nright = nright->next; 1011 nright = CX_LL_PTR(nright, ll->loc_next);
876 } 1012 }
877 } else { 1013 } else {
878 nleft = nright; 1014 nleft = nright;
879 for (size_t c = right; c > left; c--) { 1015 for (size_t c = right; c > left; c--) {
880 nleft = nleft->prev; 1016 nleft = CX_LL_PTR(nleft, ll->loc_prev);
881 } 1017 }
882 } 1018 }
883 } else { 1019 } else {
884 // search other element starting at the boundary 1020 // search other element starting at the boundary
885 if (closest == left) { 1021 if (closest == left) {
888 nleft = cx_ll_node_at(ll, other); 1024 nleft = cx_ll_node_at(ll, other);
889 } 1025 }
890 } 1026 }
891 } 1027 }
892 1028
893 cx_linked_list_node *prev = nleft->prev; 1029 void *prev = CX_LL_PTR(nleft, ll->loc_prev);
894 cx_linked_list_node *next = nright->next; 1030 void *next = CX_LL_PTR(nright, ll->loc_next);
895 cx_linked_list_node *midstart = nleft->next; 1031 void *midstart = CX_LL_PTR(nleft, ll->loc_next);
896 cx_linked_list_node *midend = nright->prev; 1032 void *midend = CX_LL_PTR(nright, ll->loc_prev);
897 1033
898 if (prev == NULL) { 1034 if (prev == NULL) {
899 ll->begin = nright; 1035 ll->begin = nright;
900 } else { 1036 } else {
901 prev->next = nright; 1037 CX_LL_PTR(prev, ll->loc_next) = nright;
902 } 1038 }
903 nright->prev = prev; 1039 CX_LL_PTR(nright, ll->loc_prev) = prev;
904 if (midstart == nright) { 1040 if (midstart == nright) {
905 // special case: both nodes are adjacent 1041 // special case: both nodes are adjacent
906 nright->next = nleft; 1042 CX_LL_PTR(nright, ll->loc_next) = nleft;
907 nleft->prev = nright; 1043 CX_LL_PTR(nleft, ll->loc_prev) = nright;
908 } else { 1044 } else {
909 // likely case: a chain is between the two nodes 1045 // likely case: a chain is between the two nodes
910 nright->next = midstart; 1046 CX_LL_PTR(nright, ll->loc_next) = midstart;
911 midstart->prev = nright; 1047 CX_LL_PTR(midstart, ll->loc_prev) = nright;
912 midend->next = nleft; 1048 CX_LL_PTR(midend, ll->loc_next) = nleft;
913 nleft->prev = midend; 1049 CX_LL_PTR(nleft, ll->loc_prev) = midend;
914 } 1050 }
915 nleft->next = next; 1051 CX_LL_PTR(nleft, ll->loc_next) = next;
916 if (next == NULL) { 1052 if (next == NULL) {
917 ll->end = nleft; 1053 ll->end = nleft;
918 } else { 1054 } else {
919 next->prev = nleft; 1055 CX_LL_PTR(next, ll->loc_prev) = nleft;
920 } 1056 }
921 1057
922 return 0; 1058 return 0;
923 } 1059 }
924 1060
925 static void *cx_ll_at( 1061 static void *cx_ll_at(
926 const struct cx_list_s *list, 1062 const struct cx_list_s *list,
927 size_t index 1063 size_t index
928 ) { 1064 ) {
929 cx_linked_list *ll = (cx_linked_list *) list; 1065 cx_linked_list *ll = (cx_linked_list *) list;
930 cx_linked_list_node *node = cx_ll_node_at(ll, index); 1066 char *node = cx_ll_node_at(ll, index);
931 return node == NULL ? NULL : node->payload; 1067 return node == NULL ? NULL : node + ll->loc_data;
932 } 1068 }
933 1069
934 static size_t cx_ll_find_remove( 1070 static size_t cx_ll_find_remove(
935 struct cx_list_s *list, 1071 struct cx_list_s *list,
936 const void *elem, 1072 const void *elem,
937 bool remove 1073 bool remove
938 ) { 1074 ) {
939 if (list->collection.size == 0) return 0; 1075 if (list->collection.size == 0) return 0;
940 1076
941 size_t index; 1077 size_t index;
942 cx_linked_list *ll = ((cx_linked_list *) list); 1078 cx_linked_list *ll = (cx_linked_list *) list;
943 cx_linked_list_node *node = cx_linked_list_find( 1079 char *node = cx_linked_list_find(
944 ll->begin, 1080 ll->begin,
945 CX_LL_LOC_NEXT, CX_LL_LOC_DATA, 1081 ll->loc_next, ll->loc_data,
946 list->collection.cmpfunc, elem, 1082 list->collection.cmpfunc, elem,
947 &index 1083 &index
948 ); 1084 );
949 if (node == NULL) { 1085 if (node == NULL) {
950 return list->collection.size; 1086 return list->collection.size;
951 } 1087 }
952 if (remove) { 1088 if (remove) {
953 cx_invoke_destructor(list, node->payload); 1089 cx_invoke_destructor(list, node + ll->loc_data);
954 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, 1090 cx_linked_list_remove(&ll->begin, &ll->end,
955 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); 1091 ll->loc_prev, ll->loc_next, node);
956 list->collection.size--; 1092 list->collection.size--;
957 cxFree(list->collection.allocator, node); 1093 cxFree(list->collection.allocator, node);
958 } 1094 }
959 return index; 1095 return index;
960 } 1096 }
961 1097
962 static void cx_ll_sort(struct cx_list_s *list) { 1098 static void cx_ll_sort(struct cx_list_s *list) {
963 cx_linked_list *ll = (cx_linked_list *) list; 1099 cx_linked_list *ll = (cx_linked_list *) list;
964 cx_linked_list_sort((void **) &ll->begin, (void **) &ll->end, 1100 cx_linked_list_sort(&ll->begin, &ll->end,
965 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, CX_LL_LOC_DATA, 1101 ll->loc_prev, ll->loc_next, ll->loc_data,
966 list->collection.cmpfunc); 1102 list->collection.cmpfunc);
967 } 1103 }
968 1104
969 static void cx_ll_reverse(struct cx_list_s *list) { 1105 static void cx_ll_reverse(struct cx_list_s *list) {
970 cx_linked_list *ll = (cx_linked_list *) list; 1106 cx_linked_list *ll = (cx_linked_list *) list;
971 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);
972 } 1108 }
973 1109
974 static int cx_ll_compare( 1110 static int cx_ll_compare(
975 const struct cx_list_s *list, 1111 const struct cx_list_s *list,
976 const struct cx_list_s *other 1112 const struct cx_list_s *other
977 ) { 1113 ) {
978 cx_linked_list *left = (cx_linked_list *) list; 1114 cx_linked_list *left = (cx_linked_list *) list;
979 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);
980 return cx_linked_list_compare(left->begin, right->begin, 1118 return cx_linked_list_compare(left->begin, right->begin,
981 CX_LL_LOC_NEXT, CX_LL_LOC_DATA, 1119 left->loc_next, left->loc_data,
982 list->collection.cmpfunc); 1120 list->collection.cmpfunc);
983 } 1121 }
984 1122
985 static bool cx_ll_iter_valid(const void *it) { 1123 static bool cx_ll_iter_valid(const void *it) {
986 const struct cx_iterator_s *iter = it; 1124 const struct cx_iterator_s *iter = it;
987 return iter->elem_handle != NULL; 1125 return iter->elem_handle != NULL;
988 } 1126 }
989 1127
990 static void cx_ll_iter_next(void *it) { 1128 static void cx_ll_iter_next(void *it) {
991 struct cx_iterator_s *iter = it; 1129 struct cx_iterator_s *iter = it;
1130 cx_linked_list *ll = iter->src_handle;
992 if (iter->base.remove) { 1131 if (iter->base.remove) {
993 iter->base.remove = false; 1132 iter->base.remove = false;
994 struct cx_list_s *list = iter->src_handle.m; 1133 struct cx_list_s *list = iter->src_handle;
995 cx_linked_list *ll = iter->src_handle.m; 1134 char *node = iter->elem_handle;
996 cx_linked_list_node *node = iter->elem_handle; 1135 iter->elem_handle = CX_LL_PTR(node, ll->loc_next);
997 iter->elem_handle = node->next; 1136 cx_invoke_destructor(list, node + ll->loc_data);
998 cx_invoke_destructor(list, node->payload); 1137 cx_linked_list_remove(&ll->begin, &ll->end,
999 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, 1138 ll->loc_prev, ll->loc_next, node);
1000 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
1001 list->collection.size--; 1139 list->collection.size--;
1140 iter->elem_count--;
1002 cxFree(list->collection.allocator, node); 1141 cxFree(list->collection.allocator, node);
1003 } else { 1142 } else {
1004 iter->index++; 1143 iter->index++;
1005 cx_linked_list_node *node = iter->elem_handle; 1144 void *node = iter->elem_handle;
1006 iter->elem_handle = node->next; 1145 iter->elem_handle = CX_LL_PTR(node, ll->loc_next);
1007 } 1146 }
1008 } 1147 }
1009 1148
1010 static void cx_ll_iter_prev(void *it) { 1149 static void cx_ll_iter_prev(void *it) {
1011 struct cx_iterator_s *iter = it; 1150 struct cx_iterator_s *iter = it;
1151 cx_linked_list *ll = iter->src_handle;
1012 if (iter->base.remove) { 1152 if (iter->base.remove) {
1013 iter->base.remove = false; 1153 iter->base.remove = false;
1014 struct cx_list_s *list = iter->src_handle.m; 1154 struct cx_list_s *list = iter->src_handle;
1015 cx_linked_list *ll = iter->src_handle.m; 1155 char *node = iter->elem_handle;
1016 cx_linked_list_node *node = iter->elem_handle; 1156 iter->elem_handle = CX_LL_PTR(node, ll->loc_prev);
1017 iter->elem_handle = node->prev;
1018 iter->index--; 1157 iter->index--;
1019 cx_invoke_destructor(list, node->payload); 1158 cx_invoke_destructor(list, node + ll->loc_data);
1020 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, 1159 cx_linked_list_remove(&ll->begin, &ll->end,
1021 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); 1160 ll->loc_prev, ll->loc_next, node);
1022 list->collection.size--; 1161 list->collection.size--;
1162 iter->elem_count--;
1023 cxFree(list->collection.allocator, node); 1163 cxFree(list->collection.allocator, node);
1024 } else { 1164 } else {
1025 iter->index--; 1165 iter->index--;
1026 cx_linked_list_node *node = iter->elem_handle; 1166 char *node = iter->elem_handle;
1027 iter->elem_handle = node->prev; 1167 iter->elem_handle = CX_LL_PTR(node, ll->loc_prev);
1028 } 1168 }
1029 } 1169 }
1030 1170
1031 static void *cx_ll_iter_current(const void *it) { 1171 static void *cx_ll_iter_current(const void *it) {
1032 const struct cx_iterator_s *iter = it; 1172 const struct cx_iterator_s *iter = it;
1033 cx_linked_list_node *node = iter->elem_handle; 1173 const cx_linked_list *ll = iter->src_handle;
1034 return node->payload; 1174 char *node = iter->elem_handle;
1175 return node + ll->loc_data;
1035 } 1176 }
1036 1177
1037 static CxIterator cx_ll_iterator( 1178 static CxIterator cx_ll_iterator(
1038 const struct cx_list_s *list, 1179 const struct cx_list_s *list,
1039 size_t index, 1180 size_t index,
1040 bool backwards 1181 bool backwards
1041 ) { 1182 ) {
1042 CxIterator iter; 1183 CxIterator iter;
1043 iter.index = index; 1184 iter.index = index;
1044 iter.src_handle.c = list; 1185 iter.src_handle = (void*)list;
1045 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);
1046 iter.elem_size = list->collection.elem_size; 1187 iter.elem_size = list->collection.elem_size;
1047 iter.elem_count = list->collection.size; 1188 iter.elem_count = list->collection.size;
1048 iter.base.valid = cx_ll_iter_valid; 1189 iter.base.valid = cx_ll_iter_valid;
1049 iter.base.current = cx_ll_iter_current; 1190 iter.base.current = cx_ll_iter_current;
1050 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;
1051 iter.base.mutating = false; 1192 iter.base.allow_remove = true;
1052 iter.base.remove = false; 1193 iter.base.remove = false;
1053 return iter; 1194 return iter;
1054 } 1195 }
1055 1196
1056 static int cx_ll_insert_iter( 1197 static int cx_ll_insert_iter(
1057 CxIterator *iter, 1198 CxIterator *iter,
1058 const void *elem, 1199 const void *elem,
1059 int prepend 1200 int prepend
1060 ) { 1201 ) {
1061 struct cx_list_s *list = iter->src_handle.m; 1202 struct cx_list_s *list = iter->src_handle;
1062 cx_linked_list_node *node = iter->elem_handle; 1203 cx_linked_list *ll = iter->src_handle;
1204 void *node = iter->elem_handle;
1063 if (node != NULL) { 1205 if (node != NULL) {
1064 assert(prepend >= 0 && prepend <= 1); 1206 assert(prepend >= 0 && prepend <= 1);
1065 cx_linked_list_node *choice[2] = {node, node->prev}; 1207 void *choice[2] = {node, CX_LL_PTR(node, ll->loc_prev)};
1066 int result = cx_ll_insert_at(list, choice[prepend], elem); 1208 int result = cx_ll_insert_at(list, choice[prepend], elem);
1067 if (result == 0) { 1209 if (result == 0) {
1068 iter->elem_count++; 1210 iter->elem_count++;
1069 if (prepend) { 1211 if (prepend) {
1070 iter->index++; 1212 iter->index++;
1082 } 1224 }
1083 1225
1084 static void cx_ll_destructor(CxList *list) { 1226 static void cx_ll_destructor(CxList *list) {
1085 cx_linked_list *ll = (cx_linked_list *) list; 1227 cx_linked_list *ll = (cx_linked_list *) list;
1086 1228
1087 cx_linked_list_node *node = ll->begin; 1229 char *node = ll->begin;
1088 while (node) { 1230 while (node) {
1089 cx_invoke_destructor(list, node->payload); 1231 cx_invoke_destructor(list, node + ll->loc_data);
1090 void *next = node->next; 1232 void *next = CX_LL_PTR(node, ll->loc_next);
1091 cxFree(list->collection.allocator, node); 1233 cxFree(list->collection.allocator, node);
1092 node = next; 1234 node = next;
1093 } 1235 }
1094 1236
1095 cxFree(list->collection.allocator, list); 1237 cxFree(list->collection.allocator, list);
1098 static cx_list_class cx_linked_list_class = { 1240 static cx_list_class cx_linked_list_class = {
1099 cx_ll_destructor, 1241 cx_ll_destructor,
1100 cx_ll_insert_element, 1242 cx_ll_insert_element,
1101 cx_ll_insert_array, 1243 cx_ll_insert_array,
1102 cx_ll_insert_sorted, 1244 cx_ll_insert_sorted,
1245 cx_ll_insert_unique,
1103 cx_ll_insert_iter, 1246 cx_ll_insert_iter,
1104 cx_ll_remove, 1247 cx_ll_remove,
1105 cx_ll_clear, 1248 cx_ll_clear,
1106 cx_ll_swap, 1249 cx_ll_swap,
1107 cx_ll_at, 1250 cx_ll_at,
1121 allocator = cxDefaultAllocator; 1264 allocator = cxDefaultAllocator;
1122 } 1265 }
1123 1266
1124 cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list)); 1267 cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list));
1125 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;
1126 cx_list_init((CxList*)list, &cx_linked_list_class, 1273 cx_list_init((CxList*)list, &cx_linked_list_class,
1127 allocator, comparator, elem_size); 1274 allocator, comparator, elem_size);
1128 1275
1129 return (CxList *) list; 1276 return (CxList *) list;
1130 } 1277 }

mercurial