Thu, 23 Dec 2021 15:20:50 +0100
add several new linked list functions
* cx_linked_list_insert()
* cx_linked_list_insert_chain()
* cx_linked_list_link()
* cx_linked_list_unlink()
Also uses the most general function wherever possible.
src/cx/linked_list.h | file | annotate | diff | comparison | revisions | |
src/linked_list.c | file | annotate | diff | comparison | revisions |
--- a/src/cx/linked_list.h Mon Dec 20 13:01:38 2021 +0100 +++ b/src/cx/linked_list.h Thu Dec 23 15:20:50 2021 +0100 @@ -55,7 +55,11 @@ * @param item_size the size of each element in bytes * @return the created list */ -CxList cxLinkedListCreate(CxAllocator allocator, CxListComparator comparator, size_t item_size); +CxList cxLinkedListCreate( + CxAllocator allocator, + CxListComparator comparator, + size_t item_size +); /** * Allocates a linked list for storing pointers. @@ -66,7 +70,10 @@ * @param comparator the comparator for the elements * @return the created list */ -CxList cxPointerLinkedListCreate(CxAllocator allocator, CxListComparator comparator); +CxList cxPointerLinkedListCreate( + CxAllocator allocator, + CxListComparator comparator +); /** * Deallocates the memory of the entire list. @@ -94,8 +101,12 @@ * @param index the search index * @return the node found at the specified index */ -void *cx_linked_list_at(void *start, size_t start_index, ptrdiff_t loc_advance, size_t index) -__attribute__((__nonnull__)); +void *cx_linked_list_at( + void *start, + size_t start_index, + ptrdiff_t loc_advance, + size_t index +) __attribute__((__nonnull__)); /** * Finds the index of an element within a linked list. @@ -109,9 +120,14 @@ * @param elem a pointer to the element to find * @return the index of the element or a past-one index if the element could not be found */ -size_t cx_linked_list_find(void *start, ptrdiff_t loc_advance, ptrdiff_t loc_data, int follow_ptr, - CxListComparator cmp_func, void *elem) -__attribute__((__nonnull__)); +size_t cx_linked_list_find( + void *start, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + int follow_ptr, + CxListComparator cmp_func, + void *elem +) __attribute__((__nonnull__)); /** * Finds the first node in a linked list. @@ -123,8 +139,10 @@ * @param node a pointer to a node in the list * @param loc_prev the location of the \c prev pointer */ -void *cx_linked_list_first(void *node, ptrdiff_t loc_prev) -__attribute__((__nonnull__)); +void *cx_linked_list_first( + void *node, + ptrdiff_t loc_prev +) __attribute__((__nonnull__)); /** * Finds the last node in a linked list. @@ -137,8 +155,10 @@ * @param loc_next the location of the \c next pointer * @return a pointer to the last node */ -void *cx_linked_list_last(void *node, ptrdiff_t loc_next) -__attribute__((__nonnull__)); +void *cx_linked_list_last( + void *node, + ptrdiff_t loc_next +) __attribute__((__nonnull__)); /** * Finds the predecessor of a node in case it is not linked. @@ -150,8 +170,11 @@ * @param node the successor of the node to find * @return the node or \c NULL if \p node has no predecessor */ -void *cx_linked_list_prev(void *begin, ptrdiff_t loc_next, void *node) -__attribute__((__nonnull__)); +void *cx_linked_list_prev( + void *begin, + ptrdiff_t loc_next, + void *node +) __attribute__((__nonnull__)); /** * Adds a new node to a linked list. @@ -165,8 +188,13 @@ * @param loc_next the location of a \c next pointer within your node struct (required) * @param new_node a pointer to the node that shall be appended */ -void cx_linked_list_add(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node) -__attribute__((__nonnull__(5))); +void cx_linked_list_add( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *new_node +) __attribute__((__nonnull__(5))); /** * Prepends a new node to a linked list. @@ -180,8 +208,98 @@ * @param loc_next the location of a \c next pointer within your node struct (required) * @param new_node a pointer to the node that shall be prepended */ -void cx_linked_list_prepend(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node) -__attribute__((__nonnull__(5))); +void cx_linked_list_prepend( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *new_node +) __attribute__((__nonnull__(5))); + +/** + * Links two nodes. + * + * @param left the new predecessor of \p right + * @param right the new successor of \p left + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + */ +void cx_linked_list_link( + void *left, + void *right, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) __attribute__((__nonnull__)); + +/** + * Unlinks two nodes. + * + * If right is not the successor of left, the behavior is undefined. + * + * @param left the predecessor of \p right + * @param right the successor of \p left + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + */ +void cx_linked_list_unlink( + void *left, + void *right, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) __attribute__((__nonnull__)); + +/** + * Inserts a new node after a given node of a linked list. + * The new node must not be part of any list already. + * + * \note If you specify \c NULL as the \p node to insert after, this function needs either the \p begin or + * the \p end pointer to determine the start of the list. Then the new node will be prepended to the list. + * + * @param begin a pointer to the begin node pointer (if your list has one) + * @param end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param node the node after which to insert (\c NULL if you want to prepend the node to the list) + * @param new_node a pointer to the node that shall be prepended + */ +void cx_linked_list_insert( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node, + void *new_node +) __attribute__((__nonnull__(6))); + +/** + * Inserts a chain of nodes after a given node of a linked list. + * The chain must not be part of any list already. + * + * If you do not explicitly specify the end of the chain, it will be determined by traversing + * the \c next pointer. + * + * \note If you specify \c NULL as the \p node to insert after, this function needs either the \p begin or + * the \p end pointer to determine the start of the list. If only the \p end pointer is specified, you also need + * to provide a valid \p loc_prev location. + * Then the chain will be prepended to the list. + * + * @param begin a pointer to the begin node pointer (if your list has one) + * @param end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param node the node after which to insert (\c NULL to prepend the chain to the list) + * @param insert_begin a pointer to the first node of the chain that shall be inserted + * @param insert_end a pointer to the last node of the chain (or NULL if the last node shall be determined) + */ +void cx_linked_list_insert_chain( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node, + void *insert_begin, + void *insert_end +) __attribute__((__nonnull__(6))); /** * Removes a node from the linked list. @@ -202,8 +320,13 @@ * @param loc_next the location of a \c next pointer within your node struct (required) * @param node the node to remove */ -void cx_linked_list_remove(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node) -__attribute__((__nonnull__(5))); +void cx_linked_list_remove( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node +) __attribute__((__nonnull__(5))); /** @@ -212,7 +335,10 @@ * @param loc_next the location of the \c next pointer within the node struct * @return the size of the list or zero if \p node is \c NULL */ -size_t cx_linked_list_size(void *node, ptrdiff_t loc_next); +size_t cx_linked_list_size( + void *node, + ptrdiff_t loc_next +); /** * Sorts a linked list based on a comparison function. @@ -245,10 +371,15 @@ * If \c true, the data at \p loc_data is assumed to be a pointer, dereferenced, and then passed to \p cmp_func. * @param cmp_func the compare function defining the sort order */ -void cx_linked_list_sort(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, - ptrdiff_t loc_data, int follow_ptr, CxListComparator cmp_func) -__attribute__((__nonnull__(1, 7))); - +void cx_linked_list_sort( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + ptrdiff_t loc_data, + int follow_ptr, + CxListComparator cmp_func +) __attribute__((__nonnull__(1, 7))); /** * Reverses the order of the nodes in a linked list. @@ -258,8 +389,12 @@ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) * @param loc_next the location of a \c next pointer within your node struct (required) */ -void cx_linked_list_reverse(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next) -__attribute__((__nonnull__(1))); +void cx_linked_list_reverse( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) __attribute__((__nonnull__(1))); #ifdef __cplusplus } /* extern "C" */
--- a/src/linked_list.c Mon Dec 20 13:01:38 2021 +0100 +++ b/src/linked_list.c Thu Dec 23 15:20:50 2021 +0100 @@ -39,7 +39,12 @@ #define ll_advance(node) CX_LL_PTR(node, loc_advance) #define ll_data(node) (follow_ptr?CX_LL_PTR(node, loc_data):(((char*)node)+loc_data)) -void *cx_linked_list_at(void *start, size_t start_index, ptrdiff_t loc_advance, size_t index) { +void *cx_linked_list_at( + void *start, + size_t start_index, + ptrdiff_t loc_advance, + size_t index +) { assert(start != NULL); assert(loc_advance >= 0); size_t i = start_index; @@ -51,8 +56,14 @@ return cur; } -size_t cx_linked_list_find(void *start, ptrdiff_t loc_advance, ptrdiff_t loc_data, int follow_ptr, - CxListComparator cmp_func, void *elem) { +size_t cx_linked_list_find( + void *start, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + int follow_ptr, + CxListComparator cmp_func, + void *elem +) { assert(start != NULL); assert(loc_advance >= 0); assert(loc_data >= 0); @@ -71,11 +82,17 @@ return index; } -void *cx_linked_list_first(void *node, ptrdiff_t loc_prev) { +void *cx_linked_list_first( + void *node, + ptrdiff_t loc_prev +) { return cx_linked_list_last(node, loc_prev); } -void *cx_linked_list_last(void *node, ptrdiff_t loc_next) { +void *cx_linked_list_last( + void *node, + ptrdiff_t loc_next +) { assert(node != NULL); assert(loc_next >= 0); @@ -88,7 +105,11 @@ return last; } -void *cx_linked_list_prev(void *begin, ptrdiff_t loc_next, void *node) { +void *cx_linked_list_prev( + void *begin, + ptrdiff_t loc_next, + void *node +) { assert(begin != NULL); assert(node != NULL); assert(loc_next >= 0); @@ -102,10 +123,41 @@ } } -void cx_linked_list_add(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node) { - assert(new_node != NULL); +void cx_linked_list_link( + void *left, + void *right, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { assert(loc_next >= 0); - assert(ll_next(new_node) == NULL); + ll_next(left) = right; + if (loc_prev >= 0) { + ll_prev(right) = left; + } +} + +void cx_linked_list_unlink( + void *left, + void *right, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + assert (loc_next >= 0); + assert(ll_next(left) == right); + ll_next(left) = NULL; + if (loc_prev >= 0) { + assert(ll_prev(right) == left); + ll_prev(right) = NULL; + } +} + +void cx_linked_list_add( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *new_node +) { void *last; if (end == NULL) { assert(begin != NULL); @@ -113,57 +165,76 @@ } else { last = *end; } - if (last == NULL) { - if (begin != NULL) { - *begin = new_node; - } - } else { - // if there is a last node, update its next pointer - ll_next(last) = new_node; + cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, last, new_node, new_node); +} + +void cx_linked_list_prepend( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *new_node +) { + cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, NULL, new_node, new_node); +} + +void cx_linked_list_insert( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node, + void *new_node +) { + cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, node, new_node, new_node); +} + +void cx_linked_list_insert_chain( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node, + void *insert_begin, + void *insert_end +) { + // find the end of the chain, if not specified + if (insert_end == NULL) { + insert_end = cx_linked_list_last(insert_begin, loc_next); } - // if there is an end pointer, update it - if (end != NULL) { - *end = new_node; + // determine the successor + void *successor; + if (node == NULL) { + assert(begin != NULL || (end != NULL && loc_prev >= 0)); + if (begin != NULL) { + successor = *begin; + *begin = insert_begin; + } else { + successor = *end == NULL ? NULL : cx_linked_list_first(*end, loc_prev); + } + } else { + successor = ll_next(node); + cx_linked_list_link(node, insert_begin, loc_prev, loc_next); } - // if the nodes use a prev pointer, update it - if (loc_prev >= 0) { - assert(ll_prev(new_node) == NULL); - ll_prev(new_node) = last; + if (successor == NULL) { + // the list ends with the new chain + if (end != NULL) { + *end = insert_end; + } + } else { + cx_linked_list_link(insert_end, successor, loc_prev, loc_next); } } -void cx_linked_list_prepend(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node) { - assert(new_node != NULL); - assert(loc_next >= 0); - assert(ll_next(new_node) == NULL); - void *first; - if (begin == NULL) { - assert(end != NULL); - assert(loc_prev >= 0); - first = *end == NULL ? NULL : cx_linked_list_first(*end, loc_prev); - } else { - first = *begin; - } - if (first == NULL) { - if (end != NULL) { - *end = new_node; - } - } else { - ll_next(new_node) = first; - if (loc_prev >= 0) { - ll_prev(first) = new_node; - } - } - - if (begin != NULL) { - assert(loc_prev < 0 || ll_prev(new_node) == NULL); - *begin = new_node; - } -} - -void cx_linked_list_remove(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node) { +void cx_linked_list_remove( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node +) { assert(node != NULL); assert(loc_next >= 0); assert(loc_prev >= 0 || begin != NULL); @@ -196,7 +267,10 @@ } } -size_t cx_linked_list_size(void *node, ptrdiff_t loc_next) { +size_t cx_linked_list_size( + void *node, + ptrdiff_t loc_next +) { assert(loc_next >= 0); size_t size = 0; while (node != NULL) { @@ -206,10 +280,17 @@ return size; } -static void *cx_linked_list_sort_merge(ptrdiff_t loc_prev, ptrdiff_t loc_next, - ptrdiff_t loc_data, int follow_ptr, - size_t length, void *ls, void *le, void *re, - CxListComparator cmp_func) { +static void *cx_linked_list_sort_merge( + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + ptrdiff_t loc_data, + int follow_ptr, + size_t length, + void *ls, + void *le, + void *re, + CxListComparator cmp_func +) { const size_t sbo_len = 1024; void *sbo[sbo_len]; void **sorted = (length >= sbo_len) ? malloc(sizeof(void *) * length) : sbo; @@ -242,8 +323,7 @@ // Update pointer if (loc_prev >= 0) ll_prev(sorted[0]) = NULL; for (size_t i = 0; i < length - 1; i++) { - ll_next(sorted[i]) = sorted[i + 1]; - if (loc_prev >= 0) ll_prev(sorted[i + 1]) = sorted[i]; + cx_linked_list_link(sorted[i], sorted[i + 1], loc_prev, loc_next); } ll_next(sorted[length - 1]) = NULL; @@ -255,8 +335,14 @@ } void cx_linked_list_sort( /* NOLINT(misc-no-recursion) - purposely recursive function */ - void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, - ptrdiff_t loc_data, int follow_ptr, CxListComparator cmp_func) { + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + ptrdiff_t loc_data, + int follow_ptr, + CxListComparator cmp_func +) { assert(begin != NULL); assert(loc_next >= 0); assert(loc_data >= 0); @@ -310,7 +396,12 @@ } } -void cx_linked_list_reverse(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next) { +void cx_linked_list_reverse( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { assert(begin != NULL); assert(loc_next >= 0); @@ -355,7 +446,10 @@ cx_linked_list_node *end; } cx_linked_list; -static cx_linked_list_node *cx_ll_node_at(cx_linked_list *list, size_t index) { +static cx_linked_list_node *cx_ll_node_at( + cx_linked_list *list, + size_t index +) { if (index >= list->base.size) { return NULL; } else if (index > list->base.size / 2) { @@ -365,72 +459,69 @@ } } -static int cx_ll_insert(cx_list_s *list, size_t index, void *elem) { +static int cx_ll_insert( + cx_list_s *list, + size_t index, + void *elem +) { // out-of bounds check if (index > list->size) return 1; // cast a linked list pointer cx_linked_list *ll = (cx_linked_list *) list; - // create the new node - cx_linked_list_node *node = cxMalloc(list->allocator, - sizeof(cx_linked_list_node) + list->itemsize); + // create the new new_node + cx_linked_list_node *new_node = cxMalloc(list->allocator, + sizeof(cx_linked_list_node) + list->itemsize); // sortir if failed - if (node == NULL) return 1; + if (new_node == NULL) return 1; - // copy payload to the new node - memcpy(node->payload, elem, list->itemsize); + // initialize new new_node + new_node->prev = new_node->next = NULL; + memcpy(new_node->payload, elem, list->itemsize); - // check if this is the first node - if (ll->begin == NULL) { - node->prev = node->next = NULL; - ll->begin = ll->end = node; - } else { - // check if this shall be the new end node - if (index == list->size) { - ll->end->next = node; - node->prev = ll->end; - node->next = NULL; - ll->end = node; - } - // check if this shall be the new start node - else if (index == 0) { - ll->begin->prev = node; - node->next = ll->begin; - node->prev = NULL; - ll->begin = node; - } else { - // find the node at the current index - cx_linked_list_node *cur = cx_ll_node_at(ll, index); + // find position efficiently + cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at(ll, index - 1); - // insert before that node - // (we know all ptr are non-null because we handled all other cases before) - node->next = cur; - node->prev = cur->prev; - cur->prev = node; - node->prev->next = node; - } - } + // insert + cx_linked_list_insert_chain( + (void **) &ll->begin, (void **) &ll->end, + CX_LL_LOC_PREV, CX_LL_LOC_NEXT, + node, new_node, new_node + ); // increase the size and return list->size++; return 0; } -static int cx_ll_add(cx_list_s *list, void *elem) { +static int cx_ll_add( + cx_list_s *list, + void *elem +) { return cx_ll_insert(list, list->size, elem); } -static int cx_pll_insert(cx_list_s *list, size_t index, void *elem) { +static int cx_pll_insert( + cx_list_s *list, + size_t index, + void *elem +) { return cx_ll_insert(list, index, &elem); } -static int cx_pll_add(cx_list_s *list, void *elem) { +static int cx_pll_add( + cx_list_s *list, + void *elem +) { return cx_ll_insert(list, list->size, &elem); } -static int cx_ll_remove(cx_list_s *list, size_t index) { +static int cx_ll_remove( + cx_list_s *list, + size_t index +) { cx_linked_list *ll = (cx_linked_list *) list; cx_linked_list_node *node = cx_ll_node_at(ll, index); @@ -450,25 +541,37 @@ return 0; } -static void *cx_ll_at(cx_list_s *list, size_t index) { +static void *cx_ll_at( + cx_list_s *list, + size_t index +) { cx_linked_list *ll = (cx_linked_list *) list; cx_linked_list_node *node = cx_ll_node_at(ll, index); return node == NULL ? NULL : node->payload; } -static void *cx_pll_at(cx_list_s *list, size_t index) { +static void *cx_pll_at( + cx_list_s *list, + size_t index +) { cx_linked_list *ll = (cx_linked_list *) list; cx_linked_list_node *node = cx_ll_node_at(ll, index); return node == NULL ? NULL : *(void **) node->payload; } -static size_t cx_ll_find(cx_list_s *list, void *elem) { +static size_t cx_ll_find( + cx_list_s *list, + void *elem +) { return cx_linked_list_find(((cx_linked_list *) list)->begin, CX_LL_LOC_NEXT, CX_LL_LOC_DATA, 0, list->cmpfunc, elem); } -static size_t cx_pll_find(cx_list_s *list, void *elem) { +static size_t cx_pll_find( + cx_list_s *list, + void *elem +) { return cx_linked_list_find(((cx_linked_list *) list)->begin, CX_LL_LOC_NEXT, CX_LL_LOC_DATA, 1, list->cmpfunc, elem); @@ -506,7 +609,11 @@ cx_pll_sort }; -CxList cxLinkedListCreate(CxAllocator allocator, CxListComparator comparator, size_t item_size) { +CxList cxLinkedListCreate( + CxAllocator allocator, + CxListComparator comparator, + size_t item_size +) { cx_linked_list *list = cxMalloc(allocator, sizeof(cx_linked_list)); if (list == NULL) return NULL; @@ -524,7 +631,10 @@ return (CxList) list; } -CxList cxPointerLinkedListCreate(CxAllocator allocator, CxListComparator comparator) { +CxList cxPointerLinkedListCreate( + CxAllocator allocator, + CxListComparator comparator +) { cx_linked_list *list = cxMalloc(allocator, sizeof(cx_linked_list)); if (list == NULL) return NULL;