UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "cx/linked_list.h" 30 #include "cx/utils.h" 31 #include "cx/compare.h" 32 #include <string.h> 33 #include <assert.h> 34 35 // LOW LEVEL LINKED LIST FUNCTIONS 36 37 #define CX_LL_PTR(cur, off) (*(void**)(((char*)(cur))+(off))) 38 #define ll_prev(node) CX_LL_PTR(node, loc_prev) 39 #define ll_next(node) CX_LL_PTR(node, loc_next) 40 #define ll_advance(node) CX_LL_PTR(node, loc_advance) 41 #define ll_data(node) (((char*)(node))+loc_data) 42 43 void *cx_linked_list_at( 44 void const *start, 45 size_t start_index, 46 ptrdiff_t loc_advance, 47 size_t index 48 ) { 49 assert(start != NULL); 50 assert(loc_advance >= 0); 51 size_t i = start_index; 52 void const *cur = start; 53 while (i != index && cur != NULL) { 54 cur = ll_advance(cur); 55 i < index ? i++ : i--; 56 } 57 return (void *) cur; 58 } 59 60 ssize_t cx_linked_list_find( 61 void const *start, 62 ptrdiff_t loc_advance, 63 ptrdiff_t loc_data, 64 cx_compare_func cmp_func, 65 void const *elem 66 ) { 67 void *dummy; 68 return cx_linked_list_find_node( 69 &dummy, start, 70 loc_advance, loc_data, 71 cmp_func, elem 72 ); 73 } 74 75 ssize_t cx_linked_list_find_node( 76 void **result, 77 void const *start, 78 ptrdiff_t loc_advance, 79 ptrdiff_t loc_data, 80 cx_compare_func cmp_func, 81 void const *elem 82 ) { 83 assert(result != NULL); 84 assert(start != NULL); 85 assert(loc_advance >= 0); 86 assert(loc_data >= 0); 87 assert(cmp_func); 88 89 void const *node = start; 90 ssize_t index = 0; 91 do { 92 void *current = ll_data(node); 93 if (cmp_func(current, elem) == 0) { 94 *result = (void*) node; 95 return index; 96 } 97 node = ll_advance(node); 98 index++; 99 } while (node != NULL); 100 *result = NULL; 101 return -1; 102 } 103 104 void *cx_linked_list_first( 105 void const *node, 106 ptrdiff_t loc_prev 107 ) { 108 return cx_linked_list_last(node, loc_prev); 109 } 110 111 void *cx_linked_list_last( 112 void const *node, 113 ptrdiff_t loc_next 114 ) { 115 assert(node != NULL); 116 assert(loc_next >= 0); 117 118 void const *cur = node; 119 void const *last; 120 do { 121 last = cur; 122 } while ((cur = ll_next(cur)) != NULL); 123 124 return (void *) last; 125 } 126 127 void *cx_linked_list_prev( 128 void const *begin, 129 ptrdiff_t loc_next, 130 void const *node 131 ) { 132 assert(begin != NULL); 133 assert(node != NULL); 134 assert(loc_next >= 0); 135 if (begin == node) return NULL; 136 void const *cur = begin; 137 void const *next; 138 while (1) { 139 next = ll_next(cur); 140 if (next == node) return (void *) cur; 141 cur = next; 142 } 143 } 144 145 void cx_linked_list_link( 146 void *left, 147 void *right, 148 ptrdiff_t loc_prev, 149 ptrdiff_t loc_next 150 ) { 151 assert(loc_next >= 0); 152 ll_next(left) = right; 153 if (loc_prev >= 0) { 154 ll_prev(right) = left; 155 } 156 } 157 158 void cx_linked_list_unlink( 159 void *left, 160 void *right, 161 ptrdiff_t loc_prev, 162 ptrdiff_t loc_next 163 ) { 164 assert (loc_next >= 0); 165 assert(ll_next(left) == right); 166 ll_next(left) = NULL; 167 if (loc_prev >= 0) { 168 assert(ll_prev(right) == left); 169 ll_prev(right) = NULL; 170 } 171 } 172 173 void cx_linked_list_add( 174 void **begin, 175 void **end, 176 ptrdiff_t loc_prev, 177 ptrdiff_t loc_next, 178 void *new_node 179 ) { 180 void *last; 181 if (end == NULL) { 182 assert(begin != NULL); 183 last = *begin == NULL ? NULL : cx_linked_list_last(*begin, loc_next); 184 } else { 185 last = *end; 186 } 187 cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, last, new_node, new_node); 188 } 189 190 void cx_linked_list_prepend( 191 void **begin, 192 void **end, 193 ptrdiff_t loc_prev, 194 ptrdiff_t loc_next, 195 void *new_node 196 ) { 197 cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, NULL, new_node, new_node); 198 } 199 200 void cx_linked_list_insert( 201 void **begin, 202 void **end, 203 ptrdiff_t loc_prev, 204 ptrdiff_t loc_next, 205 void *node, 206 void *new_node 207 ) { 208 cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, node, new_node, new_node); 209 } 210 211 void cx_linked_list_insert_chain( 212 void **begin, 213 void **end, 214 ptrdiff_t loc_prev, 215 ptrdiff_t loc_next, 216 void *node, 217 void *insert_begin, 218 void *insert_end 219 ) { 220 // find the end of the chain, if not specified 221 if (insert_end == NULL) { 222 insert_end = cx_linked_list_last(insert_begin, loc_next); 223 } 224 225 // determine the successor 226 void *successor; 227 if (node == NULL) { 228 assert(begin != NULL || (end != NULL && loc_prev >= 0)); 229 if (begin != NULL) { 230 successor = *begin; 231 *begin = insert_begin; 232 } else { 233 successor = *end == NULL ? NULL : cx_linked_list_first(*end, loc_prev); 234 } 235 } else { 236 successor = ll_next(node); 237 cx_linked_list_link(node, insert_begin, loc_prev, loc_next); 238 } 239 240 if (successor == NULL) { 241 // the list ends with the new chain 242 if (end != NULL) { 243 *end = insert_end; 244 } 245 } else { 246 cx_linked_list_link(insert_end, successor, loc_prev, loc_next); 247 } 248 } 249 250 void cx_linked_list_remove( 251 void **begin, 252 void **end, 253 ptrdiff_t loc_prev, 254 ptrdiff_t loc_next, 255 void *node 256 ) { 257 assert(node != NULL); 258 assert(loc_next >= 0); 259 assert(loc_prev >= 0 || begin != NULL); 260 261 // find adjacent nodes 262 void *next = ll_next(node); 263 void *prev; 264 if (loc_prev >= 0) { 265 prev = ll_prev(node); 266 } else { 267 prev = cx_linked_list_prev(*begin, loc_next, node); 268 } 269 270 // update next pointer of prev node, or set begin 271 if (prev == NULL) { 272 if (begin != NULL) { 273 *begin = next; 274 } 275 } else { 276 ll_next(prev) = next; 277 } 278 279 // update prev pointer of next node, or set end 280 if (next == NULL) { 281 if (end != NULL) { 282 *end = prev; 283 } 284 } else if (loc_prev >= 0) { 285 ll_prev(next) = prev; 286 } 287 } 288 289 size_t cx_linked_list_size( 290 void const *node, 291 ptrdiff_t loc_next 292 ) { 293 assert(loc_next >= 0); 294 size_t size = 0; 295 while (node != NULL) { 296 node = ll_next(node); 297 size++; 298 } 299 return size; 300 } 301 302 #ifndef CX_LINKED_LIST_SORT_SBO_SIZE 303 #define CX_LINKED_LIST_SORT_SBO_SIZE 1024 304 #endif 305 306 static void cx_linked_list_sort_merge( 307 ptrdiff_t loc_prev, 308 ptrdiff_t loc_next, 309 ptrdiff_t loc_data, 310 size_t length, 311 void *ls, 312 void *le, 313 void *re, 314 cx_compare_func cmp_func, 315 void **begin, 316 void **end 317 ) { 318 void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE]; 319 void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ? 320 malloc(sizeof(void *) * length) : sbo; 321 if (sorted == NULL) abort(); 322 void *rc, *lc; 323 324 lc = ls; 325 rc = le; 326 size_t n = 0; 327 while (lc && lc != le && rc != re) { 328 if (cmp_func(ll_data(lc), ll_data(rc)) <= 0) { 329 sorted[n] = lc; 330 lc = ll_next(lc); 331 } else { 332 sorted[n] = rc; 333 rc = ll_next(rc); 334 } 335 n++; 336 } 337 while (lc && lc != le) { 338 sorted[n] = lc; 339 lc = ll_next(lc); 340 n++; 341 } 342 while (rc && rc != re) { 343 sorted[n] = rc; 344 rc = ll_next(rc); 345 n++; 346 } 347 348 // Update pointer 349 if (loc_prev >= 0) ll_prev(sorted[0]) = NULL; 350 cx_for_n (i, length - 1) { 351 cx_linked_list_link(sorted[i], sorted[i + 1], loc_prev, loc_next); 352 } 353 ll_next(sorted[length - 1]) = NULL; 354 355 *begin = sorted[0]; 356 *end = sorted[length-1]; 357 if (sorted != sbo) { 358 free(sorted); 359 } 360 } 361 362 void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive function 363 void **begin, 364 void **end, 365 ptrdiff_t loc_prev, 366 ptrdiff_t loc_next, 367 ptrdiff_t loc_data, 368 cx_compare_func cmp_func 369 ) { 370 assert(begin != NULL); 371 assert(loc_next >= 0); 372 assert(loc_data >= 0); 373 assert(cmp_func); 374 375 void *lc, *ls, *le, *re; 376 377 // set start node 378 ls = *begin; 379 380 // early exit when this list is empty 381 if (ls == NULL) return; 382 383 // check how many elements are already sorted 384 lc = ls; 385 size_t ln = 1; 386 while (ll_next(lc) != NULL && cmp_func(ll_data(ll_next(lc)), ll_data(lc)) > 0) { 387 lc = ll_next(lc); 388 ln++; 389 } 390 le = ll_next(lc); 391 392 // if first unsorted node is NULL, the list is already completely sorted 393 if (le != NULL) { 394 void *rc; 395 size_t rn = 1; 396 rc = le; 397 // skip already sorted elements 398 while (ll_next(rc) != NULL && cmp_func(ll_data(ll_next(rc)), ll_data(rc)) > 0) { 399 rc = ll_next(rc); 400 rn++; 401 } 402 re = ll_next(rc); 403 404 // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them 405 void *sorted_begin, *sorted_end; 406 cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, 407 ln + rn, ls, le, re, cmp_func, 408 &sorted_begin, &sorted_end); 409 410 // Something left? Sort it! 411 size_t remainder_length = cx_linked_list_size(re, loc_next); 412 if (remainder_length > 0) { 413 void *remainder = re; 414 cx_linked_list_sort(&remainder, NULL, loc_prev, loc_next, loc_data, cmp_func); 415 416 // merge sorted list with (also sorted) remainder 417 cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, 418 ln + rn + remainder_length, 419 sorted_begin, remainder, NULL, cmp_func, 420 &sorted_begin, &sorted_end); 421 } 422 *begin = sorted_begin; 423 if (end) *end = sorted_end; 424 } 425 } 426 427 int cx_linked_list_compare( 428 void const *begin_left, 429 void const *begin_right, 430 ptrdiff_t loc_advance, 431 ptrdiff_t loc_data, 432 cx_compare_func cmp_func 433 ) { 434 void const *left = begin_left, *right = begin_right; 435 436 while (left != NULL && right != NULL) { 437 void const *left_data = ll_data(left); 438 void const *right_data = ll_data(right); 439 int result = cmp_func(left_data, right_data); 440 if (result != 0) return result; 441 left = ll_advance(left); 442 right = ll_advance(right); 443 } 444 445 if (left != NULL) { return 1; } 446 else if (right != NULL) { return -1; } 447 else { return 0; } 448 } 449 450 void cx_linked_list_reverse( 451 void **begin, 452 void **end, 453 ptrdiff_t loc_prev, 454 ptrdiff_t loc_next 455 ) { 456 assert(begin != NULL); 457 assert(loc_next >= 0); 458 459 // swap all links 460 void *prev = NULL; 461 void *cur = *begin; 462 while (cur != NULL) { 463 void *next = ll_next(cur); 464 465 ll_next(cur) = prev; 466 if (loc_prev >= 0) { 467 ll_prev(cur) = next; 468 } 469 470 prev = cur; 471 cur = next; 472 } 473 474 // update begin and end 475 if (end != NULL) { 476 *end = *begin; 477 } 478 *begin = prev; 479 } 480 481 // HIGH LEVEL LINKED LIST IMPLEMENTATION 482 483 typedef struct cx_linked_list_node cx_linked_list_node; 484 struct cx_linked_list_node { 485 cx_linked_list_node *prev; 486 cx_linked_list_node *next; 487 char payload[]; 488 }; 489 490 #define CX_LL_LOC_PREV offsetof(cx_linked_list_node, prev) 491 #define CX_LL_LOC_NEXT offsetof(cx_linked_list_node, next) 492 #define CX_LL_LOC_DATA offsetof(cx_linked_list_node, payload) 493 494 typedef struct { 495 struct cx_list_s base; 496 cx_linked_list_node *begin; 497 cx_linked_list_node *end; 498 } cx_linked_list; 499 500 static cx_linked_list_node *cx_ll_node_at( 501 cx_linked_list const *list, 502 size_t index 503 ) { 504 if (index >= list->base.collection.size) { 505 return NULL; 506 } else if (index > list->base.collection.size / 2) { 507 return cx_linked_list_at(list->end, list->base.collection.size - 1, CX_LL_LOC_PREV, index); 508 } else { 509 return cx_linked_list_at(list->begin, 0, CX_LL_LOC_NEXT, index); 510 } 511 } 512 513 static int cx_ll_insert_at( 514 struct cx_list_s *list, 515 cx_linked_list_node *node, 516 void const *elem 517 ) { 518 519 // create the new new_node 520 cx_linked_list_node *new_node = cxMalloc(list->collection.allocator, 521 sizeof(cx_linked_list_node) + list->collection.elem_size); 522 523 // sortir if failed 524 if (new_node == NULL) return 1; 525 526 // initialize new new_node 527 new_node->prev = new_node->next = NULL; 528 memcpy(new_node->payload, elem, list->collection.elem_size); 529 530 // insert 531 cx_linked_list *ll = (cx_linked_list *) list; 532 cx_linked_list_insert_chain( 533 (void **) &ll->begin, (void **) &ll->end, 534 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, 535 node, new_node, new_node 536 ); 537 538 // increase the size and return 539 list->collection.size++; 540 return 0; 541 } 542 543 static size_t cx_ll_insert_array( 544 struct cx_list_s *list, 545 size_t index, 546 void const *array, 547 size_t n 548 ) { 549 // out-of bounds and corner case check 550 if (index > list->collection.size || n == 0) return 0; 551 552 // find position efficiently 553 cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); 554 555 // perform first insert 556 if (0 != cx_ll_insert_at(list, node, array)) { 557 return 1; 558 } 559 560 // is there more? 561 if (n == 1) return 1; 562 563 // we now know exactly where we are 564 node = node == NULL ? ((cx_linked_list *) list)->begin : node->next; 565 566 // we can add the remaining nodes and immedately advance to the inserted node 567 char const *source = array; 568 for (size_t i = 1; i < n; i++) { 569 source += list->collection.elem_size; 570 if (0 != cx_ll_insert_at(list, node, source)) { 571 return i; 572 } 573 node = node->next; 574 } 575 return n; 576 } 577 578 static int cx_ll_insert_element( 579 struct cx_list_s *list, 580 size_t index, 581 void const *element 582 ) { 583 return 1 != cx_ll_insert_array(list, index, element, 1); 584 } 585 586 static int cx_ll_remove( 587 struct cx_list_s *list, 588 size_t index 589 ) { 590 cx_linked_list *ll = (cx_linked_list *) list; 591 cx_linked_list_node *node = cx_ll_node_at(ll, index); 592 593 // out-of-bounds check 594 if (node == NULL) return 1; 595 596 // element destruction 597 cx_invoke_destructor(list, node->payload); 598 599 // remove 600 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, 601 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); 602 603 // adjust size 604 list->collection.size--; 605 606 // free and return 607 cxFree(list->collection.allocator, node); 608 609 return 0; 610 } 611 612 static void cx_ll_clear(struct cx_list_s *list) { 613 if (list->collection.size == 0) return; 614 615 cx_linked_list *ll = (cx_linked_list *) list; 616 cx_linked_list_node *node = ll->begin; 617 while (node != NULL) { 618 cx_invoke_destructor(list, node->payload); 619 cx_linked_list_node *next = node->next; 620 cxFree(list->collection.allocator, node); 621 node = next; 622 } 623 ll->begin = ll->end = NULL; 624 list->collection.size = 0; 625 } 626 627 #ifndef CX_LINKED_LIST_SWAP_SBO_SIZE 628 #define CX_LINKED_LIST_SWAP_SBO_SIZE 128 629 #endif 630 unsigned cx_linked_list_swap_sbo_size = CX_LINKED_LIST_SWAP_SBO_SIZE; 631 632 static int cx_ll_swap( 633 struct cx_list_s *list, 634 size_t i, 635 size_t j 636 ) { 637 if (i >= list->collection.size || j >= list->collection.size) return 1; 638 if (i == j) return 0; 639 640 // perform an optimized search that finds both elements in one run 641 cx_linked_list *ll = (cx_linked_list *) list; 642 size_t mid = list->collection.size / 2; 643 size_t left, right; 644 if (i < j) { 645 left = i; 646 right = j; 647 } else { 648 left = j; 649 right = i; 650 } 651 cx_linked_list_node *nleft, *nright; 652 if (left < mid && right < mid) { 653 // case 1: both items left from mid 654 nleft = cx_ll_node_at(ll, left); 655 assert(nleft != NULL); 656 nright = nleft; 657 for (size_t c = left; c < right; c++) { 658 nright = nright->next; 659 } 660 } else if (left >= mid && right >= mid) { 661 // case 2: both items right from mid 662 nright = cx_ll_node_at(ll, right); 663 assert(nright != NULL); 664 nleft = nright; 665 for (size_t c = right; c > left; c--) { 666 nleft = nleft->prev; 667 } 668 } else { 669 // case 3: one item left, one item right 670 671 // chose the closest to begin / end 672 size_t closest; 673 size_t other; 674 size_t diff2boundary = list->collection.size - right - 1; 675 if (left <= diff2boundary) { 676 closest = left; 677 other = right; 678 nleft = cx_ll_node_at(ll, left); 679 } else { 680 closest = right; 681 other = left; 682 diff2boundary = left; 683 nright = cx_ll_node_at(ll, right); 684 } 685 686 // is other element closer to us or closer to boundary? 687 if (right - left <= diff2boundary) { 688 // search other element starting from already found element 689 if (closest == left) { 690 nright = nleft; 691 for (size_t c = left; c < right; c++) { 692 nright = nright->next; 693 } 694 } else { 695 nleft = nright; 696 for (size_t c = right; c > left; c--) { 697 nleft = nleft->prev; 698 } 699 } 700 } else { 701 // search other element starting at the boundary 702 if (closest == left) { 703 nright = cx_ll_node_at(ll, other); 704 } else { 705 nleft = cx_ll_node_at(ll, other); 706 } 707 } 708 } 709 710 if (list->collection.elem_size > CX_LINKED_LIST_SWAP_SBO_SIZE) { 711 cx_linked_list_node *prev = nleft->prev; 712 cx_linked_list_node *next = nright->next; 713 cx_linked_list_node *midstart = nleft->next; 714 cx_linked_list_node *midend = nright->prev; 715 716 if (prev == NULL) { 717 ll->begin = nright; 718 } else { 719 prev->next = nright; 720 } 721 nright->prev = prev; 722 if (midstart == nright) { 723 // special case: both nodes are adjacent 724 nright->next = nleft; 725 nleft->prev = nright; 726 } else { 727 // likely case: a chain is between the two nodes 728 nright->next = midstart; 729 midstart->prev = nright; 730 midend->next = nleft; 731 nleft->prev = midend; 732 } 733 nleft->next = next; 734 if (next == NULL) { 735 ll->end = nleft; 736 } else { 737 next->prev = nleft; 738 } 739 } else { 740 // swap payloads to avoid relinking 741 char buf[CX_LINKED_LIST_SWAP_SBO_SIZE]; 742 memcpy(buf, nleft->payload, list->collection.elem_size); 743 memcpy(nleft->payload, nright->payload, list->collection.elem_size); 744 memcpy(nright->payload, buf, list->collection.elem_size); 745 } 746 747 return 0; 748 } 749 750 static void *cx_ll_at( 751 struct cx_list_s const *list, 752 size_t index 753 ) { 754 cx_linked_list *ll = (cx_linked_list *) list; 755 cx_linked_list_node *node = cx_ll_node_at(ll, index); 756 return node == NULL ? NULL : node->payload; 757 } 758 759 static ssize_t cx_ll_find_remove( 760 struct cx_list_s *list, 761 void const *elem, 762 bool remove 763 ) { 764 if (remove) { 765 cx_linked_list *ll = ((cx_linked_list *) list); 766 cx_linked_list_node *node; 767 ssize_t index = cx_linked_list_find_node( 768 (void **) &node, 769 ll->begin, 770 CX_LL_LOC_NEXT, CX_LL_LOC_DATA, 771 list->collection.cmpfunc, elem 772 ); 773 if (node != NULL) { 774 cx_invoke_destructor(list, node->payload); 775 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, 776 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); 777 list->collection.size--; 778 cxFree(list->collection.allocator, node); 779 } 780 return index; 781 } else { 782 return cx_linked_list_find( 783 ((cx_linked_list *) list)->begin, 784 CX_LL_LOC_NEXT, CX_LL_LOC_DATA, 785 list->collection.cmpfunc, elem 786 ); 787 } 788 } 789 790 static void cx_ll_sort(struct cx_list_s *list) { 791 cx_linked_list *ll = (cx_linked_list *) list; 792 cx_linked_list_sort((void **) &ll->begin, (void **) &ll->end, 793 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, CX_LL_LOC_DATA, 794 list->collection.cmpfunc); 795 } 796 797 static void cx_ll_reverse(struct cx_list_s *list) { 798 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); 800 } 801 802 static int cx_ll_compare( 803 struct cx_list_s const *list, 804 struct cx_list_s const *other 805 ) { 806 cx_linked_list *left = (cx_linked_list *) list; 807 cx_linked_list *right = (cx_linked_list *) other; 808 return cx_linked_list_compare(left->begin, right->begin, 809 CX_LL_LOC_NEXT, CX_LL_LOC_DATA, 810 list->collection.cmpfunc); 811 } 812 813 static bool cx_ll_iter_valid(void const *it) { 814 struct cx_iterator_s const *iter = it; 815 return iter->elem_handle != NULL; 816 } 817 818 static void cx_ll_iter_next(void *it) { 819 struct cx_iterator_s *iter = it; 820 if (iter->base.remove) { 821 iter->base.remove = false; 822 struct cx_list_s *list = iter->src_handle.m; 823 cx_linked_list *ll = iter->src_handle.m; 824 cx_linked_list_node *node = iter->elem_handle; 825 iter->elem_handle = node->next; 826 cx_invoke_destructor(list, node->payload); 827 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, 828 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); 829 list->collection.size--; 830 cxFree(list->collection.allocator, node); 831 } else { 832 iter->index++; 833 cx_linked_list_node *node = iter->elem_handle; 834 iter->elem_handle = node->next; 835 } 836 } 837 838 static void cx_ll_iter_prev(void *it) { 839 struct cx_iterator_s *iter = it; 840 if (iter->base.remove) { 841 iter->base.remove = false; 842 struct cx_list_s *list = iter->src_handle.m; 843 cx_linked_list *ll = iter->src_handle.m; 844 cx_linked_list_node *node = iter->elem_handle; 845 iter->elem_handle = node->prev; 846 iter->index--; 847 cx_invoke_destructor(list, node->payload); 848 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, 849 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); 850 list->collection.size--; 851 cxFree(list->collection.allocator, node); 852 } else { 853 iter->index--; 854 cx_linked_list_node *node = iter->elem_handle; 855 iter->elem_handle = node->prev; 856 } 857 } 858 859 static void *cx_ll_iter_current(void const *it) { 860 struct cx_iterator_s const *iter = it; 861 cx_linked_list_node *node = iter->elem_handle; 862 return node->payload; 863 } 864 865 static CxIterator cx_ll_iterator( 866 struct cx_list_s const *list, 867 size_t index, 868 bool backwards 869 ) { 870 CxIterator iter; 871 iter.index = index; 872 iter.src_handle.c = list; 873 iter.elem_handle = cx_ll_node_at((cx_linked_list const *) list, index); 874 iter.elem_size = list->collection.elem_size; 875 iter.elem_count = list->collection.size; 876 iter.base.valid = cx_ll_iter_valid; 877 iter.base.current = cx_ll_iter_current; 878 iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next; 879 iter.base.mutating = false; 880 iter.base.remove = false; 881 return iter; 882 } 883 884 static int cx_ll_insert_iter( 885 CxIterator *iter, 886 void const *elem, 887 int prepend 888 ) { 889 struct cx_list_s *list = iter->src_handle.m; 890 cx_linked_list_node *node = iter->elem_handle; 891 if (node != NULL) { 892 assert(prepend >= 0 && prepend <= 1); 893 cx_linked_list_node *choice[2] = {node, node->prev}; 894 int result = cx_ll_insert_at(list, choice[prepend], elem); 895 iter->index += prepend * (0 == result); 896 return result; 897 } else { 898 int result = cx_ll_insert_element(list, list->collection.size, elem); 899 iter->index = list->collection.size; 900 return result; 901 } 902 } 903 904 static void cx_ll_destructor(CxList *list) { 905 cx_linked_list *ll = (cx_linked_list *) list; 906 907 cx_linked_list_node *node = ll->begin; 908 while (node) { 909 cx_invoke_destructor(list, node->payload); 910 void *next = node->next; 911 cxFree(list->collection.allocator, node); 912 node = next; 913 } 914 915 cxFree(list->collection.allocator, list); 916 } 917 918 static cx_list_class cx_linked_list_class = { 919 cx_ll_destructor, 920 cx_ll_insert_element, 921 cx_ll_insert_array, 922 cx_ll_insert_iter, 923 cx_ll_remove, 924 cx_ll_clear, 925 cx_ll_swap, 926 cx_ll_at, 927 cx_ll_find_remove, 928 cx_ll_sort, 929 cx_ll_compare, 930 cx_ll_reverse, 931 cx_ll_iterator, 932 }; 933 934 CxList *cxLinkedListCreate( 935 CxAllocator const *allocator, 936 cx_compare_func comparator, 937 size_t elem_size 938 ) { 939 if (allocator == NULL) { 940 allocator = cxDefaultAllocator; 941 } 942 943 cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list)); 944 if (list == NULL) return NULL; 945 946 list->base.cl = &cx_linked_list_class; 947 list->base.collection.allocator = allocator; 948 949 if (elem_size > 0) { 950 list->base.collection.elem_size = elem_size; 951 list->base.collection.cmpfunc = comparator; 952 } else { 953 list->base.collection.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator; 954 cxListStorePointers((CxList *) list); 955 } 956 957 return (CxList *) list; 958 } 959