39 #define ll_next(node) CX_LL_PTR(node, loc_next) |
38 #define ll_next(node) CX_LL_PTR(node, loc_next) |
40 #define ll_advance(node) CX_LL_PTR(node, loc_advance) |
39 #define ll_advance(node) CX_LL_PTR(node, loc_advance) |
41 #define ll_data(node) (((char*)(node))+loc_data) |
40 #define ll_data(node) (((char*)(node))+loc_data) |
42 |
41 |
43 void *cx_linked_list_at( |
42 void *cx_linked_list_at( |
44 void const *start, |
43 const void *start, |
45 size_t start_index, |
44 size_t start_index, |
46 ptrdiff_t loc_advance, |
45 ptrdiff_t loc_advance, |
47 size_t index |
46 size_t index |
48 ) { |
47 ) { |
49 assert(start != NULL); |
48 assert(start != NULL); |
50 assert(loc_advance >= 0); |
49 assert(loc_advance >= 0); |
51 size_t i = start_index; |
50 size_t i = start_index; |
52 void const *cur = start; |
51 const void *cur = start; |
53 while (i != index && cur != NULL) { |
52 while (i != index && cur != NULL) { |
54 cur = ll_advance(cur); |
53 cur = ll_advance(cur); |
55 i < index ? i++ : i--; |
54 i < index ? i++ : i--; |
56 } |
55 } |
57 return (void *) cur; |
56 return (void *) cur; |
58 } |
57 } |
59 |
58 |
60 ssize_t cx_linked_list_find( |
59 ssize_t cx_linked_list_find( |
61 void const *start, |
60 const void *start, |
62 ptrdiff_t loc_advance, |
61 ptrdiff_t loc_advance, |
63 ptrdiff_t loc_data, |
62 ptrdiff_t loc_data, |
64 cx_compare_func cmp_func, |
63 cx_compare_func cmp_func, |
65 void const *elem |
64 const void *elem |
66 ) { |
65 ) { |
67 void *dummy; |
66 void *dummy; |
68 return cx_linked_list_find_node( |
67 return cx_linked_list_find_node( |
69 &dummy, start, |
68 &dummy, start, |
70 loc_advance, loc_data, |
69 loc_advance, loc_data, |
72 ); |
71 ); |
73 } |
72 } |
74 |
73 |
75 ssize_t cx_linked_list_find_node( |
74 ssize_t cx_linked_list_find_node( |
76 void **result, |
75 void **result, |
77 void const *start, |
76 const void *start, |
78 ptrdiff_t loc_advance, |
77 ptrdiff_t loc_advance, |
79 ptrdiff_t loc_data, |
78 ptrdiff_t loc_data, |
80 cx_compare_func cmp_func, |
79 cx_compare_func cmp_func, |
81 void const *elem |
80 const void *elem |
82 ) { |
81 ) { |
83 assert(result != NULL); |
82 assert(result != NULL); |
84 assert(start != NULL); |
83 assert(start != NULL); |
85 assert(loc_advance >= 0); |
84 assert(loc_advance >= 0); |
86 assert(loc_data >= 0); |
85 assert(loc_data >= 0); |
87 assert(cmp_func); |
86 assert(cmp_func); |
88 |
87 |
89 void const *node = start; |
88 const void *node = start; |
90 ssize_t index = 0; |
89 ssize_t index = 0; |
91 do { |
90 do { |
92 void *current = ll_data(node); |
91 void *current = ll_data(node); |
93 if (cmp_func(current, elem) == 0) { |
92 if (cmp_func(current, elem) == 0) { |
94 *result = (void*) node; |
93 *result = (void *) node; |
95 return index; |
94 return index; |
96 } |
95 } |
97 node = ll_advance(node); |
96 node = ll_advance(node); |
98 index++; |
97 index++; |
99 } while (node != NULL); |
98 } while (node != NULL); |
100 *result = NULL; |
99 *result = NULL; |
101 return -1; |
100 return -1; |
102 } |
101 } |
103 |
102 |
104 void *cx_linked_list_first( |
103 void *cx_linked_list_first( |
105 void const *node, |
104 const void *node, |
106 ptrdiff_t loc_prev |
105 ptrdiff_t loc_prev |
107 ) { |
106 ) { |
108 return cx_linked_list_last(node, loc_prev); |
107 return cx_linked_list_last(node, loc_prev); |
109 } |
108 } |
110 |
109 |
111 void *cx_linked_list_last( |
110 void *cx_linked_list_last( |
112 void const *node, |
111 const void *node, |
113 ptrdiff_t loc_next |
112 ptrdiff_t loc_next |
114 ) { |
113 ) { |
115 assert(node != NULL); |
114 assert(node != NULL); |
116 assert(loc_next >= 0); |
115 assert(loc_next >= 0); |
117 |
116 |
118 void const *cur = node; |
117 const void *cur = node; |
119 void const *last; |
118 const void *last; |
120 do { |
119 do { |
121 last = cur; |
120 last = cur; |
122 } while ((cur = ll_next(cur)) != NULL); |
121 } while ((cur = ll_next(cur)) != NULL); |
123 |
122 |
124 return (void *) last; |
123 return (void *) last; |
125 } |
124 } |
126 |
125 |
127 void *cx_linked_list_prev( |
126 void *cx_linked_list_prev( |
128 void const *begin, |
127 const void *begin, |
129 ptrdiff_t loc_next, |
128 ptrdiff_t loc_next, |
130 void const *node |
129 const void *node |
131 ) { |
130 ) { |
132 assert(begin != NULL); |
131 assert(begin != NULL); |
133 assert(node != NULL); |
132 assert(node != NULL); |
134 assert(loc_next >= 0); |
133 assert(loc_next >= 0); |
135 if (begin == node) return NULL; |
134 if (begin == node) return NULL; |
136 void const *cur = begin; |
135 const void *cur = begin; |
137 void const *next; |
136 const void *next; |
138 while (1) { |
137 while (1) { |
139 next = ll_next(cur); |
138 next = ll_next(cur); |
140 if (next == node) return (void *) cur; |
139 if (next == node) return (void *) cur; |
141 cur = next; |
140 cur = next; |
142 } |
141 } |
245 } else { |
244 } else { |
246 cx_linked_list_link(insert_end, successor, loc_prev, loc_next); |
245 cx_linked_list_link(insert_end, successor, loc_prev, loc_next); |
247 } |
246 } |
248 } |
247 } |
249 |
248 |
250 void cx_linked_list_remove( |
249 void cx_linked_list_insert_sorted( |
251 void **begin, |
250 void **begin, |
252 void **end, |
251 void **end, |
253 ptrdiff_t loc_prev, |
252 ptrdiff_t loc_prev, |
254 ptrdiff_t loc_next, |
253 ptrdiff_t loc_next, |
255 void *node |
254 void *new_node, |
|
255 cx_compare_func cmp_func |
|
256 ) { |
|
257 assert(ll_next(new_node) == NULL); |
|
258 cx_linked_list_insert_sorted_chain( |
|
259 begin, end, loc_prev, loc_next, new_node, cmp_func); |
|
260 } |
|
261 |
|
262 void cx_linked_list_insert_sorted_chain( |
|
263 void **begin, |
|
264 void **end, |
|
265 ptrdiff_t loc_prev, |
|
266 ptrdiff_t loc_next, |
|
267 void *insert_begin, |
|
268 cx_compare_func cmp_func |
|
269 ) { |
|
270 assert(begin != NULL); |
|
271 assert(loc_next >= 0); |
|
272 assert(insert_begin != NULL); |
|
273 |
|
274 // track currently observed nodes |
|
275 void *dest_prev = NULL; |
|
276 void *dest = *begin; |
|
277 void *src = insert_begin; |
|
278 |
|
279 // special case: list is empty |
|
280 if (dest == NULL) { |
|
281 *begin = src; |
|
282 if (end != NULL) { |
|
283 *end = cx_linked_list_last(src, loc_next); |
|
284 } |
|
285 return; |
|
286 } |
|
287 |
|
288 // search the list for insertion points |
|
289 while (dest != NULL && src != NULL) { |
|
290 // compare current list node with source node |
|
291 // if less or equal, skip |
|
292 if (cmp_func(dest, src) <= 0) { |
|
293 dest_prev = dest; |
|
294 dest = ll_next(dest); |
|
295 continue; |
|
296 } |
|
297 |
|
298 // determine chain of elements that can be inserted |
|
299 void *end_of_chain = src; |
|
300 void *next_in_chain = ll_next(src); |
|
301 while (next_in_chain != NULL) { |
|
302 // once we become larger than the list elem, break |
|
303 if (cmp_func(dest, next_in_chain) <= 0) { |
|
304 break; |
|
305 } |
|
306 // otherwise, we can insert one more |
|
307 end_of_chain = next_in_chain; |
|
308 next_in_chain = ll_next(next_in_chain); |
|
309 } |
|
310 |
|
311 // insert the elements |
|
312 if (dest_prev == NULL) { |
|
313 // new begin |
|
314 *begin = src; |
|
315 } else { |
|
316 cx_linked_list_link(dest_prev, src, loc_prev, loc_next); |
|
317 } |
|
318 cx_linked_list_link(end_of_chain, dest, loc_prev, loc_next); |
|
319 |
|
320 // continue with next |
|
321 src = next_in_chain; |
|
322 dest_prev = dest; |
|
323 dest = ll_next(dest); |
|
324 } |
|
325 |
|
326 // insert remaining items |
|
327 if (src != NULL) { |
|
328 cx_linked_list_link(dest_prev, src, loc_prev, loc_next); |
|
329 } |
|
330 |
|
331 // determine new end of list, if requested |
|
332 if (end != NULL) { |
|
333 *end = cx_linked_list_last( |
|
334 dest != NULL ? dest : dest_prev, loc_next); |
|
335 } |
|
336 } |
|
337 |
|
338 size_t cx_linked_list_remove_chain( |
|
339 void **begin, |
|
340 void **end, |
|
341 ptrdiff_t loc_prev, |
|
342 ptrdiff_t loc_next, |
|
343 void *node, |
|
344 size_t num |
256 ) { |
345 ) { |
257 assert(node != NULL); |
346 assert(node != NULL); |
258 assert(loc_next >= 0); |
347 assert(loc_next >= 0); |
259 assert(loc_prev >= 0 || begin != NULL); |
348 assert(loc_prev >= 0 || begin != NULL); |
260 |
349 |
|
350 // easy exit |
|
351 if (num == 0) return 0; |
|
352 |
261 // find adjacent nodes |
353 // find adjacent nodes |
262 void *next = ll_next(node); |
|
263 void *prev; |
354 void *prev; |
264 if (loc_prev >= 0) { |
355 if (loc_prev >= 0) { |
265 prev = ll_prev(node); |
356 prev = ll_prev(node); |
266 } else { |
357 } else { |
267 prev = cx_linked_list_prev(*begin, loc_next, node); |
358 prev = cx_linked_list_prev(*begin, loc_next, node); |
|
359 } |
|
360 |
|
361 void *next = ll_next(node); |
|
362 size_t removed = 1; |
|
363 for (; removed < num && next != NULL ; removed++) { |
|
364 next = ll_next(next); |
268 } |
365 } |
269 |
366 |
270 // update next pointer of prev node, or set begin |
367 // update next pointer of prev node, or set begin |
271 if (prev == NULL) { |
368 if (prev == NULL) { |
272 if (begin != NULL) { |
369 if (begin != NULL) { |
423 if (end) *end = sorted_end; |
522 if (end) *end = sorted_end; |
424 } |
523 } |
425 } |
524 } |
426 |
525 |
427 int cx_linked_list_compare( |
526 int cx_linked_list_compare( |
428 void const *begin_left, |
527 const void *begin_left, |
429 void const *begin_right, |
528 const void *begin_right, |
430 ptrdiff_t loc_advance, |
529 ptrdiff_t loc_advance, |
431 ptrdiff_t loc_data, |
530 ptrdiff_t loc_data, |
432 cx_compare_func cmp_func |
531 cx_compare_func cmp_func |
433 ) { |
532 ) { |
434 void const *left = begin_left, *right = begin_right; |
533 const void *left = begin_left, *right = begin_right; |
435 |
534 |
436 while (left != NULL && right != NULL) { |
535 while (left != NULL && right != NULL) { |
437 void const *left_data = ll_data(left); |
536 const void *left_data = ll_data(left); |
438 void const *right_data = ll_data(right); |
537 const void *right_data = ll_data(right); |
439 int result = cmp_func(left_data, right_data); |
538 int result = cmp_func(left_data, right_data); |
440 if (result != 0) return result; |
539 if (result != 0) return result; |
441 left = ll_advance(left); |
540 left = ll_advance(left); |
442 right = ll_advance(right); |
541 right = ll_advance(right); |
443 } |
542 } |
541 } |
644 } |
542 |
645 |
543 static size_t cx_ll_insert_array( |
646 static size_t cx_ll_insert_array( |
544 struct cx_list_s *list, |
647 struct cx_list_s *list, |
545 size_t index, |
648 size_t index, |
546 void const *array, |
649 const void *array, |
547 size_t n |
650 size_t n |
548 ) { |
651 ) { |
549 // out-of bounds and corner case check |
652 // out-of bounds and corner case check |
550 if (index > list->collection.size || n == 0) return 0; |
653 if (index > list->collection.size || n == 0) return 0; |
551 |
654 |
552 // find position efficiently |
655 // find position efficiently |
553 cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); |
656 cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); |
554 |
657 |
555 // perform first insert |
658 // perform first insert |
556 if (0 != cx_ll_insert_at(list, node, array)) { |
659 if (0 != cx_ll_insert_at(list, node, array)) return 1; |
557 return 1; |
|
558 } |
|
559 |
660 |
560 // is there more? |
661 // is there more? |
561 if (n == 1) return 1; |
662 if (n == 1) return 1; |
562 |
663 |
563 // we now know exactly where we are |
664 // we now know exactly where we are |
564 node = node == NULL ? ((cx_linked_list *) list)->begin : node->next; |
665 node = node == NULL ? ((cx_linked_list *) list)->begin : node->next; |
565 |
666 |
566 // we can add the remaining nodes and immedately advance to the inserted node |
667 // we can add the remaining nodes and immediately advance to the inserted node |
567 char const *source = array; |
668 const char *source = array; |
568 for (size_t i = 1; i < n; i++) { |
669 for (size_t i = 1; i < n; i++) { |
569 source += list->collection.elem_size; |
670 source += list->collection.elem_size; |
570 if (0 != cx_ll_insert_at(list, node, source)) { |
671 if (0 != cx_ll_insert_at(list, node, source)) return i; |
571 return i; |
|
572 } |
|
573 node = node->next; |
672 node = node->next; |
574 } |
673 } |
575 return n; |
674 return n; |
576 } |
675 } |
577 |
676 |
578 static int cx_ll_insert_element( |
677 static int cx_ll_insert_element( |
579 struct cx_list_s *list, |
678 struct cx_list_s *list, |
580 size_t index, |
679 size_t index, |
581 void const *element |
680 const void *element |
582 ) { |
681 ) { |
583 return 1 != cx_ll_insert_array(list, index, element, 1); |
682 return 1 != cx_ll_insert_array(list, index, element, 1); |
584 } |
683 } |
585 |
684 |
586 static int cx_ll_remove( |
685 static _Thread_local cx_compare_func cx_ll_insert_sorted_cmp_func; |
|
686 |
|
687 static int cx_ll_insert_sorted_cmp_helper(const void *l, const void *r) { |
|
688 const cx_linked_list_node *left = l; |
|
689 const cx_linked_list_node *right = r; |
|
690 return cx_ll_insert_sorted_cmp_func(left->payload, right->payload); |
|
691 } |
|
692 |
|
693 static size_t cx_ll_insert_sorted( |
587 struct cx_list_s *list, |
694 struct cx_list_s *list, |
588 size_t index |
695 const void *array, |
|
696 size_t n |
|
697 ) { |
|
698 // special case |
|
699 if (n == 0) return 0; |
|
700 |
|
701 // create a new chain of nodes |
|
702 cx_linked_list_node *chain = cx_ll_malloc_node(list); |
|
703 if (chain == NULL) return 0; |
|
704 |
|
705 memcpy(chain->payload, array, list->collection.elem_size); |
|
706 chain->prev = NULL; |
|
707 chain->next = NULL; |
|
708 |
|
709 // add all elements from the array to that chain |
|
710 cx_linked_list_node *prev = chain; |
|
711 const char *src = array; |
|
712 size_t inserted = 1; |
|
713 for (; inserted < n; inserted++) { |
|
714 cx_linked_list_node *next = cx_ll_malloc_node(list); |
|
715 if (next == NULL) break; |
|
716 src += list->collection.elem_size; |
|
717 memcpy(next->payload, src, list->collection.elem_size); |
|
718 prev->next = next; |
|
719 next->prev = prev; |
|
720 prev = next; |
|
721 } |
|
722 prev->next = NULL; |
|
723 |
|
724 // invoke the low level function |
|
725 cx_linked_list *ll = (cx_linked_list *) list; |
|
726 cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc; |
|
727 cx_linked_list_insert_sorted_chain( |
|
728 (void **) &ll->begin, |
|
729 (void **) &ll->end, |
|
730 CX_LL_LOC_PREV, |
|
731 CX_LL_LOC_NEXT, |
|
732 chain, |
|
733 cx_ll_insert_sorted_cmp_helper |
|
734 ); |
|
735 |
|
736 // adjust the list metadata |
|
737 list->collection.size += inserted; |
|
738 |
|
739 return inserted; |
|
740 } |
|
741 |
|
742 static size_t cx_ll_remove( |
|
743 struct cx_list_s *list, |
|
744 size_t index, |
|
745 size_t num, |
|
746 void *targetbuf |
589 ) { |
747 ) { |
590 cx_linked_list *ll = (cx_linked_list *) list; |
748 cx_linked_list *ll = (cx_linked_list *) list; |
591 cx_linked_list_node *node = cx_ll_node_at(ll, index); |
749 cx_linked_list_node *node = cx_ll_node_at(ll, index); |
592 |
750 |
593 // out-of-bounds check |
751 // out-of-bounds check |
594 if (node == NULL) return 1; |
752 if (node == NULL) return 0; |
595 |
|
596 // element destruction |
|
597 cx_invoke_destructor(list, node->payload); |
|
598 |
753 |
599 // remove |
754 // remove |
600 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, |
755 size_t removed = cx_linked_list_remove_chain( |
601 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); |
756 (void **) &ll->begin, |
|
757 (void **) &ll->end, |
|
758 CX_LL_LOC_PREV, |
|
759 CX_LL_LOC_NEXT, |
|
760 node, |
|
761 num |
|
762 ); |
602 |
763 |
603 // adjust size |
764 // adjust size |
604 list->collection.size--; |
765 list->collection.size -= removed; |
605 |
766 |
606 // free and return |
767 // copy or destroy the removed chain |
607 cxFree(list->collection.allocator, node); |
768 if (targetbuf == NULL) { |
608 |
769 cx_linked_list_node *n = node; |
609 return 0; |
770 for (size_t i = 0; i < removed; i++) { |
|
771 // element destruction |
|
772 cx_invoke_destructor(list, n->payload); |
|
773 |
|
774 // free the node and advance |
|
775 void *next = n->next; |
|
776 cxFree(list->collection.allocator, n); |
|
777 n = next; |
|
778 } |
|
779 } else { |
|
780 char *dest = targetbuf; |
|
781 cx_linked_list_node *n = node; |
|
782 for (size_t i = 0; i < removed; i++) { |
|
783 // copy payload |
|
784 memcpy(dest, n->payload, list->collection.elem_size); |
|
785 |
|
786 // advance target buffer |
|
787 dest += list->collection.elem_size; |
|
788 |
|
789 // free the node and advance |
|
790 void *next = n->next; |
|
791 cxFree(list->collection.allocator, n); |
|
792 n = next; |
|
793 } |
|
794 } |
|
795 |
|
796 return removed; |
610 } |
797 } |
611 |
798 |
612 static void cx_ll_clear(struct cx_list_s *list) { |
799 static void cx_ll_clear(struct cx_list_s *list) { |
613 if (list->collection.size == 0) return; |
800 if (list->collection.size == 0) return; |
614 |
801 |
798 cx_linked_list *ll = (cx_linked_list *) list; |
985 cx_linked_list *ll = (cx_linked_list *) list; |
799 cx_linked_list_reverse((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT); |
986 cx_linked_list_reverse((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT); |
800 } |
987 } |
801 |
988 |
802 static int cx_ll_compare( |
989 static int cx_ll_compare( |
803 struct cx_list_s const *list, |
990 const struct cx_list_s *list, |
804 struct cx_list_s const *other |
991 const struct cx_list_s *other |
805 ) { |
992 ) { |
806 cx_linked_list *left = (cx_linked_list *) list; |
993 cx_linked_list *left = (cx_linked_list *) list; |
807 cx_linked_list *right = (cx_linked_list *) other; |
994 cx_linked_list *right = (cx_linked_list *) other; |
808 return cx_linked_list_compare(left->begin, right->begin, |
995 return cx_linked_list_compare(left->begin, right->begin, |
809 CX_LL_LOC_NEXT, CX_LL_LOC_DATA, |
996 CX_LL_LOC_NEXT, CX_LL_LOC_DATA, |
810 list->collection.cmpfunc); |
997 list->collection.cmpfunc); |
811 } |
998 } |
812 |
999 |
813 static bool cx_ll_iter_valid(void const *it) { |
1000 static bool cx_ll_iter_valid(const void *it) { |
814 struct cx_iterator_s const *iter = it; |
1001 const struct cx_iterator_s *iter = it; |
815 return iter->elem_handle != NULL; |
1002 return iter->elem_handle != NULL; |
816 } |
1003 } |
817 |
1004 |
818 static void cx_ll_iter_next(void *it) { |
1005 static void cx_ll_iter_next(void *it) { |
819 struct cx_iterator_s *iter = it; |
1006 struct cx_iterator_s *iter = it; |
854 cx_linked_list_node *node = iter->elem_handle; |
1041 cx_linked_list_node *node = iter->elem_handle; |
855 iter->elem_handle = node->prev; |
1042 iter->elem_handle = node->prev; |
856 } |
1043 } |
857 } |
1044 } |
858 |
1045 |
859 static void *cx_ll_iter_current(void const *it) { |
1046 static void *cx_ll_iter_current(const void *it) { |
860 struct cx_iterator_s const *iter = it; |
1047 const struct cx_iterator_s *iter = it; |
861 cx_linked_list_node *node = iter->elem_handle; |
1048 cx_linked_list_node *node = iter->elem_handle; |
862 return node->payload; |
1049 return node->payload; |
863 } |
1050 } |
864 |
1051 |
865 static CxIterator cx_ll_iterator( |
1052 static CxIterator cx_ll_iterator( |
866 struct cx_list_s const *list, |
1053 const struct cx_list_s *list, |
867 size_t index, |
1054 size_t index, |
868 bool backwards |
1055 bool backwards |
869 ) { |
1056 ) { |
870 CxIterator iter; |
1057 CxIterator iter; |
871 iter.index = index; |
1058 iter.index = index; |
872 iter.src_handle.c = list; |
1059 iter.src_handle.c = list; |
873 iter.elem_handle = cx_ll_node_at((cx_linked_list const *) list, index); |
1060 iter.elem_handle = cx_ll_node_at((const cx_linked_list *) list, index); |
874 iter.elem_size = list->collection.elem_size; |
1061 iter.elem_size = list->collection.elem_size; |
875 iter.elem_count = list->collection.size; |
1062 iter.elem_count = list->collection.size; |
876 iter.base.valid = cx_ll_iter_valid; |
1063 iter.base.valid = cx_ll_iter_valid; |
877 iter.base.current = cx_ll_iter_current; |
1064 iter.base.current = cx_ll_iter_current; |
878 iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next; |
1065 iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next; |