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/list.h" #include <string.h> // <editor-fold desc="Store Pointers Functionality"> static _Thread_local cx_compare_func cx_pl_cmpfunc_impl; static int cx_pl_cmpfunc( const void *l, const void *r ) { void *const *lptr = l; void *const *rptr = r; const void *left = lptr == NULL ? NULL : *lptr; const void *right = rptr == NULL ? NULL : *rptr; return cx_pl_cmpfunc_impl(left, right); } static void cx_pl_hack_cmpfunc(const struct cx_list_s *list) { // cast away const - this is the hacky thing struct cx_collection_s *l = (struct cx_collection_s*) &list->collection; cx_pl_cmpfunc_impl = l->cmpfunc; l->cmpfunc = cx_pl_cmpfunc; } static void cx_pl_unhack_cmpfunc(const struct cx_list_s *list) { // cast away const - this is the hacky thing struct cx_collection_s *l = (struct cx_collection_s*) &list->collection; l->cmpfunc = cx_pl_cmpfunc_impl; } static void cx_pl_destructor(struct cx_list_s *list) { list->climpl->deallocate(list); } static void *cx_pl_insert_element( struct cx_list_s *list, size_t index, const void *element ) { return list->climpl->insert_element(list, index, &element); } static size_t cx_pl_insert_array( struct cx_list_s *list, size_t index, const void *array, size_t n ) { return list->climpl->insert_array(list, index, array, n); } static size_t cx_pl_insert_sorted( struct cx_list_s *list, const void *array, size_t n ) { cx_pl_hack_cmpfunc(list); size_t result = list->climpl->insert_sorted(list, array, n); cx_pl_unhack_cmpfunc(list); return result; } static int cx_pl_insert_iter( struct cx_iterator_s *iter, const void *elem, int prepend ) { struct cx_list_s *list = iter->src_handle.m; return list->climpl->insert_iter(iter, &elem, prepend); } static size_t cx_pl_remove( struct cx_list_s *list, size_t index, size_t num, void *targetbuf ) { return list->climpl->remove(list, index, num, targetbuf); } static void cx_pl_clear(struct cx_list_s *list) { list->climpl->clear(list); } static int cx_pl_swap( struct cx_list_s *list, size_t i, size_t j ) { return list->climpl->swap(list, i, j); } static void *cx_pl_at( const struct cx_list_s *list, size_t index ) { void **ptr = list->climpl->at(list, index); return ptr == NULL ? NULL : *ptr; } static size_t cx_pl_find_remove( struct cx_list_s *list, const void *elem, bool remove ) { cx_pl_hack_cmpfunc(list); size_t ret = list->climpl->find_remove(list, &elem, remove); cx_pl_unhack_cmpfunc(list); return ret; } static void cx_pl_sort(struct cx_list_s *list) { cx_pl_hack_cmpfunc(list); list->climpl->sort(list); cx_pl_unhack_cmpfunc(list); } static int cx_pl_compare( const struct cx_list_s *list, const struct cx_list_s *other ) { cx_pl_hack_cmpfunc(list); int ret = list->climpl->compare(list, other); cx_pl_unhack_cmpfunc(list); return ret; } static void cx_pl_reverse(struct cx_list_s *list) { list->climpl->reverse(list); } static void *cx_pl_iter_current(const void *it) { const struct cx_iterator_s *iter = it; void **ptr = iter->base.current_impl(it); return ptr == NULL ? NULL : *ptr; } static struct cx_iterator_s cx_pl_iterator( const struct cx_list_s *list, size_t index, bool backwards ) { struct cx_iterator_s iter = list->climpl->iterator(list, index, backwards); iter.base.current_impl = iter.base.current; iter.base.current = cx_pl_iter_current; return iter; } static cx_list_class cx_pointer_list_class = { cx_pl_destructor, cx_pl_insert_element, cx_pl_insert_array, cx_pl_insert_sorted, cx_pl_insert_iter, cx_pl_remove, cx_pl_clear, cx_pl_swap, cx_pl_at, cx_pl_find_remove, cx_pl_sort, cx_pl_compare, cx_pl_reverse, cx_pl_iterator, }; // </editor-fold> // <editor-fold desc="empty list implementation"> static void cx_emptyl_noop(cx_attr_unused CxList *list) { // this is a noop, but MUST be implemented } static void *cx_emptyl_at( cx_attr_unused const struct cx_list_s *list, cx_attr_unused size_t index ) { return NULL; } static size_t cx_emptyl_find_remove( cx_attr_unused struct cx_list_s *list, cx_attr_unused const void *elem, cx_attr_unused bool remove ) { return 0; } static bool cx_emptyl_iter_valid(cx_attr_unused const void *iter) { return false; } static CxIterator cx_emptyl_iterator( const struct cx_list_s *list, size_t index, cx_attr_unused bool backwards ) { CxIterator iter = {0}; iter.src_handle.c = list; iter.index = index; iter.base.valid = cx_emptyl_iter_valid; return iter; } static cx_list_class cx_empty_list_class = { cx_emptyl_noop, NULL, NULL, NULL, NULL, NULL, cx_emptyl_noop, NULL, cx_emptyl_at, cx_emptyl_find_remove, cx_emptyl_noop, NULL, cx_emptyl_noop, cx_emptyl_iterator, }; CxList cx_empty_list = { { NULL, NULL, 0, 0, NULL, NULL, NULL, false, true, }, &cx_empty_list_class, NULL }; CxList *const cxEmptyList = &cx_empty_list; // </editor-fold> #define invoke_list_func(name, list, ...) \ ((list)->climpl == NULL ? (list)->cl->name : (list)->climpl->name) \ (list, __VA_ARGS__) size_t cx_list_default_insert_array( struct cx_list_s *list, size_t index, const void *data, size_t n ) { size_t elem_size = list->collection.elem_size; const char *src = data; size_t i = 0; for (; i < n; i++) { if (NULL == invoke_list_func( insert_element, list, index + i, src + (i * elem_size))) return i; } return i; } size_t cx_list_default_insert_sorted( struct cx_list_s *list, const void *sorted_data, size_t n ) { // corner case if (n == 0) return 0; size_t elem_size = list->collection.elem_size; cx_compare_func cmp = list->collection.cmpfunc; const char *src = sorted_data; // track indices and number of inserted items size_t di = 0, si = 0, inserted = 0; // search the list for insertion points for (; di < list->collection.size; di++) { const void *list_elm = invoke_list_func(at, list, di); // compare current list element with first source element // if less or equal, skip if (cmp(list_elm, src) <= 0) { continue; } // determine number of consecutive elements that can be inserted size_t ins = 1; const char *next = src; while (++si < n) { next += elem_size; // once we become larger than the list elem, break if (cmp(list_elm, next) <= 0) { break; } // otherwise, we can insert one more ins++; } // insert the elements at location si if (ins == 1) { if (NULL == invoke_list_func( insert_element, list, di, src)) return inserted; } else { size_t r = invoke_list_func(insert_array, list, di, src, ins); if (r < ins) return inserted + r; } inserted += ins; di += ins; // everything inserted? if (inserted == n) return inserted; src = next; } // insert remaining items if (si < n) { inserted += invoke_list_func(insert_array, list, di, src, n - si); } return inserted; } void cx_list_default_sort(struct cx_list_s *list) { size_t elem_size = list->collection.elem_size; size_t list_size = list->collection.size; void *tmp = cxMallocDefault(elem_size * list_size); if (tmp == NULL) abort(); // copy elements from source array char *loc = tmp; for (size_t i = 0; i < list_size; i++) { void *src = invoke_list_func(at, list, i); memcpy(loc, src, elem_size); loc += elem_size; } // qsort qsort(tmp, list_size, elem_size, list->collection.cmpfunc); // copy elements back loc = tmp; for (size_t i = 0; i < list_size; i++) { void *dest = invoke_list_func(at, list, i); memcpy(dest, loc, elem_size); loc += elem_size; } cxFreeDefault(tmp); } int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j) { if (i == j) return 0; if (i >= list->collection.size) return 1; if (j >= list->collection.size) return 1; size_t elem_size = list->collection.elem_size; void *tmp = cxMallocDefault(elem_size); if (tmp == NULL) return 1; void *ip = invoke_list_func(at, list, i); void *jp = invoke_list_func(at, list, j); memcpy(tmp, ip, elem_size); memcpy(ip, jp, elem_size); memcpy(jp, tmp, elem_size); cxFreeDefault(tmp); return 0; } void cx_list_init( struct cx_list_s *list, struct cx_list_class_s *cl, const struct cx_allocator_s *allocator, cx_compare_func comparator, size_t elem_size ) { list->cl = cl; list->collection.allocator = allocator; list->collection.cmpfunc = comparator; if (elem_size > 0) { list->collection.elem_size = elem_size; } else { list->collection.elem_size = sizeof(void *); if (list->collection.cmpfunc == NULL) { list->collection.cmpfunc = cx_cmp_ptr; } list->collection.store_pointer = true; list->climpl = list->cl; list->cl = &cx_pointer_list_class; } } int cxListCompare( const CxList *list, const CxList *other ) { bool cannot_optimize = false; // if one is storing pointers but the other is not cannot_optimize |= list->collection.store_pointer ^ other->collection.store_pointer; // if one class is wrapped but the other is not cannot_optimize |= (list->climpl == NULL) ^ (other->climpl == NULL); // if the compare functions do not match or both are NULL if (!cannot_optimize) { cx_compare_func list_cmp = (cx_compare_func) (list->climpl != NULL ? list->climpl->compare : list->cl->compare); cx_compare_func other_cmp = (cx_compare_func) (other->climpl != NULL ? other->climpl->compare : other->cl->compare); cannot_optimize |= list_cmp != other_cmp; cannot_optimize |= list_cmp == NULL; } if (cannot_optimize) { // lists are definitely different - cannot use internal compare function if (list->collection.size == other->collection.size) { CxIterator left = list->cl->iterator(list, 0, false); CxIterator right = other->cl->iterator(other, 0, false); for (size_t i = 0; i < list->collection.size; i++) { void *leftValue = cxIteratorCurrent(left); void *rightValue = cxIteratorCurrent(right); int d = list->collection.cmpfunc(leftValue, rightValue); if (d != 0) { return d; } cxIteratorNext(left); cxIteratorNext(right); } return 0; } else { return list->collection.size < other->collection.size ? -1 : 1; } } else { // lists are compatible return list->cl->compare(list, other); } } CxIterator cxListMutIteratorAt( CxList *list, size_t index ) { CxIterator it = list->cl->iterator(list, index, false); it.base.mutating = true; return it; } CxIterator cxListMutBackwardsIteratorAt( CxList *list, size_t index ) { CxIterator it = list->cl->iterator(list, index, true); it.base.mutating = true; return it; } void cxListFree(CxList *list) { if (list == NULL) return; list->cl->deallocate(list); } int cxListSet( CxList *list, size_t index, const void *elem ) { if (index >= list->collection.size) { return 1; } if (list->collection.store_pointer) { // For pointer collections, always use climpl void **target = list->climpl->at(list, index); *target = (void *)elem; } else { void *target = list->cl->at(list, index); memcpy(target, elem, list->collection.elem_size); } return 0; }