| 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 // IMPORTANT: the layout must stay exactly like this, because kv_list.c uses that! |
|
| 569 struct cx_linked_list_node { |
|
| 570 cx_linked_list_node *prev; |
|
| 571 cx_linked_list_node *next; |
|
| 572 char payload[]; |
|
| 573 }; |
|
| 574 |
|
| 575 #define CX_LL_LOC_PREV offsetof(cx_linked_list_node, prev) |
|
| 576 #define CX_LL_LOC_NEXT offsetof(cx_linked_list_node, next) |
|
| 577 #define CX_LL_LOC_DATA offsetof(cx_linked_list_node, payload) |
|
| 578 |
|
| 579 typedef struct { |
|
| 580 struct cx_list_s base; |
|
| 581 cx_linked_list_node *begin; |
|
| 582 cx_linked_list_node *end; |
|
| 583 } cx_linked_list; |
|
| 584 |
|
| 585 static cx_linked_list_node *cx_ll_node_at( |
|
| 586 const cx_linked_list *list, |
682 const cx_linked_list *list, |
| 587 size_t index |
683 size_t index |
| 588 ) { |
684 ) { |
| 589 if (index >= list->base.collection.size) { |
685 if (index >= list->base.collection.size) { |
| 590 return NULL; |
686 return NULL; |
| 591 } else if (index > list->base.collection.size / 2) { |
687 } else if (index > list->base.collection.size / 2) { |
| 592 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); |
| 593 } else { |
689 } else { |
| 594 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); |
| 595 } |
691 } |
| 596 } |
692 } |
| 597 |
693 |
| 598 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) { |
| 599 return cxMalloc(list->collection.allocator, |
695 return cxZalloc(list->base.collection.allocator, |
| 600 sizeof(cx_linked_list_node) + list->collection.elem_size); |
696 list->loc_data + list->base.collection.elem_size + list->extra_data_len); |
| 601 } |
697 } |
| 602 |
698 |
| 603 static int cx_ll_insert_at( |
699 static int cx_ll_insert_at( |
| 604 struct cx_list_s *list, |
700 struct cx_list_s *list, |
| 605 cx_linked_list_node *node, |
701 void *node, |
| 606 const void *elem |
702 const void *elem |
| 607 ) { |
703 ) { |
| |
704 cx_linked_list *ll = (cx_linked_list *) list; |
| 608 |
705 |
| 609 // create the new new_node |
706 // create the new new_node |
| 610 cx_linked_list_node *new_node = cx_ll_malloc_node(list); |
707 void *new_node = cx_ll_malloc_node(ll); |
| 611 |
708 |
| 612 // sortir if failed |
709 // sortir if failed |
| 613 if (new_node == NULL) return 1; |
710 if (new_node == NULL) return 1; |
| 614 |
711 |
| 615 // initialize new new_node |
712 // copy the data |
| 616 new_node->prev = new_node->next = NULL; |
|
| 617 if (elem != NULL) { |
713 if (elem != NULL) { |
| 618 memcpy(new_node->payload, elem, list->collection.elem_size); |
714 memcpy((char*)new_node + ll->loc_data, elem, list->collection.elem_size); |
| 619 } |
715 } |
| 620 |
716 |
| 621 // insert |
717 // insert |
| 622 cx_linked_list *ll = (cx_linked_list *) list; |
|
| 623 cx_linked_list_insert_chain( |
718 cx_linked_list_insert_chain( |
| 624 (void **) &ll->begin, (void **) &ll->end, |
719 &ll->begin, &ll->end, |
| 625 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, |
720 ll->loc_prev, ll->loc_next, |
| 626 node, new_node, new_node |
721 node, new_node, new_node |
| 627 ); |
722 ); |
| 628 |
723 |
| 629 // increase the size and return |
724 // increase the size and return |
| 630 list->collection.size++; |
725 list->collection.size++; |
| 669 ) { |
767 ) { |
| 670 // out-of-bounds check |
768 // out-of-bounds check |
| 671 if (index > list->collection.size) return NULL; |
769 if (index > list->collection.size) return NULL; |
| 672 |
770 |
| 673 // find position efficiently |
771 // find position efficiently |
| 674 cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); |
772 void *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); |
| 675 |
773 |
| 676 // perform first insert |
774 // perform first insert |
| 677 if (cx_ll_insert_at(list, node, element)) return NULL; |
775 if (cx_ll_insert_at(list, node, element)) return NULL; |
| 678 |
776 |
| 679 // return a pointer to the data of the inserted node |
777 // return a pointer to the data of the inserted node |
| |
778 cx_linked_list *ll = (cx_linked_list *) list; |
| 680 if (node == NULL) { |
779 if (node == NULL) { |
| 681 return ((cx_linked_list *) list)->begin->payload; |
780 return (char*)(ll->begin) + ll->loc_data; |
| 682 } else { |
781 } else { |
| 683 return node->next->payload; |
782 char *next = CX_LL_PTR(node, ll->loc_next); |
| |
783 return next + ll->loc_data; |
| 684 } |
784 } |
| 685 } |
785 } |
| 686 |
786 |
| 687 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; |
| 688 |
789 |
| 689 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) { |
| 690 const cx_linked_list_node *left = l; |
791 const char *left = (const char*)l + cx_ll_insert_sorted_loc_data; |
| 691 const cx_linked_list_node *right = r; |
792 const char *right = (const char*)r + cx_ll_insert_sorted_loc_data; |
| 692 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; |
| 693 } |
861 } |
| 694 |
862 |
| 695 static size_t cx_ll_insert_sorted( |
863 static size_t cx_ll_insert_sorted( |
| 696 struct cx_list_s *list, |
864 struct cx_list_s *list, |
| 697 const void *array, |
865 const void *array, |
| 698 size_t n |
866 size_t n |
| 699 ) { |
867 ) { |
| 700 // special case |
868 return cx_ll_insert_sorted_impl(list, array, n, true); |
| 701 if (n == 0) return 0; |
869 } |
| 702 |
870 |
| 703 // create a new chain of nodes |
871 static size_t cx_ll_insert_unique( |
| 704 cx_linked_list_node *chain = cx_ll_malloc_node(list); |
872 struct cx_list_s *list, |
| 705 if (chain == NULL) return 0; |
873 const void *array, |
| 706 |
874 size_t n |
| 707 memcpy(chain->payload, array, list->collection.elem_size); |
875 ) { |
| 708 chain->prev = NULL; |
876 return cx_ll_insert_sorted_impl(list, array, n, false); |
| 709 chain->next = NULL; |
|
| 710 |
|
| 711 // add all elements from the array to that chain |
|
| 712 cx_linked_list_node *prev = chain; |
|
| 713 const char *src = array; |
|
| 714 size_t inserted = 1; |
|
| 715 for (; inserted < n; inserted++) { |
|
| 716 cx_linked_list_node *next = cx_ll_malloc_node(list); |
|
| 717 if (next == NULL) break; |
|
| 718 src += list->collection.elem_size; |
|
| 719 memcpy(next->payload, src, list->collection.elem_size); |
|
| 720 prev->next = next; |
|
| 721 next->prev = prev; |
|
| 722 prev = next; |
|
| 723 } |
|
| 724 prev->next = NULL; |
|
| 725 |
|
| 726 // invoke the low level function |
|
| 727 cx_linked_list *ll = (cx_linked_list *) list; |
|
| 728 cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc; |
|
| 729 cx_linked_list_insert_sorted_chain( |
|
| 730 (void **) &ll->begin, |
|
| 731 (void **) &ll->end, |
|
| 732 CX_LL_LOC_PREV, |
|
| 733 CX_LL_LOC_NEXT, |
|
| 734 chain, |
|
| 735 cx_ll_insert_sorted_cmp_helper |
|
| 736 ); |
|
| 737 |
|
| 738 // adjust the list metadata |
|
| 739 list->collection.size += inserted; |
|
| 740 |
|
| 741 return inserted; |
|
| 742 } |
877 } |
| 743 |
878 |
| 744 static size_t cx_ll_remove( |
879 static size_t cx_ll_remove( |
| 745 struct cx_list_s *list, |
880 struct cx_list_s *list, |
| 746 size_t index, |
881 size_t index, |
| 747 size_t num, |
882 size_t num, |
| 748 void *targetbuf |
883 void *targetbuf |
| 749 ) { |
884 ) { |
| 750 cx_linked_list *ll = (cx_linked_list *) list; |
885 cx_linked_list *ll = (cx_linked_list *) list; |
| 751 cx_linked_list_node *node = cx_ll_node_at(ll, index); |
886 void *node = cx_ll_node_at(ll, index); |
| 752 |
887 |
| 753 // out-of-bounds check |
888 // out-of-bounds check |
| 754 if (node == NULL) return 0; |
889 if (node == NULL) return 0; |
| 755 |
890 |
| 756 // remove |
891 // remove |
| 757 size_t removed = cx_linked_list_remove_chain( |
892 size_t removed = cx_linked_list_remove_chain( |
| 758 (void **) &ll->begin, |
893 (void **) &ll->begin, |
| 759 (void **) &ll->end, |
894 (void **) &ll->end, |
| 760 CX_LL_LOC_PREV, |
895 ll->loc_prev, |
| 761 CX_LL_LOC_NEXT, |
896 ll->loc_next, |
| 762 node, |
897 node, |
| 763 num |
898 num |
| 764 ); |
899 ); |
| 765 |
900 |
| 766 // adjust size |
901 // adjust size |
| 767 list->collection.size -= removed; |
902 list->collection.size -= removed; |
| 768 |
903 |
| 769 // copy or destroy the removed chain |
904 // copy or destroy the removed chain |
| 770 if (targetbuf == NULL) { |
905 if (targetbuf == NULL) { |
| 771 cx_linked_list_node *n = node; |
906 char *n = node; |
| 772 for (size_t i = 0; i < removed; i++) { |
907 for (size_t i = 0; i < removed; i++) { |
| 773 // element destruction |
908 // element destruction |
| 774 cx_invoke_destructor(list, n->payload); |
909 cx_invoke_destructor(list, n + ll->loc_data); |
| 775 |
910 |
| 776 // free the node and advance |
911 // free the node and advance |
| 777 void *next = n->next; |
912 void *next = CX_LL_PTR(n, ll->loc_next); |
| 778 cxFree(list->collection.allocator, n); |
913 cxFree(list->collection.allocator, n); |
| 779 n = next; |
914 n = next; |
| 780 } |
915 } |
| 781 } else { |
916 } else { |
| 782 char *dest = targetbuf; |
917 char *dest = targetbuf; |
| 783 cx_linked_list_node *n = node; |
918 char *n = node; |
| 784 for (size_t i = 0; i < removed; i++) { |
919 for (size_t i = 0; i < removed; i++) { |
| 785 // copy payload |
920 // copy payload |
| 786 memcpy(dest, n->payload, list->collection.elem_size); |
921 memcpy(dest, n + ll->loc_data, list->collection.elem_size); |
| 787 |
922 |
| 788 // advance target buffer |
923 // advance target buffer |
| 789 dest += list->collection.elem_size; |
924 dest += list->collection.elem_size; |
| 790 |
925 |
| 791 // free the node and advance |
926 // free the node and advance |
| 792 void *next = n->next; |
927 void *next = CX_LL_PTR(n, ll->loc_next); |
| 793 cxFree(list->collection.allocator, n); |
928 cxFree(list->collection.allocator, n); |
| 794 n = next; |
929 n = next; |
| 795 } |
930 } |
| 796 } |
931 } |
| 797 |
932 |
| 889 nleft = cx_ll_node_at(ll, other); |
1024 nleft = cx_ll_node_at(ll, other); |
| 890 } |
1025 } |
| 891 } |
1026 } |
| 892 } |
1027 } |
| 893 |
1028 |
| 894 cx_linked_list_node *prev = nleft->prev; |
1029 void *prev = CX_LL_PTR(nleft, ll->loc_prev); |
| 895 cx_linked_list_node *next = nright->next; |
1030 void *next = CX_LL_PTR(nright, ll->loc_next); |
| 896 cx_linked_list_node *midstart = nleft->next; |
1031 void *midstart = CX_LL_PTR(nleft, ll->loc_next); |
| 897 cx_linked_list_node *midend = nright->prev; |
1032 void *midend = CX_LL_PTR(nright, ll->loc_prev); |
| 898 |
1033 |
| 899 if (prev == NULL) { |
1034 if (prev == NULL) { |
| 900 ll->begin = nright; |
1035 ll->begin = nright; |
| 901 } else { |
1036 } else { |
| 902 prev->next = nright; |
1037 CX_LL_PTR(prev, ll->loc_next) = nright; |
| 903 } |
1038 } |
| 904 nright->prev = prev; |
1039 CX_LL_PTR(nright, ll->loc_prev) = prev; |
| 905 if (midstart == nright) { |
1040 if (midstart == nright) { |
| 906 // special case: both nodes are adjacent |
1041 // special case: both nodes are adjacent |
| 907 nright->next = nleft; |
1042 CX_LL_PTR(nright, ll->loc_next) = nleft; |
| 908 nleft->prev = nright; |
1043 CX_LL_PTR(nleft, ll->loc_prev) = nright; |
| 909 } else { |
1044 } else { |
| 910 // likely case: a chain is between the two nodes |
1045 // likely case: a chain is between the two nodes |
| 911 nright->next = midstart; |
1046 CX_LL_PTR(nright, ll->loc_next) = midstart; |
| 912 midstart->prev = nright; |
1047 CX_LL_PTR(midstart, ll->loc_prev) = nright; |
| 913 midend->next = nleft; |
1048 CX_LL_PTR(midend, ll->loc_next) = nleft; |
| 914 nleft->prev = midend; |
1049 CX_LL_PTR(nleft, ll->loc_prev) = midend; |
| 915 } |
1050 } |
| 916 nleft->next = next; |
1051 CX_LL_PTR(nleft, ll->loc_next) = next; |
| 917 if (next == NULL) { |
1052 if (next == NULL) { |
| 918 ll->end = nleft; |
1053 ll->end = nleft; |
| 919 } else { |
1054 } else { |
| 920 next->prev = nleft; |
1055 CX_LL_PTR(next, ll->loc_prev) = nleft; |
| 921 } |
1056 } |
| 922 |
1057 |
| 923 return 0; |
1058 return 0; |
| 924 } |
1059 } |
| 925 |
1060 |
| 926 static void *cx_ll_at( |
1061 static void *cx_ll_at( |
| 927 const struct cx_list_s *list, |
1062 const struct cx_list_s *list, |
| 928 size_t index |
1063 size_t index |
| 929 ) { |
1064 ) { |
| 930 cx_linked_list *ll = (cx_linked_list *) list; |
1065 cx_linked_list *ll = (cx_linked_list *) list; |
| 931 cx_linked_list_node *node = cx_ll_node_at(ll, index); |
1066 char *node = cx_ll_node_at(ll, index); |
| 932 return node == NULL ? NULL : node->payload; |
1067 return node == NULL ? NULL : node + ll->loc_data; |
| 933 } |
1068 } |
| 934 |
1069 |
| 935 static size_t cx_ll_find_remove( |
1070 static size_t cx_ll_find_remove( |
| 936 struct cx_list_s *list, |
1071 struct cx_list_s *list, |
| 937 const void *elem, |
1072 const void *elem, |
| 938 bool remove |
1073 bool remove |
| 939 ) { |
1074 ) { |
| 940 if (list->collection.size == 0) return 0; |
1075 if (list->collection.size == 0) return 0; |
| 941 |
1076 |
| 942 size_t index; |
1077 size_t index; |
| 943 cx_linked_list *ll = ((cx_linked_list *) list); |
1078 cx_linked_list *ll = (cx_linked_list *) list; |
| 944 cx_linked_list_node *node = cx_linked_list_find( |
1079 char *node = cx_linked_list_find( |
| 945 ll->begin, |
1080 ll->begin, |
| 946 CX_LL_LOC_NEXT, CX_LL_LOC_DATA, |
1081 ll->loc_next, ll->loc_data, |
| 947 list->collection.cmpfunc, elem, |
1082 list->collection.cmpfunc, elem, |
| 948 &index |
1083 &index |
| 949 ); |
1084 ); |
| 950 if (node == NULL) { |
1085 if (node == NULL) { |
| 951 return list->collection.size; |
1086 return list->collection.size; |
| 952 } |
1087 } |
| 953 if (remove) { |
1088 if (remove) { |
| 954 cx_invoke_destructor(list, node->payload); |
1089 cx_invoke_destructor(list, node + ll->loc_data); |
| 955 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, |
1090 cx_linked_list_remove(&ll->begin, &ll->end, |
| 956 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); |
1091 ll->loc_prev, ll->loc_next, node); |
| 957 list->collection.size--; |
1092 list->collection.size--; |
| 958 cxFree(list->collection.allocator, node); |
1093 cxFree(list->collection.allocator, node); |
| 959 } |
1094 } |
| 960 return index; |
1095 return index; |
| 961 } |
1096 } |
| 962 |
1097 |
| 963 static void cx_ll_sort(struct cx_list_s *list) { |
1098 static void cx_ll_sort(struct cx_list_s *list) { |
| 964 cx_linked_list *ll = (cx_linked_list *) list; |
1099 cx_linked_list *ll = (cx_linked_list *) list; |
| 965 cx_linked_list_sort((void **) &ll->begin, (void **) &ll->end, |
1100 cx_linked_list_sort(&ll->begin, &ll->end, |
| 966 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, CX_LL_LOC_DATA, |
1101 ll->loc_prev, ll->loc_next, ll->loc_data, |
| 967 list->collection.cmpfunc); |
1102 list->collection.cmpfunc); |
| 968 } |
1103 } |
| 969 |
1104 |
| 970 static void cx_ll_reverse(struct cx_list_s *list) { |
1105 static void cx_ll_reverse(struct cx_list_s *list) { |
| 971 cx_linked_list *ll = (cx_linked_list *) list; |
1106 cx_linked_list *ll = (cx_linked_list *) list; |
| 972 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); |
| 973 } |
1108 } |
| 974 |
1109 |
| 975 static int cx_ll_compare( |
1110 static int cx_ll_compare( |
| 976 const struct cx_list_s *list, |
1111 const struct cx_list_s *list, |
| 977 const struct cx_list_s *other |
1112 const struct cx_list_s *other |
| 978 ) { |
1113 ) { |
| 979 cx_linked_list *left = (cx_linked_list *) list; |
1114 cx_linked_list *left = (cx_linked_list *) list; |
| 980 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); |
| 981 return cx_linked_list_compare(left->begin, right->begin, |
1118 return cx_linked_list_compare(left->begin, right->begin, |
| 982 CX_LL_LOC_NEXT, CX_LL_LOC_DATA, |
1119 left->loc_next, left->loc_data, |
| 983 list->collection.cmpfunc); |
1120 list->collection.cmpfunc); |
| 984 } |
1121 } |
| 985 |
1122 |
| 986 static bool cx_ll_iter_valid(const void *it) { |
1123 static bool cx_ll_iter_valid(const void *it) { |
| 987 const struct cx_iterator_s *iter = it; |
1124 const struct cx_iterator_s *iter = it; |
| 988 return iter->elem_handle != NULL; |
1125 return iter->elem_handle != NULL; |
| 989 } |
1126 } |
| 990 |
1127 |
| 991 static void cx_ll_iter_next(void *it) { |
1128 static void cx_ll_iter_next(void *it) { |
| 992 struct cx_iterator_s *iter = it; |
1129 struct cx_iterator_s *iter = it; |
| |
1130 cx_linked_list *ll = iter->src_handle; |
| 993 if (iter->base.remove) { |
1131 if (iter->base.remove) { |
| 994 iter->base.remove = false; |
1132 iter->base.remove = false; |
| 995 struct cx_list_s *list = iter->src_handle.m; |
1133 struct cx_list_s *list = iter->src_handle; |
| 996 cx_linked_list *ll = iter->src_handle.m; |
1134 char *node = iter->elem_handle; |
| 997 cx_linked_list_node *node = iter->elem_handle; |
1135 iter->elem_handle = CX_LL_PTR(node, ll->loc_next); |
| 998 iter->elem_handle = node->next; |
1136 cx_invoke_destructor(list, node + ll->loc_data); |
| 999 cx_invoke_destructor(list, node->payload); |
1137 cx_linked_list_remove(&ll->begin, &ll->end, |
| 1000 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, |
1138 ll->loc_prev, ll->loc_next, node); |
| 1001 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); |
|
| 1002 list->collection.size--; |
1139 list->collection.size--; |
| |
1140 iter->elem_count--; |
| 1003 cxFree(list->collection.allocator, node); |
1141 cxFree(list->collection.allocator, node); |
| 1004 } else { |
1142 } else { |
| 1005 iter->index++; |
1143 iter->index++; |
| 1006 cx_linked_list_node *node = iter->elem_handle; |
1144 void *node = iter->elem_handle; |
| 1007 iter->elem_handle = node->next; |
1145 iter->elem_handle = CX_LL_PTR(node, ll->loc_next); |
| 1008 } |
1146 } |
| 1009 } |
1147 } |
| 1010 |
1148 |
| 1011 static void cx_ll_iter_prev(void *it) { |
1149 static void cx_ll_iter_prev(void *it) { |
| 1012 struct cx_iterator_s *iter = it; |
1150 struct cx_iterator_s *iter = it; |
| |
1151 cx_linked_list *ll = iter->src_handle; |
| 1013 if (iter->base.remove) { |
1152 if (iter->base.remove) { |
| 1014 iter->base.remove = false; |
1153 iter->base.remove = false; |
| 1015 struct cx_list_s *list = iter->src_handle.m; |
1154 struct cx_list_s *list = iter->src_handle; |
| 1016 cx_linked_list *ll = iter->src_handle.m; |
1155 char *node = iter->elem_handle; |
| 1017 cx_linked_list_node *node = iter->elem_handle; |
1156 iter->elem_handle = CX_LL_PTR(node, ll->loc_prev); |
| 1018 iter->elem_handle = node->prev; |
|
| 1019 iter->index--; |
1157 iter->index--; |
| 1020 cx_invoke_destructor(list, node->payload); |
1158 cx_invoke_destructor(list, node + ll->loc_data); |
| 1021 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, |
1159 cx_linked_list_remove(&ll->begin, &ll->end, |
| 1022 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); |
1160 ll->loc_prev, ll->loc_next, node); |
| 1023 list->collection.size--; |
1161 list->collection.size--; |
| |
1162 iter->elem_count--; |
| 1024 cxFree(list->collection.allocator, node); |
1163 cxFree(list->collection.allocator, node); |
| 1025 } else { |
1164 } else { |
| 1026 iter->index--; |
1165 iter->index--; |
| 1027 cx_linked_list_node *node = iter->elem_handle; |
1166 char *node = iter->elem_handle; |
| 1028 iter->elem_handle = node->prev; |
1167 iter->elem_handle = CX_LL_PTR(node, ll->loc_prev); |
| 1029 } |
1168 } |
| 1030 } |
1169 } |
| 1031 |
1170 |
| 1032 static void *cx_ll_iter_current(const void *it) { |
1171 static void *cx_ll_iter_current(const void *it) { |
| 1033 const struct cx_iterator_s *iter = it; |
1172 const struct cx_iterator_s *iter = it; |
| 1034 cx_linked_list_node *node = iter->elem_handle; |
1173 const cx_linked_list *ll = iter->src_handle; |
| 1035 return node->payload; |
1174 char *node = iter->elem_handle; |
| |
1175 return node + ll->loc_data; |
| 1036 } |
1176 } |
| 1037 |
1177 |
| 1038 static CxIterator cx_ll_iterator( |
1178 static CxIterator cx_ll_iterator( |
| 1039 const struct cx_list_s *list, |
1179 const struct cx_list_s *list, |
| 1040 size_t index, |
1180 size_t index, |
| 1041 bool backwards |
1181 bool backwards |
| 1042 ) { |
1182 ) { |
| 1043 CxIterator iter; |
1183 CxIterator iter; |
| 1044 iter.index = index; |
1184 iter.index = index; |
| 1045 iter.src_handle.c = list; |
1185 iter.src_handle = (void*)list; |
| 1046 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); |
| 1047 iter.elem_size = list->collection.elem_size; |
1187 iter.elem_size = list->collection.elem_size; |
| 1048 iter.elem_count = list->collection.size; |
1188 iter.elem_count = list->collection.size; |
| 1049 iter.base.valid = cx_ll_iter_valid; |
1189 iter.base.valid = cx_ll_iter_valid; |
| 1050 iter.base.current = cx_ll_iter_current; |
1190 iter.base.current = cx_ll_iter_current; |
| 1051 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; |
| 1052 iter.base.mutating = false; |
1192 iter.base.allow_remove = true; |
| 1053 iter.base.remove = false; |
1193 iter.base.remove = false; |
| 1054 return iter; |
1194 return iter; |
| 1055 } |
1195 } |
| 1056 |
1196 |
| 1057 static int cx_ll_insert_iter( |
1197 static int cx_ll_insert_iter( |
| 1058 CxIterator *iter, |
1198 CxIterator *iter, |
| 1059 const void *elem, |
1199 const void *elem, |
| 1060 int prepend |
1200 int prepend |
| 1061 ) { |
1201 ) { |
| 1062 struct cx_list_s *list = iter->src_handle.m; |
1202 struct cx_list_s *list = iter->src_handle; |
| 1063 cx_linked_list_node *node = iter->elem_handle; |
1203 cx_linked_list *ll = iter->src_handle; |
| |
1204 void *node = iter->elem_handle; |
| 1064 if (node != NULL) { |
1205 if (node != NULL) { |
| 1065 assert(prepend >= 0 && prepend <= 1); |
1206 assert(prepend >= 0 && prepend <= 1); |
| 1066 cx_linked_list_node *choice[2] = {node, node->prev}; |
1207 void *choice[2] = {node, CX_LL_PTR(node, ll->loc_prev)}; |
| 1067 int result = cx_ll_insert_at(list, choice[prepend], elem); |
1208 int result = cx_ll_insert_at(list, choice[prepend], elem); |
| 1068 if (result == 0) { |
1209 if (result == 0) { |
| 1069 iter->elem_count++; |
1210 iter->elem_count++; |
| 1070 if (prepend) { |
1211 if (prepend) { |
| 1071 iter->index++; |
1212 iter->index++; |