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