src/linked_list.c

changeset 647
2e6e9d9f2159
parent 641
d402fead3386
child 654
c9d008861178
     1.1 --- a/src/linked_list.c	Wed Feb 08 18:56:58 2023 +0100
     1.2 +++ b/src/linked_list.c	Wed Feb 08 20:26:09 2023 +0100
     1.3 @@ -451,6 +451,8 @@
     1.4  
     1.5  // HIGH LEVEL LINKED LIST IMPLEMENTATION
     1.6  
     1.7 +bool CX_DISABLE_LINKED_LIST_SWAP_SBO = false;
     1.8 +
     1.9  typedef struct cx_linked_list_node cx_linked_list_node;
    1.10  struct cx_linked_list_node {
    1.11      cx_linked_list_node *prev;
    1.12 @@ -577,6 +579,126 @@
    1.13      return 0;
    1.14  }
    1.15  
    1.16 +#ifndef CX_LINKED_LIST_SWAP_SBO_SIZE
    1.17 +#define CX_LINKED_LIST_SWAP_SBO_SIZE 16
    1.18 +#endif
    1.19 +
    1.20 +static int cx_ll_swap(
    1.21 +        struct cx_list_s *list,
    1.22 +        size_t i,
    1.23 +        size_t j
    1.24 +) {
    1.25 +    if (i >= list->size || j >= list->size) return 1;
    1.26 +    if (i == j) return 0;
    1.27 +
    1.28 +    // perform an optimized search that finds both elements in one run
    1.29 +    cx_linked_list *ll = (cx_linked_list *) list;
    1.30 +    size_t mid = list->size / 2;
    1.31 +    size_t left, right;
    1.32 +    if (i < j) {
    1.33 +        left = i;
    1.34 +        right = j;
    1.35 +    } else {
    1.36 +        left = j;
    1.37 +        right = i;
    1.38 +    }
    1.39 +    cx_linked_list_node *nleft, *nright;
    1.40 +    if (left < mid && right < mid) {
    1.41 +        // case 1: both items left from mid
    1.42 +        nleft = cx_ll_node_at(ll, left);
    1.43 +        nright = nleft;
    1.44 +        for (size_t c = left; c < right; c++) {
    1.45 +            nright = nright->next;
    1.46 +        }
    1.47 +    } else if (left >= mid && right >= mid) {
    1.48 +        // case 2: both items right from mid
    1.49 +        nright = cx_ll_node_at(ll, right);
    1.50 +        nleft = nright;
    1.51 +        for (size_t c = right; c > left; c--) {
    1.52 +            nleft = nleft->prev;
    1.53 +        }
    1.54 +    } else {
    1.55 +        // case 3: one item left, one item right
    1.56 +
    1.57 +        // chose the closest to begin / end
    1.58 +        size_t closest;
    1.59 +        size_t other;
    1.60 +        size_t diff2boundary = list->size - right - 1;
    1.61 +        if (left <= diff2boundary) {
    1.62 +            closest = left;
    1.63 +            other = right;
    1.64 +            nleft = cx_ll_node_at(ll, left);
    1.65 +        } else {
    1.66 +            closest = right;
    1.67 +            other = left;
    1.68 +            diff2boundary = left;
    1.69 +            nright = cx_ll_node_at(ll, right);
    1.70 +        }
    1.71 +
    1.72 +        // is other element closer to us or closer to boundary?
    1.73 +        if (right - left <= diff2boundary) {
    1.74 +            // search other element starting from already found element
    1.75 +            if (closest == left) {
    1.76 +                nright = nleft;
    1.77 +                for (size_t c = left; c < right; c++) {
    1.78 +                    nright = nright->next;
    1.79 +                }
    1.80 +            } else {
    1.81 +                nleft = nright;
    1.82 +                for (size_t c = right; c > left; c--) {
    1.83 +                    nleft = nleft->prev;
    1.84 +                }
    1.85 +            }
    1.86 +        } else {
    1.87 +            // search other element starting at the boundary
    1.88 +            if (closest == left) {
    1.89 +                nright = cx_ll_node_at(ll, other);
    1.90 +            } else {
    1.91 +                nleft = cx_ll_node_at(ll, other);
    1.92 +            }
    1.93 +        }
    1.94 +    }
    1.95 +
    1.96 +    if (list->itemsize > CX_LINKED_LIST_SWAP_SBO_SIZE || CX_DISABLE_LINKED_LIST_SWAP_SBO) {
    1.97 +        cx_linked_list_node *prev = nleft->prev;
    1.98 +        cx_linked_list_node *next = nright->next;
    1.99 +        cx_linked_list_node *midstart = nleft->next;
   1.100 +        cx_linked_list_node *midend = nright->prev;
   1.101 +
   1.102 +        if (prev == NULL) {
   1.103 +            ll->begin = nright;
   1.104 +        } else {
   1.105 +            prev->next = nright;
   1.106 +        }
   1.107 +        nright->prev = prev;
   1.108 +        if (midstart == nright) {
   1.109 +            // special case: both nodes are adjacent
   1.110 +            nright->next = nleft;
   1.111 +            nleft->prev = nright;
   1.112 +        } else {
   1.113 +            // likely case: a chain is between the two nodes
   1.114 +            nright->next = midstart;
   1.115 +            midstart->prev = nright;
   1.116 +            midend->next = nleft;
   1.117 +            nleft->prev = midend;
   1.118 +        }
   1.119 +        nleft->next = next;
   1.120 +        if (next == NULL) {
   1.121 +            ll->end = nleft;
   1.122 +        } else {
   1.123 +            next->prev = nleft;
   1.124 +        }
   1.125 +    } else {
   1.126 +        // swap payloads to avoid relinking
   1.127 +        char buf[CX_LINKED_LIST_SWAP_SBO_SIZE];
   1.128 +        memcpy(buf, nleft->payload, list->itemsize);
   1.129 +        memcpy(nleft->payload, nright->payload, list->itemsize);
   1.130 +        memcpy(nright->payload, buf, list->itemsize);
   1.131 +    }
   1.132 +
   1.133 +    return 0;
   1.134 +}
   1.135 +
   1.136  static void *cx_ll_at(
   1.137          struct cx_list_s const *list,
   1.138          size_t index
   1.139 @@ -714,6 +836,7 @@
   1.140          cx_ll_insert_array,
   1.141          cx_ll_insert_iter,
   1.142          cx_ll_remove,
   1.143 +        cx_ll_swap,
   1.144          cx_ll_at,
   1.145          cx_ll_find,
   1.146          cx_ll_sort,

mercurial