| 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, |
| 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 |
| 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); |