Fri, 23 May 2025 12:44:24 +0200
make test-compile depend on both static and shared
the shared lib is not needed for the tests,
but when run with coverage, gcov will be confused
when outdated line information is available from
a previous shared build
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "cx/linked_list.h" #include "cx/compare.h" #include <string.h> #include <assert.h> // LOW LEVEL LINKED LIST FUNCTIONS #define CX_LL_PTR(cur, off) (*(void**)(((char*)(cur))+(off))) #define ll_prev(node) CX_LL_PTR(node, loc_prev) #define ll_next(node) CX_LL_PTR(node, loc_next) #define ll_advance(node) CX_LL_PTR(node, loc_advance) #define ll_data(node) (((char*)(node))+loc_data) void *cx_linked_list_at( const 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; const void *cur = start; while (i != index && cur != NULL) { cur = ll_advance(cur); i < index ? i++ : i--; } return (void *) cur; } void *cx_linked_list_find( const void *start, ptrdiff_t loc_advance, ptrdiff_t loc_data, cx_compare_func cmp_func, const void *elem, size_t *found_index ) { assert(start != NULL); assert(loc_advance >= 0); assert(loc_data >= 0); assert(cmp_func); void *node = (void*) start; size_t index = 0; do { void *current = ll_data(node); if (cmp_func(current, elem) == 0) { if (found_index != NULL) { *found_index = index; } return node; } node = ll_advance(node); index++; } while (node != NULL); return NULL; } void *cx_linked_list_first( const void *node, ptrdiff_t loc_prev ) { return cx_linked_list_last(node, loc_prev); } void *cx_linked_list_last( const void *node, ptrdiff_t loc_next ) { assert(node != NULL); assert(loc_next >= 0); const void *cur = node; const void *last; do { last = cur; } while ((cur = ll_next(cur)) != NULL); return (void *) last; } void *cx_linked_list_prev( const void *begin, ptrdiff_t loc_next, const void *node ) { assert(begin != NULL); assert(node != NULL); assert(loc_next >= 0); if (begin == node) return NULL; const void *cur = begin; const void *next; while (1) { next = ll_next(cur); if (next == node) return (void *) cur; cur = next; } } void cx_linked_list_link( void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next ) { assert(loc_next >= 0); 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); last = *begin == NULL ? NULL : cx_linked_list_last(*begin, loc_next); } else { last = *end; } 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); } // 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 (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_insert_sorted( void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func cmp_func ) { assert(ll_next(new_node) == NULL); cx_linked_list_insert_sorted_chain( begin, end, loc_prev, loc_next, new_node, cmp_func); } void cx_linked_list_insert_sorted_chain( void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func cmp_func ) { assert(begin != NULL); assert(loc_next >= 0); assert(insert_begin != NULL); // track currently observed nodes void *dest_prev = NULL; void *dest = *begin; void *src = insert_begin; // special case: list is empty if (dest == NULL) { *begin = src; if (end != NULL) { *end = cx_linked_list_last(src, loc_next); } return; } // search the list for insertion points while (dest != NULL && src != NULL) { // compare current list node with source node // if less or equal, skip if (cmp_func(dest, src) <= 0) { dest_prev = dest; dest = ll_next(dest); continue; } // determine chain of elements that can be inserted void *end_of_chain = src; void *next_in_chain = ll_next(src); while (next_in_chain != NULL) { // once we become larger than the list elem, break if (cmp_func(dest, next_in_chain) <= 0) { break; } // otherwise, we can insert one more end_of_chain = next_in_chain; next_in_chain = ll_next(next_in_chain); } // insert the elements if (dest_prev == NULL) { // new begin *begin = src; } else { cx_linked_list_link(dest_prev, src, loc_prev, loc_next); } cx_linked_list_link(end_of_chain, dest, loc_prev, loc_next); // continue with next src = next_in_chain; dest_prev = dest; dest = ll_next(dest); } // insert remaining items if (src != NULL) { cx_linked_list_link(dest_prev, src, loc_prev, loc_next); } // determine new end of list, if requested if (end != NULL) { *end = cx_linked_list_last( dest != NULL ? dest : dest_prev, loc_next); } } size_t cx_linked_list_remove_chain( void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, size_t num ) { assert(node != NULL); assert(loc_next >= 0); assert(loc_prev >= 0 || begin != NULL); // easy exit if (num == 0) return 0; // find adjacent nodes void *prev; if (loc_prev >= 0) { prev = ll_prev(node); } else { prev = cx_linked_list_prev(*begin, loc_next, node); } void *next = ll_next(node); size_t removed = 1; for (; removed < num && next != NULL ; removed++) { next = ll_next(next); } // update next pointer of prev node, or set begin if (prev == NULL) { if (begin != NULL) { *begin = next; } } else { ll_next(prev) = next; } // update prev pointer of next node, or set end if (next == NULL) { if (end != NULL) { *end = prev; } } else if (loc_prev >= 0) { ll_prev(next) = prev; } return removed; } size_t cx_linked_list_size( const void *node, ptrdiff_t loc_next ) { assert(loc_next >= 0); size_t size = 0; while (node != NULL) { node = ll_next(node); size++; } return size; } #ifndef CX_LINKED_LIST_SORT_SBO_SIZE #define CX_LINKED_LIST_SORT_SBO_SIZE 1024 #endif static void cx_linked_list_sort_merge( ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_data, size_t length, void *ls, void *le, void *re, cx_compare_func cmp_func, void **begin, void **end ) { void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE]; void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ? cxMallocDefault(sizeof(void *) * length) : sbo; if (sorted == NULL) abort(); void *rc, *lc; lc = ls; rc = le; size_t n = 0; while (lc && lc != le && rc != re) { if (cmp_func(ll_data(lc), ll_data(rc)) <= 0) { sorted[n] = lc; lc = ll_next(lc); } else { sorted[n] = rc; rc = ll_next(rc); } n++; } while (lc && lc != le) { sorted[n] = lc; lc = ll_next(lc); n++; } while (rc && rc != re) { sorted[n] = rc; rc = ll_next(rc); n++; } // Update pointer if (loc_prev >= 0) ll_prev(sorted[0]) = NULL; for (size_t i = 0 ; i < length - 1; i++) { cx_linked_list_link(sorted[i], sorted[i + 1], loc_prev, loc_next); } ll_next(sorted[length - 1]) = NULL; *begin = sorted[0]; *end = sorted[length - 1]; if (sorted != sbo) { cxFreeDefault(sorted); } } 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, cx_compare_func cmp_func ) { assert(begin != NULL); assert(loc_next >= 0); assert(loc_data >= 0); assert(cmp_func); void *lc, *ls, *le, *re; // set start node ls = *begin; // early exit when this list is empty if (ls == NULL) return; // check how many elements are already sorted lc = ls; size_t ln = 1; while (ll_next(lc) != NULL && cmp_func(ll_data(ll_next(lc)), ll_data(lc)) > 0) { lc = ll_next(lc); ln++; } le = ll_next(lc); // if first unsorted node is NULL, the list is already completely sorted if (le != NULL) { void *rc; size_t rn = 1; rc = le; // skip already sorted elements while (ll_next(rc) != NULL && cmp_func(ll_data(ll_next(rc)), ll_data(rc)) > 0) { rc = ll_next(rc); rn++; } re = ll_next(rc); // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them void *sorted_begin, *sorted_end; cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, ln + rn, ls, le, re, cmp_func, &sorted_begin, &sorted_end); // Something left? Sort it! size_t remainder_length = cx_linked_list_size(re, loc_next); if (remainder_length > 0) { void *remainder = re; cx_linked_list_sort(&remainder, NULL, loc_prev, loc_next, loc_data, cmp_func); // merge sorted list with (also sorted) remainder cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, ln + rn + remainder_length, sorted_begin, remainder, NULL, cmp_func, &sorted_begin, &sorted_end); } *begin = sorted_begin; if (end) *end = sorted_end; } } int cx_linked_list_compare( const void *begin_left, const void *begin_right, ptrdiff_t loc_advance, ptrdiff_t loc_data, cx_compare_func cmp_func ) { const void *left = begin_left, *right = begin_right; while (left != NULL && right != NULL) { const void *left_data = ll_data(left); const void *right_data = ll_data(right); int result = cmp_func(left_data, right_data); if (result != 0) return result; left = ll_advance(left); right = ll_advance(right); } if (left != NULL) { return 1; } else if (right != NULL) { return -1; } else { return 0; } } void cx_linked_list_reverse( void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next ) { assert(begin != NULL); assert(loc_next >= 0); // swap all links void *prev = NULL; void *cur = *begin; while (cur != NULL) { void *next = ll_next(cur); ll_next(cur) = prev; if (loc_prev >= 0) { ll_prev(cur) = next; } prev = cur; cur = next; } // update begin and end if (end != NULL) { *end = *begin; } *begin = prev; } // HIGH LEVEL LINKED LIST IMPLEMENTATION typedef struct cx_linked_list_node cx_linked_list_node; struct cx_linked_list_node { cx_linked_list_node *prev; cx_linked_list_node *next; char payload[]; }; #define CX_LL_LOC_PREV offsetof(cx_linked_list_node, prev) #define CX_LL_LOC_NEXT offsetof(cx_linked_list_node, next) #define CX_LL_LOC_DATA offsetof(cx_linked_list_node, payload) typedef struct { struct cx_list_s base; cx_linked_list_node *begin; cx_linked_list_node *end; } cx_linked_list; static cx_linked_list_node *cx_ll_node_at( const cx_linked_list *list, size_t index ) { if (index >= list->base.collection.size) { return NULL; } else if (index > list->base.collection.size / 2) { return cx_linked_list_at(list->end, list->base.collection.size - 1, CX_LL_LOC_PREV, index); } else { return cx_linked_list_at(list->begin, 0, CX_LL_LOC_NEXT, index); } } static cx_linked_list_node *cx_ll_malloc_node(const struct cx_list_s *list) { return cxMalloc(list->collection.allocator, sizeof(cx_linked_list_node) + list->collection.elem_size); } static int cx_ll_insert_at( struct cx_list_s *list, cx_linked_list_node *node, const void *elem ) { // create the new new_node cx_linked_list_node *new_node = cx_ll_malloc_node(list); // sortir if failed if (new_node == NULL) return 1; // initialize new new_node new_node->prev = new_node->next = NULL; if (elem != NULL) { memcpy(new_node->payload, elem, list->collection.elem_size); } // insert cx_linked_list *ll = (cx_linked_list *) list; 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->collection.size++; return 0; } static size_t cx_ll_insert_array( struct cx_list_s *list, size_t index, const void *array, size_t n ) { // out-of bounds and corner case check if (index > list->collection.size || n == 0) return 0; // find position efficiently cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); // perform first insert if (0 != cx_ll_insert_at(list, node, array)) return 1; // is there more? if (n == 1) return 1; // we now know exactly where we are node = node == NULL ? ((cx_linked_list *) list)->begin : node->next; // we can add the remaining nodes and immediately advance to the inserted node const char *source = array; for (size_t i = 1; i < n; i++) { source += list->collection.elem_size; if (0 != cx_ll_insert_at(list, node, source)) return i; node = node->next; } return n; } static void *cx_ll_insert_element( struct cx_list_s *list, size_t index, const void *element ) { // out-of-bounds check if (index > list->collection.size) return NULL; // find position efficiently cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); // perform first insert if (cx_ll_insert_at(list, node, element)) return NULL; // return a pointer to the data of the inserted node if (node == NULL) { return ((cx_linked_list *) list)->begin->payload; } else { return node->next->payload; } } static _Thread_local cx_compare_func cx_ll_insert_sorted_cmp_func; static int cx_ll_insert_sorted_cmp_helper(const void *l, const void *r) { const cx_linked_list_node *left = l; const cx_linked_list_node *right = r; return cx_ll_insert_sorted_cmp_func(left->payload, right->payload); } static size_t cx_ll_insert_sorted( struct cx_list_s *list, const void *array, size_t n ) { // special case if (n == 0) return 0; // create a new chain of nodes cx_linked_list_node *chain = cx_ll_malloc_node(list); if (chain == NULL) return 0; memcpy(chain->payload, array, list->collection.elem_size); chain->prev = NULL; chain->next = NULL; // add all elements from the array to that chain cx_linked_list_node *prev = chain; const char *src = array; size_t inserted = 1; for (; inserted < n; inserted++) { cx_linked_list_node *next = cx_ll_malloc_node(list); if (next == NULL) break; src += list->collection.elem_size; memcpy(next->payload, src, list->collection.elem_size); prev->next = next; next->prev = prev; prev = next; } prev->next = NULL; // invoke the low level function cx_linked_list *ll = (cx_linked_list *) list; cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc; cx_linked_list_insert_sorted_chain( (void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT, chain, cx_ll_insert_sorted_cmp_helper ); // adjust the list metadata list->collection.size += inserted; return inserted; } static size_t cx_ll_remove( struct cx_list_s *list, size_t index, size_t num, void *targetbuf ) { cx_linked_list *ll = (cx_linked_list *) list; cx_linked_list_node *node = cx_ll_node_at(ll, index); // out-of-bounds check if (node == NULL) return 0; // remove size_t removed = cx_linked_list_remove_chain( (void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node, num ); // adjust size list->collection.size -= removed; // copy or destroy the removed chain if (targetbuf == NULL) { cx_linked_list_node *n = node; for (size_t i = 0; i < removed; i++) { // element destruction cx_invoke_destructor(list, n->payload); // free the node and advance void *next = n->next; cxFree(list->collection.allocator, n); n = next; } } else { char *dest = targetbuf; cx_linked_list_node *n = node; for (size_t i = 0; i < removed; i++) { // copy payload memcpy(dest, n->payload, list->collection.elem_size); // advance target buffer dest += list->collection.elem_size; // free the node and advance void *next = n->next; cxFree(list->collection.allocator, n); n = next; } } return removed; } static void cx_ll_clear(struct cx_list_s *list) { if (list->collection.size == 0) return; cx_linked_list *ll = (cx_linked_list *) list; cx_linked_list_node *node = ll->begin; while (node != NULL) { cx_invoke_destructor(list, node->payload); cx_linked_list_node *next = node->next; cxFree(list->collection.allocator, node); node = next; } ll->begin = ll->end = NULL; list->collection.size = 0; } static int cx_ll_swap( struct cx_list_s *list, size_t i, size_t j ) { if (i >= list->collection.size || j >= list->collection.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->collection.size / 2; size_t left, right; if (i < j) { left = i; right = j; } else { left = j; right = i; } cx_linked_list_node *nleft = NULL, *nright = NULL; if (left < mid && right < mid) { // case 1: both items left from mid nleft = cx_ll_node_at(ll, left); assert(nleft != NULL); 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); assert(nright != NULL); 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->collection.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); } } } 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; } return 0; } static void *cx_ll_at( const struct 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 size_t cx_ll_find_remove( struct cx_list_s *list, const void *elem, bool remove ) { if (list->collection.size == 0) return 0; size_t index; cx_linked_list *ll = ((cx_linked_list *) list); cx_linked_list_node *node = cx_linked_list_find( ll->begin, CX_LL_LOC_NEXT, CX_LL_LOC_DATA, list->collection.cmpfunc, elem, &index ); if (node == NULL) { return list->collection.size; } if (remove) { cx_invoke_destructor(list, node->payload); cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); list->collection.size--; cxFree(list->collection.allocator, node); } return index; } static void cx_ll_sort(struct cx_list_s *list) { cx_linked_list *ll = (cx_linked_list *) list; cx_linked_list_sort((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT, CX_LL_LOC_DATA, list->collection.cmpfunc); } static void cx_ll_reverse(struct cx_list_s *list) { cx_linked_list *ll = (cx_linked_list *) list; cx_linked_list_reverse((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT); } static int cx_ll_compare( const struct cx_list_s *list, const struct cx_list_s *other ) { cx_linked_list *left = (cx_linked_list *) list; cx_linked_list *right = (cx_linked_list *) other; return cx_linked_list_compare(left->begin, right->begin, CX_LL_LOC_NEXT, CX_LL_LOC_DATA, list->collection.cmpfunc); } static bool cx_ll_iter_valid(const void *it) { const struct cx_iterator_s *iter = it; return iter->elem_handle != NULL; } static void cx_ll_iter_next(void *it) { struct cx_iterator_s *iter = it; if (iter->base.remove) { iter->base.remove = false; struct cx_list_s *list = iter->src_handle.m; cx_linked_list *ll = iter->src_handle.m; cx_linked_list_node *node = iter->elem_handle; iter->elem_handle = node->next; cx_invoke_destructor(list, node->payload); cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); list->collection.size--; cxFree(list->collection.allocator, node); } else { iter->index++; cx_linked_list_node *node = iter->elem_handle; iter->elem_handle = node->next; } } static void cx_ll_iter_prev(void *it) { struct cx_iterator_s *iter = it; if (iter->base.remove) { iter->base.remove = false; struct cx_list_s *list = iter->src_handle.m; cx_linked_list *ll = iter->src_handle.m; cx_linked_list_node *node = iter->elem_handle; iter->elem_handle = node->prev; iter->index--; cx_invoke_destructor(list, node->payload); cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); list->collection.size--; cxFree(list->collection.allocator, node); } else { iter->index--; cx_linked_list_node *node = iter->elem_handle; iter->elem_handle = node->prev; } } static void *cx_ll_iter_current(const void *it) { const struct cx_iterator_s *iter = it; cx_linked_list_node *node = iter->elem_handle; return node->payload; } static CxIterator cx_ll_iterator( const struct cx_list_s *list, size_t index, bool backwards ) { CxIterator iter; iter.index = index; iter.src_handle.c = list; iter.elem_handle = cx_ll_node_at((const cx_linked_list *) list, index); iter.elem_size = list->collection.elem_size; iter.elem_count = list->collection.size; iter.base.valid = cx_ll_iter_valid; iter.base.current = cx_ll_iter_current; iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next; iter.base.mutating = false; iter.base.remove = false; return iter; } static int cx_ll_insert_iter( CxIterator *iter, const void *elem, int prepend ) { struct cx_list_s *list = iter->src_handle.m; cx_linked_list_node *node = iter->elem_handle; if (node != NULL) { assert(prepend >= 0 && prepend <= 1); cx_linked_list_node *choice[2] = {node, node->prev}; int result = cx_ll_insert_at(list, choice[prepend], elem); if (result == 0) { iter->elem_count++; if (prepend) { iter->index++; } } return result; } else { if (cx_ll_insert_element(list, list->collection.size, elem) == NULL) { return 1; } iter->elem_count++; iter->index = list->collection.size; return 0; } } static void cx_ll_destructor(CxList *list) { cx_linked_list *ll = (cx_linked_list *) list; cx_linked_list_node *node = ll->begin; while (node) { cx_invoke_destructor(list, node->payload); void *next = node->next; cxFree(list->collection.allocator, node); node = next; } cxFree(list->collection.allocator, list); } static cx_list_class cx_linked_list_class = { cx_ll_destructor, cx_ll_insert_element, cx_ll_insert_array, cx_ll_insert_sorted, cx_ll_insert_iter, cx_ll_remove, cx_ll_clear, cx_ll_swap, cx_ll_at, cx_ll_find_remove, cx_ll_sort, cx_ll_compare, cx_ll_reverse, cx_ll_iterator, }; CxList *cxLinkedListCreate( const CxAllocator *allocator, cx_compare_func comparator, size_t elem_size ) { if (allocator == NULL) { allocator = cxDefaultAllocator; } cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list)); if (list == NULL) return NULL; cx_list_init((CxList*)list, &cx_linked_list_class, allocator, comparator, elem_size); return (CxList *) list; }