diff -r dfd0403ff8b6 -r 2e6e9d9f2159 src/linked_list.c --- a/src/linked_list.c Wed Feb 08 18:56:58 2023 +0100 +++ b/src/linked_list.c Wed Feb 08 20:26:09 2023 +0100 @@ -451,6 +451,8 @@ // HIGH LEVEL LINKED LIST IMPLEMENTATION +bool CX_DISABLE_LINKED_LIST_SWAP_SBO = false; + typedef struct cx_linked_list_node cx_linked_list_node; struct cx_linked_list_node { cx_linked_list_node *prev; @@ -577,6 +579,126 @@ return 0; } +#ifndef CX_LINKED_LIST_SWAP_SBO_SIZE +#define CX_LINKED_LIST_SWAP_SBO_SIZE 16 +#endif + +static int cx_ll_swap( + struct cx_list_s *list, + size_t i, + size_t j +) { + if (i >= list->size || j >= list->size) return 1; + if (i == j) return 0; + + // perform an optimized search that finds both elements in one run + cx_linked_list *ll = (cx_linked_list *) list; + size_t mid = list->size / 2; + size_t left, right; + if (i < j) { + left = i; + right = j; + } else { + left = j; + right = i; + } + cx_linked_list_node *nleft, *nright; + if (left < mid && right < mid) { + // case 1: both items left from mid + nleft = cx_ll_node_at(ll, left); + nright = nleft; + for (size_t c = left; c < right; c++) { + nright = nright->next; + } + } else if (left >= mid && right >= mid) { + // case 2: both items right from mid + nright = cx_ll_node_at(ll, right); + nleft = nright; + for (size_t c = right; c > left; c--) { + nleft = nleft->prev; + } + } else { + // case 3: one item left, one item right + + // chose the closest to begin / end + size_t closest; + size_t other; + size_t diff2boundary = list->size - right - 1; + if (left <= diff2boundary) { + closest = left; + other = right; + nleft = cx_ll_node_at(ll, left); + } else { + closest = right; + other = left; + diff2boundary = left; + nright = cx_ll_node_at(ll, right); + } + + // is other element closer to us or closer to boundary? + if (right - left <= diff2boundary) { + // search other element starting from already found element + if (closest == left) { + nright = nleft; + for (size_t c = left; c < right; c++) { + nright = nright->next; + } + } else { + nleft = nright; + for (size_t c = right; c > left; c--) { + nleft = nleft->prev; + } + } + } else { + // search other element starting at the boundary + if (closest == left) { + nright = cx_ll_node_at(ll, other); + } else { + nleft = cx_ll_node_at(ll, other); + } + } + } + + if (list->itemsize > CX_LINKED_LIST_SWAP_SBO_SIZE || CX_DISABLE_LINKED_LIST_SWAP_SBO) { + cx_linked_list_node *prev = nleft->prev; + cx_linked_list_node *next = nright->next; + cx_linked_list_node *midstart = nleft->next; + cx_linked_list_node *midend = nright->prev; + + if (prev == NULL) { + ll->begin = nright; + } else { + prev->next = nright; + } + nright->prev = prev; + if (midstart == nright) { + // special case: both nodes are adjacent + nright->next = nleft; + nleft->prev = nright; + } else { + // likely case: a chain is between the two nodes + nright->next = midstart; + midstart->prev = nright; + midend->next = nleft; + nleft->prev = midend; + } + nleft->next = next; + if (next == NULL) { + ll->end = nleft; + } else { + next->prev = nleft; + } + } else { + // swap payloads to avoid relinking + char buf[CX_LINKED_LIST_SWAP_SBO_SIZE]; + memcpy(buf, nleft->payload, list->itemsize); + memcpy(nleft->payload, nright->payload, list->itemsize); + memcpy(nright->payload, buf, list->itemsize); + } + + return 0; +} + static void *cx_ll_at( struct cx_list_s const *list, size_t index @@ -714,6 +836,7 @@ cx_ll_insert_array, cx_ll_insert_iter, cx_ll_remove, + cx_ll_swap, cx_ll_at, cx_ll_find, cx_ll_sort,