diff -r fe49cff3c571 -r bb7da585debc ucx/list.c --- a/ucx/list.c Sun May 23 09:44:43 2021 +0200 +++ b/ucx/list.c Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * 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: @@ -26,403 +26,462 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/list.h" +#include "cx/list.h" + +#include + +// + +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; +} -UcxList *ucx_list_clone(const UcxList *l, copy_func fnc, void *data) { - return ucx_list_clone_a(ucx_default_allocator(), l, fnc, data); +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->destructor(list); +} + +static int 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); } -UcxList *ucx_list_clone_a(UcxAllocator *alloc, const UcxList *l, - copy_func fnc, void *data) { - UcxList *ret = NULL; - while (l) { - if (fnc) { - ret = ucx_list_append_a(alloc, ret, fnc(l->data, data)); - } else { - ret = ucx_list_append_a(alloc, ret, l->data); - } - l = l->next; - } +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 int cx_pl_remove( + struct cx_list_s *list, + size_t index +) { + return list->climpl->remove(list, index); +} + +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 ssize_t cx_pl_find_remove( + struct cx_list_s *list, + const void *elem, + bool remove +) { + cx_pl_hack_cmpfunc(list); + ssize_t ret = list->climpl->find_remove(list, &elem, remove); + cx_pl_unhack_cmpfunc(list); return ret; } -int ucx_list_equals(const UcxList *l1, const UcxList *l2, - cmp_func fnc, void* data) { - if (l1 == l2) return 1; - - while (l1 != NULL && l2 != NULL) { - if (fnc == NULL) { - if (l1->data != l2->data) return 0; - } else { - if (fnc(l1->data, l2->data, data) != 0) return 0; - } - l1 = l1->next; - l2 = l2->next; - } - - return (l1 == NULL && l2 == NULL); +static void cx_pl_sort(struct cx_list_s *list) { + cx_pl_hack_cmpfunc(list); + list->climpl->sort(list); + cx_pl_unhack_cmpfunc(list); } -void ucx_list_free(UcxList *l) { - ucx_list_free_a(ucx_default_allocator(), l); +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; } -void ucx_list_free_a(UcxAllocator *alloc, UcxList *l) { - UcxList *e = l, *f; - while (e != NULL) { - f = e; - e = e->next; - alfree(alloc, f); - } -} - -void ucx_list_free_content(UcxList* list, ucx_destructor destr) { - if (!destr) destr = free; - while (list != NULL) { - destr(list->data); - list = list->next; - } +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; } -UcxList *ucx_list_append(UcxList *l, void *data) { - return ucx_list_append_a(ucx_default_allocator(), l, data); -} +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, +}; -UcxList *ucx_list_append_a(UcxAllocator *alloc, UcxList *l, void *data) { - UcxList *nl = (UcxList*) almalloc(alloc, sizeof(UcxList)); - if (!nl) { - return NULL; - } - - nl->data = data; - nl->next = NULL; - if (l) { - UcxList *t = ucx_list_last(l); - t->next = nl; - nl->prev = t; - return l; - } else { - nl->prev = NULL; - return nl; +void cxListStoreObjects(CxList *list) { + list->collection.store_pointer = false; + if (list->climpl != NULL) { + list->cl = list->climpl; + list->climpl = NULL; } } -UcxList *ucx_list_prepend(UcxList *l, void *data) { - return ucx_list_prepend_a(ucx_default_allocator(), l, data); -} - -UcxList *ucx_list_prepend_a(UcxAllocator *alloc, UcxList *l, void *data) { - UcxList *nl = ucx_list_append_a(alloc, NULL, data); - if (!nl) { - return NULL; - } - l = ucx_list_first(l); - - if (l) { - nl->next = l; - l->prev = nl; - } - return nl; -} - -UcxList *ucx_list_concat(UcxList *l1, UcxList *l2) { - if (l1) { - UcxList *last = ucx_list_last(l1); - last->next = l2; - if (l2) { - l2->prev = last; - } - return l1; - } else { - return l2; - } +void cxListStorePointers(CxList *list) { + list->collection.elem_size = sizeof(void *); + list->collection.store_pointer = true; + list->climpl = list->cl; + list->cl = &cx_pointer_list_class; } -UcxList *ucx_list_last(const UcxList *l) { - if (l == NULL) return NULL; - - const UcxList *e = l; - while (e->next != NULL) { - e = e->next; - } - return (UcxList*)e; -} +// -ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem) { - ssize_t index = 0; - while (list) { - if (list == elem) { - return index; - } - list = list->next; - index++; - } - return -1; +// + +static void cx_emptyl_noop(__attribute__((__unused__)) CxList *list) { + // this is a noop, but MUST be implemented } -UcxList *ucx_list_get(const UcxList *l, size_t index) { - if (l == NULL) return NULL; - - const UcxList *e = l; - while (e->next && index > 0) { - e = e->next; - index--; - } - - return (UcxList*)(index == 0 ? e : NULL); +static void *cx_emptyl_at( + __attribute__((__unused__)) const struct cx_list_s *list, + __attribute__((__unused__)) size_t index +) { + return NULL; } -ssize_t ucx_list_find(const UcxList *l, void *elem, - cmp_func fnc, void *cmpdata) { - ssize_t index = 0; - UCX_FOREACH(e, l) { - if (fnc) { - if (fnc(elem, e->data, cmpdata) == 0) { - return index; - } - } else { - if (elem == e->data) { - return index; - } - } - index++; - } +static ssize_t cx_emptyl_find_remove( + __attribute__((__unused__)) struct cx_list_s *list, + __attribute__((__unused__)) const void *elem, + __attribute__((__unused__)) bool remove +) { return -1; } -int ucx_list_contains(const UcxList *l, void *elem, - cmp_func fnc, void *cmpdata) { - return ucx_list_find(l, elem, fnc, cmpdata) > -1; +static bool cx_emptyl_iter_valid(__attribute__((__unused__)) const void *iter) { + return false; } -size_t ucx_list_size(const UcxList *l) { - if (l == NULL) return 0; - - const UcxList *e = l; - size_t s = 1; - while (e->next != NULL) { - e = e->next; - s++; - } - - return s; +static CxIterator cx_emptyl_iterator( + const struct cx_list_s *list, + size_t index, + __attribute__((__unused__)) bool backwards +) { + CxIterator iter = {0}; + iter.src_handle.c = list; + iter.index = index; + iter.base.valid = cx_emptyl_iter_valid; + return iter; } -static UcxList *ucx_list_sort_merge(size_t length, - UcxList* ls, UcxList* le, UcxList* re, - cmp_func fnc, void* data) { +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, +}; - UcxList** sorted = (UcxList**) malloc(sizeof(UcxList*)*length); - UcxList *rc, *lc; +CxList cx_empty_list = { + { + NULL, + NULL, + 0, + 0, + NULL, + NULL, + NULL, + false + }, + &cx_empty_list_class, + NULL +}; + +CxList *const cxEmptyList = &cx_empty_list; + +// + +#define invoke_list_func(name, list, ...) \ + ((list)->climpl == NULL ? (list)->cl->name : (list)->climpl->name) \ + (list, __VA_ARGS__) - lc = ls; rc = le; - size_t n = 0; - while (lc && lc != le && rc != re) { - if (fnc(lc->data, rc->data, data) <= 0) { - sorted[n] = lc; - lc = lc->next; +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 (0 != 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 (0 != invoke_list_func(insert_element, + list, di, src)) + return inserted; } else { - sorted[n] = rc; - rc = rc->next; + size_t r = invoke_list_func(insert_array, list, di, src, ins); + if (r < ins) return inserted + r; } - n++; - } - while (lc && lc != le) { - sorted[n] = lc; - lc = lc->next; - n++; - } - while (rc && rc != re) { - sorted[n] = rc; - rc = rc->next; - n++; + inserted += ins; + di += ins; + + // everything inserted? + if (inserted == n) return inserted; + src = next; } - // Update pointer - sorted[0]->prev = NULL; - for (int i = 0 ; i < length-1 ; i++) { - sorted[i]->next = sorted[i+1]; - sorted[i+1]->prev = sorted[i]; + // insert remaining items + if (si < n) { + inserted += invoke_list_func(insert_array, list, di, src, n - si); } - sorted[length-1]->next = NULL; - UcxList *ret = sorted[0]; - free(sorted); - return ret; + return inserted; } -UcxList *ucx_list_sort(UcxList *l, cmp_func fnc, void *data) { - if (l == NULL) { - return NULL; +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 = malloc(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; } - UcxList *lc; - size_t ln = 1; + free(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 = malloc(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); + + free(tmp); + + return 0; +} + +void cxListDestroy(CxList *list) { + list->cl->destructor(list); +} + +int cxListCompare( + const CxList *list, + const CxList *other +) { + bool cannot_optimize = false; - UcxList *ls = l, *le, *re; - - // check how many elements are already sorted - lc = ls; - while (lc->next != NULL && fnc(lc->next->data, lc->data, data) > 0) { - lc = lc->next; - ln++; + // 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; } - le = lc->next; - if (le == NULL) { - return l; // this list is already sorted :) + 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 { - UcxList *rc; - size_t rn = 1; - rc = le; - // skip already sorted elements - while (rc->next != NULL && fnc(rc->next->data, rc->data, data) > 0) { - rc = rc->next; - rn++; - } - re = rc->next; - - // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them - UcxList *sorted = ucx_list_sort_merge(ln+rn, - ls, le, re, - fnc, data); - - // Something left? Sort it! - size_t remainder_length = ucx_list_size(re); - if (remainder_length > 0) { - UcxList *remainder = ucx_list_sort(re, fnc, data); - - // merge sorted list with (also sorted) remainder - l = ucx_list_sort_merge(ln+rn+remainder_length, - sorted, remainder, NULL, fnc, data); - } else { - // no remainder - we've got our sorted list - l = sorted; - } - - return l; + // lists are compatible + return list->cl->compare(list, other); } } -UcxList *ucx_list_first(const UcxList *l) { - if (!l) { - return NULL; - } - - const UcxList *e = l; - while (e->prev) { - e = e->prev; - } - return (UcxList *)e; -} - -UcxList *ucx_list_remove(UcxList *l, UcxList *e) { - return ucx_list_remove_a(ucx_default_allocator(), l, e); -} - -UcxList *ucx_list_remove_a(UcxAllocator *alloc, UcxList *l, UcxList *e) { - if (l == e) { - l = e->next; - } - - if (e->next) { - e->next->prev = e->prev; - } - - if (e->prev) { - e->prev->next = e->next; - } - - alfree(alloc, e); - return l; +CxIterator cxListMutIteratorAt( + CxList *list, + size_t index +) { + CxIterator it = list->cl->iterator(list, index, false); + it.base.mutating = true; + return it; } - -static UcxList* ucx_list_setoperation_a(UcxAllocator *allocator, - UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata, - int op) { - - UcxList *res = NULL; - UcxList *cur = NULL; - const UcxList *src = left; - - do { - UCX_FOREACH(node, src) { - void* elem = node->data; - if ( - (op == 0 && !ucx_list_contains(res, elem, cmpfnc, cmpdata)) || - (op == 1 && ucx_list_contains(right, elem, cmpfnc, cmpdata)) || - (op == 2 && !ucx_list_contains(right, elem, cmpfnc, cmpdata))) { - UcxList *nl = almalloc(allocator, sizeof(UcxList)); - nl->prev = cur; - nl->next = NULL; - if (cpfnc) { - nl->data = cpfnc(elem, cpdata); - } else { - nl->data = elem; - } - if (cur != NULL) - cur->next = nl; - cur = nl; - if (res == NULL) - res = cur; - } - } - if (op == 0 && src == left) - src = right; - else - src = NULL; - } while (src != NULL); - - return res; +CxIterator cxListMutBackwardsIteratorAt( + CxList *list, + size_t index +) { + CxIterator it = list->cl->iterator(list, index, true); + it.base.mutating = true; + return it; } - -UcxList* ucx_list_union(UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - return ucx_list_union_a(ucx_default_allocator(), - left, right, cmpfnc, cmpdata, cpfnc, cpdata); -} - -UcxList* ucx_list_union_a(UcxAllocator *allocator, - UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - - return ucx_list_setoperation_a(allocator, left, right, - cmpfnc, cmpdata, cpfnc, cpdata, 0); -} - -UcxList* ucx_list_intersection(UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - return ucx_list_intersection_a(ucx_default_allocator(), left, right, - cmpfnc, cmpdata, cpfnc, cpdata); -} - -UcxList* ucx_list_intersection_a(UcxAllocator *allocator, - UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - - return ucx_list_setoperation_a(allocator, left, right, - cmpfnc, cmpdata, cpfnc, cpdata, 1); -} - -UcxList* ucx_list_difference(UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - return ucx_list_difference_a(ucx_default_allocator(), left, right, - cmpfnc, cmpdata, cpfnc, cpdata); -} - -UcxList* ucx_list_difference_a(UcxAllocator *allocator, - UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - - return ucx_list_setoperation_a(allocator, left, right, - cmpfnc, cmpdata, cpfnc, cpdata, 2); -}