# HG changeset patch # User Mike Becker # Date 1678821924 -3600 # Node ID af5bf4603a5dbb89715f0d0ff3554ab4a41f5d29 # Parent d50b5dc1e058c3665c5f0ebefb756493c3a883e1 add cxListClear and fix missing destructor invocations - #241 #246 diff -r d50b5dc1e058 -r af5bf4603a5d src/array_list.c --- a/src/array_list.c Sun Mar 05 10:55:32 2023 +0100 +++ b/src/array_list.c Tue Mar 14 20:25:24 2023 +0100 @@ -263,11 +263,20 @@ struct cx_list_s *list, size_t index ) { + cx_array_list *arl = (cx_array_list *) list; + // out-of-bounds check if (index >= list->size) { return 1; } + // content destruction + if (list->content_destructor_type != CX_DESTRUCTOR_NONE) { + char *ptr = arl->data; + ptr += index * list->itemsize; + cx_list_invoke_destructor(list, ptr); + } + // short-circuit removal of last element if (index == list->size - 1) { list->size--; @@ -275,7 +284,6 @@ } // just move the elements starting at index to the left - cx_array_list *arl = (cx_array_list *) list; int result = cx_array_copy( &arl->data, &list->size, @@ -293,6 +301,33 @@ return result; } +static void cx_arl_clear(struct cx_list_s *list) { + if (list->size == 0) return; + + cx_array_list *arl = (cx_array_list *) list; + char *ptr = arl->data; + + switch (list->content_destructor_type) { + case CX_DESTRUCTOR_SIMPLE: { + for (size_t i = 0; i < list->size; i++) { + list->simple_destructor(ptr); + ptr += list->itemsize; + } + break; + } + case CX_DESTRUCTOR_ADVANCED: { + for (size_t i = 0; i < list->size; i++) { + list->advanced_destructor.func(list->advanced_destructor.data, + ptr); + ptr += list->itemsize; + } + break; + } + case CX_DESTRUCTOR_NONE: + break; // nothing + } +} + static int cx_arl_swap( struct cx_list_s *list, size_t i, @@ -451,6 +486,7 @@ cx_arl_insert_array, cx_arl_insert_iter, cx_arl_remove, + cx_arl_clear, cx_arl_swap, cx_arl_at, cx_arl_find, diff -r d50b5dc1e058 -r af5bf4603a5d src/cx/list.h --- a/src/cx/list.h Sun Mar 05 10:55:32 2023 +0100 +++ b/src/cx/list.h Tue Mar 14 20:25:24 2023 +0100 @@ -164,6 +164,11 @@ ); /** + * Member function for removing all elements. + */ + void (*clear)(struct cx_list_s *list); + + /** * Member function for swapping two elements. */ int (*swap)( @@ -222,6 +227,21 @@ typedef struct cx_list_s CxList; /** + * Invokes the destructor function for a specific element. + * + * Usually only used by list implementations. There should be no need + * to invoke this function manually. + * + * @param list the list + * @param elem the element + */ +__attribute__((__nonnull__)) +void cx_list_invoke_destructor( + struct cx_list_s const *list, + void *elem +); + +/** * Advises the list to store copies of the objects (default mode of operation). * * Retrieving objects from this list will yield pointers to the copies stored @@ -398,6 +418,10 @@ /** * Removes the element at the specified index. + * + * If an element destructor function is specified, it is called before + * removing the element. + * * @param list the list * @param index the index of the element * @return zero on success, non-zero if the index is out of bounds @@ -411,6 +435,19 @@ } /** + * Removes all elements from this list. + * + * If an element destructor function is specified, it is called for each + * element before removing them. + * + * @param list the list + */ +__attribute__((__nonnull__)) +static inline void cxListClear(CxList *list) { + list->cl->clear(list); +} + +/** * Swaps two items in the list. * * Implementations should only allocate temporary memory for the swap, if diff -r d50b5dc1e058 -r af5bf4603a5d src/linked_list.c --- a/src/linked_list.c Sun Mar 05 10:55:32 2023 +0100 +++ b/src/linked_list.c Tue Mar 14 20:25:24 2023 +0100 @@ -569,6 +569,11 @@ // out-of-bounds check if (node == NULL) return 1; + // element destruction + if (list->content_destructor_type != CX_DESTRUCTOR_NONE) { + cx_list_invoke_destructor(list, node->payload); + } + // remove cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); @@ -582,6 +587,48 @@ return 0; } +static void cx_ll_clear(struct cx_list_s *list) { + if (list->size == 0) return; + + cx_linked_list *ll = (cx_linked_list *) list; + cx_linked_list_node *node = ll->begin; + + // looks super redundant, but avoids repeatedly checking + // the destructor type for each element + switch (list->content_destructor_type) { + case CX_DESTRUCTOR_SIMPLE: { + while (node != NULL) { + list->simple_destructor(node->payload); + cx_linked_list_node *next = node->next; + cxFree(list->allocator, node); + node = next; + } + break; + } + case CX_DESTRUCTOR_ADVANCED: { + while (node != NULL) { + list->advanced_destructor.func(list->advanced_destructor.data, + node->payload); + cx_linked_list_node *next = node->next; + cxFree(list->allocator, node); + node = next; + } + break; + } + case CX_DESTRUCTOR_NONE: { + while (node != NULL) { + cx_linked_list_node *next = node->next; + cxFree(list->allocator, node); + node = next; + } + break; + } + } + + ll->begin = ll->end = NULL; + list->size = 0; +} + #ifndef CX_LINKED_LIST_SWAP_SBO_SIZE #define CX_LINKED_LIST_SWAP_SBO_SIZE 16 #endif @@ -753,13 +800,17 @@ if (itbase->remove) { itbase->remove = false; struct cx_mut_iterator_s *iter = it; + struct cx_list_s *list = iter->src_handle; cx_linked_list *ll = iter->src_handle; cx_linked_list_node *node = iter->elem_handle; iter->elem_handle = node->next; + if (list->content_destructor_type != CX_DESTRUCTOR_NONE) { + cx_list_invoke_destructor(list, node->payload); + } cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); - ll->base.size--; - cxFree(ll->base.allocator, node); + list->size--; + cxFree(list->allocator, node); } else { struct cx_iterator_s *iter = it; iter->index++; @@ -773,14 +824,18 @@ if (itbase->remove) { itbase->remove = false; struct cx_mut_iterator_s *iter = it; + struct cx_list_s *list = iter->src_handle; cx_linked_list *ll = iter->src_handle; cx_linked_list_node *node = iter->elem_handle; iter->elem_handle = node->prev; iter->index--; + if (list->content_destructor_type != CX_DESTRUCTOR_NONE) { + cx_list_invoke_destructor(list, node->payload); + } cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); - ll->base.size--; - cxFree(ll->base.allocator, node); + list->size--; + cxFree(list->allocator, node); } else { struct cx_iterator_s *iter = it; iter->index--; @@ -861,6 +916,7 @@ cx_ll_insert_array, cx_ll_insert_iter, cx_ll_remove, + cx_ll_clear, cx_ll_swap, cx_ll_at, cx_ll_find, diff -r d50b5dc1e058 -r af5bf4603a5d src/list.c --- a/src/list.c Sun Mar 05 10:55:32 2023 +0100 +++ b/src/list.c Tue Mar 14 20:25:24 2023 +0100 @@ -95,6 +95,10 @@ 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, @@ -164,6 +168,7 @@ cx_pl_insert_array, cx_pl_insert_iter, cx_pl_remove, + cx_pl_clear, cx_pl_swap, cx_pl_at, cx_pl_find, @@ -192,6 +197,24 @@ // +void cx_list_invoke_destructor( + CxList const *list, + void *elem +) { + switch (list->content_destructor_type) { + case CX_DESTRUCTOR_SIMPLE: { + list->simple_destructor(elem); + break; + } + case CX_DESTRUCTOR_ADVANCED: { + list->advanced_destructor.func(list->advanced_destructor.data, elem); + break; + } + case CX_DESTRUCTOR_NONE: + break; // nothing + } +} + void cxListDestroy(CxList *list) { switch (list->content_destructor_type) { case CX_DESTRUCTOR_SIMPLE: {