add new destructor API and apply it to CxList

Tue, 15 Feb 2022 19:41:48 +0100

author
Mike Becker <universe@uap-core.de>
date
Tue, 15 Feb 2022 19:41:48 +0100
changeset 503
a89857072ace
parent 502
33e7b6ebf403
child 504
aaf89ce0cbf6

add new destructor API and apply it to CxList

src/CMakeLists.txt file | annotate | diff | comparison | revisions
src/cx/allocator.h file | annotate | diff | comparison | revisions
src/cx/common.h file | annotate | diff | comparison | revisions
src/cx/linked_list.h file | annotate | diff | comparison | revisions
src/cx/list.h file | annotate | diff | comparison | revisions
src/linked_list.c file | annotate | diff | comparison | revisions
src/list.c file | annotate | diff | comparison | revisions
test/test_list.c file | annotate | diff | comparison | revisions
     1.1 --- a/src/CMakeLists.txt	Tue Feb 15 19:07:14 2022 +0100
     1.2 +++ b/src/CMakeLists.txt	Tue Feb 15 19:41:48 2022 +0100
     1.3 @@ -1,6 +1,7 @@
     1.4  set(sources
     1.5          utils.c
     1.6          allocator.c
     1.7 +        list.c
     1.8          linked_list.c
     1.9          tree.c
    1.10          buffer.c
     2.1 --- a/src/cx/allocator.h	Tue Feb 15 19:07:14 2022 +0100
     2.2 +++ b/src/cx/allocator.h	Tue Feb 15 19:41:48 2022 +0100
     2.3 @@ -46,23 +46,37 @@
     2.4      /**
     2.5       * The allocator's malloc() implementation.
     2.6       */
     2.7 -    void *(*malloc)(void *data, size_t n);
     2.8 +    void *(*malloc)(
     2.9 +            void *data,
    2.10 +            size_t n
    2.11 +    );
    2.12  
    2.13      /**
    2.14       * The allocator's realloc() implementation.
    2.15       */
    2.16 -    void *(*realloc)(void *data, void *mem, size_t n)
    2.17 +    void *(*realloc)(
    2.18 +            void *data,
    2.19 +            void *mem,
    2.20 +            size_t n
    2.21 +    )
    2.22      __attribute__((__warn_unused_result__));
    2.23  
    2.24      /**
    2.25       * The allocator's calloc() implementation.
    2.26       */
    2.27 -    void *(*calloc)(void *data, size_t nelem, size_t n);
    2.28 +    void *(*calloc)(
    2.29 +            void *data,
    2.30 +            size_t nelem,
    2.31 +            size_t n
    2.32 +    );
    2.33  
    2.34      /**
    2.35       * The allocator's free() implementation.
    2.36       */
    2.37 -    void (*free)(void *data, void *mem)
    2.38 +    void (*free)(
    2.39 +            void *data,
    2.40 +            void *mem
    2.41 +    )
    2.42      __attribute__((__nonnull__));
    2.43  } cx_allocator_class;
    2.44  
    2.45 @@ -91,6 +105,18 @@
    2.46  extern CxAllocator *cxDefaultAllocator;
    2.47  
    2.48  /**
    2.49 + * Function pointer type for destructor functions.
    2.50 + *
    2.51 + * A destructor function deallocates possible contents and MAY free the memory
    2.52 + * pointed to by \p memory.
    2.53 + *
    2.54 + * @param memory a pointer to the object to destruct
    2.55 + * @return \p memory if it has NOT been free'd by this destructor, otherwise \c NULL
    2.56 +  */
    2.57 +typedef void *(*cx_destructor_func)(void *memory)
    2.58 +        __attribute__((__nonnull__, __warn_unused_result__));
    2.59 +
    2.60 +/**
    2.61   * Allocate \p n bytes of memory.
    2.62   *
    2.63   * @param allocator the allocator
     3.1 --- a/src/cx/common.h	Tue Feb 15 19:07:14 2022 +0100
     3.2 +++ b/src/cx/common.h	Tue Feb 15 19:41:48 2022 +0100
     3.3 @@ -114,6 +114,9 @@
     3.4  #endif /* _WIN32 */
     3.5  
     3.6  #ifndef __GNUC__
     3.7 +/**
     3.8 + * Removes GNU C attributes where they are not supported.
     3.9 + */
    3.10  #define __attribute__(x)
    3.11  #endif
    3.12  
     4.1 --- a/src/cx/linked_list.h	Tue Feb 15 19:07:14 2022 +0100
     4.2 +++ b/src/cx/linked_list.h	Tue Feb 15 19:41:48 2022 +0100
     4.3 @@ -94,15 +94,6 @@
     4.4  ) __attribute__((__nonnull__));
     4.5  
     4.6  /**
     4.7 - * Deallocates the memory of the entire list.
     4.8 - *
     4.9 - * \attention If this is a pointer list, the memory the pointers are referring to is \em not freed.
    4.10 - *
    4.11 - * @param list the list
    4.12 - */
    4.13 -void cxLinkedListDestroy(CxList *list) __attribute__((__nonnull__));
    4.14 -
    4.15 -/**
    4.16   * Finds the node at a certain index.
    4.17   *
    4.18   * This function can be used to start at an arbitrary position within the list.
     5.1 --- a/src/cx/list.h	Tue Feb 15 19:07:14 2022 +0100
     5.2 +++ b/src/cx/list.h	Tue Feb 15 19:41:48 2022 +0100
     5.3 @@ -71,6 +71,14 @@
     5.4       */
     5.5      CxAllocator *allocator;
     5.6      /**
     5.7 +     * A mandatory destructor for the list structure.
     5.8 +     */
     5.9 +    cx_destructor_func list_destructor;
    5.10 +    /**
    5.11 +     * An optional destructor for the list contents.
    5.12 +     */
    5.13 +    cx_destructor_func content_destructor;
    5.14 +    /**
    5.15       * The comparator function for the elements.
    5.16       */
    5.17      CxListComparator cmpfunc;
    5.18 @@ -86,6 +94,16 @@
    5.19       * The capacity of the list (maximum number of elements).
    5.20       */
    5.21      size_t capacity;
    5.22 +    /**
    5.23 +     * Flag indicating whether cxListDestroy() shall free the list structure,
    5.24 +     * even if cx_list_s.list_destructor did not do that.
    5.25 +     */
    5.26 +    bool autofree;
    5.27 +    /**
    5.28 +     * Flag indicating whether cxListDestroy() shall free each list element,
    5.29 +     * even if cx_list_s.content_destructor did not do that.
    5.30 +     */
    5.31 +    bool autofree_contents;
    5.32  };
    5.33  
    5.34  /**
    5.35 @@ -364,6 +382,21 @@
    5.36      return list->cl->compare(list, other);
    5.37  }
    5.38  
    5.39 +/**
    5.40 + * Calls the list's destructor function for every element.
    5.41 + * If CxList.autofree_content is \c true, the elements are automatically free'd
    5.42 + * unless the content destructor function did not already do that.
    5.43 + * Similarly, if CxList.autofree is \c true, the list structure is free'd, unless
    5.44 + * the list destructor function did not already do that.
    5.45 + *
    5.46 + * This function itself is a destructor function for the CxList.
    5.47 + *
    5.48 + * @param list the list which contents shall be destroyed
    5.49 + * @return \p list if the list structure has been free'd during the process
    5.50 + */
    5.51 +__attribute__((__nonnull__))
    5.52 +CxList *cxListDestroy(CxList *list);
    5.53 +
    5.54  #ifdef __cplusplus
    5.55  } /* extern "C" */
    5.56  #endif
     6.1 --- a/src/linked_list.c	Tue Feb 15 19:07:14 2022 +0100
     6.2 +++ b/src/linked_list.c	Tue Feb 15 19:41:48 2022 +0100
     6.3 @@ -757,24 +757,35 @@
     6.4          cx_pll_iterator,
     6.5  };
     6.6  
     6.7 +static CxList *cx_ll_default_destructor(CxList *list) {
     6.8 +    cx_linked_list *ll = (cx_linked_list *) list;
     6.9 +
    6.10 +    cx_linked_list_node *node = ll->begin;
    6.11 +    while (node) {
    6.12 +        void *next = node->next;
    6.13 +        cxFree(list->allocator, node);
    6.14 +        node = next;
    6.15 +    }
    6.16 +
    6.17 +    cxFree(list->allocator, list);
    6.18 +    return NULL;
    6.19 +}
    6.20 +
    6.21  CxList *cxLinkedListCreate(
    6.22          CxAllocator *allocator,
    6.23          CxListComparator comparator,
    6.24          size_t item_size
    6.25  ) {
    6.26 -    cx_linked_list *list = cxMalloc(allocator, sizeof(cx_linked_list));
    6.27 +    cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list));
    6.28      if (list == NULL)
    6.29          return NULL;
    6.30  
    6.31      list->base.cl = &cx_linked_list_class;
    6.32      list->base.allocator = allocator;
    6.33 +    list->base.list_destructor = (cx_destructor_func) cx_ll_default_destructor;
    6.34      list->base.cmpfunc = comparator;
    6.35      list->base.itemsize = item_size;
    6.36      list->base.capacity = SIZE_MAX;
    6.37 -    list->base.size = 0;
    6.38 -
    6.39 -    list->begin = NULL;
    6.40 -    list->end = NULL;
    6.41  
    6.42      return (CxList *) list;
    6.43  }
    6.44 @@ -783,19 +794,16 @@
    6.45          CxAllocator *allocator,
    6.46          CxListComparator comparator
    6.47  ) {
    6.48 -    cx_linked_list *list = cxMalloc(allocator, sizeof(cx_linked_list));
    6.49 +    cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list));
    6.50      if (list == NULL)
    6.51          return NULL;
    6.52  
    6.53      list->base.cl = &cx_pointer_linked_list_class;
    6.54      list->base.allocator = allocator;
    6.55 +    list->base.list_destructor = (cx_destructor_func) cx_ll_default_destructor;
    6.56      list->base.cmpfunc = comparator;
    6.57      list->base.itemsize = sizeof(void *);
    6.58      list->base.capacity = SIZE_MAX;
    6.59 -    list->base.size = 0;
    6.60 -
    6.61 -    list->begin = NULL;
    6.62 -    list->end = NULL;
    6.63  
    6.64      return (CxList *) list;
    6.65  }
    6.66 @@ -811,22 +819,8 @@
    6.67      if (list == NULL) return NULL;
    6.68      for (size_t i = 0; i < num_items; i++) {
    6.69          if (0 != cxListAdd(list, ((const unsigned char *) array) + i * item_size)) {
    6.70 -            cxLinkedListDestroy(list);
    6.71 -            return NULL;
    6.72 +            return cx_ll_default_destructor(list);
    6.73          }
    6.74      }
    6.75      return list;
    6.76  }
    6.77 -
    6.78 -void cxLinkedListDestroy(CxList *list) {
    6.79 -    cx_linked_list *ll = (cx_linked_list *) list;
    6.80 -
    6.81 -    cx_linked_list_node *node = ll->begin;
    6.82 -    while (node) {
    6.83 -        void *next = node->next;
    6.84 -        cxFree(list->allocator, node);
    6.85 -        node = next;
    6.86 -    }
    6.87 -
    6.88 -    cxFree(list->allocator, list);
    6.89 -}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/list.c	Tue Feb 15 19:41:48 2022 +0100
     7.3 @@ -0,0 +1,59 @@
     7.4 +/*
     7.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     7.6 + *
     7.7 + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
     7.8 + *
     7.9 + * Redistribution and use in source and binary forms, with or without
    7.10 + * modification, are permitted provided that the following conditions are met:
    7.11 + *
    7.12 + *   1. Redistributions of source code must retain the above copyright
    7.13 + *      notice, this list of conditions and the following disclaimer.
    7.14 + *
    7.15 + *   2. Redistributions in binary form must reproduce the above copyright
    7.16 + *      notice, this list of conditions and the following disclaimer in the
    7.17 + *      documentation and/or other materials provided with the distribution.
    7.18 + *
    7.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    7.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    7.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    7.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    7.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    7.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    7.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    7.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    7.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    7.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    7.29 + * POSSIBILITY OF SUCH DAMAGE.
    7.30 + */
    7.31 +
    7.32 +#include "cx/list.h"
    7.33 +
    7.34 +CxList *cxListDestroy(CxList *list) {
    7.35 +    if (list->content_destructor == NULL) {
    7.36 +        if (list->autofree_contents) {
    7.37 +            CxIterator iter = cxListBegin(list);
    7.38 +            cx_foreach(void*, elem, iter) {
    7.39 +                cxFree(list->allocator, elem);
    7.40 +            }
    7.41 +        }
    7.42 +    } else {
    7.43 +        // avoid checking this condition every loop iteration
    7.44 +        if (list->autofree_contents) {
    7.45 +            CxIterator iter = cxListBegin(list);
    7.46 +            cx_foreach(void*, elem, iter) {
    7.47 +                cxFree(list->allocator, list->content_destructor(elem));
    7.48 +            }
    7.49 +        } else {
    7.50 +            CxIterator iter = cxListBegin(list);
    7.51 +            cx_foreach(void*, elem, iter) {
    7.52 +                elem = list->content_destructor(elem);
    7.53 +            }
    7.54 +        }
    7.55 +    }
    7.56 +    if (list->autofree) {
    7.57 +        cxFree(list->allocator, list->list_destructor(list));
    7.58 +        return NULL;
    7.59 +    } else {
    7.60 +        return list->list_destructor(list);
    7.61 +    }
    7.62 +}
     8.1 --- a/test/test_list.c	Tue Feb 15 19:07:14 2022 +0100
     8.2 +++ b/test/test_list.c	Tue Feb 15 19:41:48 2022 +0100
     8.3 @@ -568,7 +568,7 @@
     8.4      CU_ASSERT_EQUAL(list->itemsize, sizeof(int))
     8.5      CU_ASSERT_PTR_EQUAL(list->cmpfunc, cmp_int)
     8.6  
     8.7 -    cxLinkedListDestroy(list);
     8.8 +    cxListDestroy(list);
     8.9      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
    8.10  }
    8.11  
    8.12 @@ -583,7 +583,7 @@
    8.13      CU_ASSERT_EQUAL(list->itemsize, sizeof(void *))
    8.14      CU_ASSERT_PTR_EQUAL(list->cmpfunc, cmp_int)
    8.15  
    8.16 -    cxLinkedListDestroy(list);
    8.17 +    cxListDestroy(list);
    8.18      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
    8.19  }
    8.20  
    8.21 @@ -599,8 +599,8 @@
    8.22  
    8.23      CU_ASSERT_TRUE(0 == cxListCompare(list, expected))
    8.24  
    8.25 -    cxLinkedListDestroy(list);
    8.26 -    cxLinkedListDestroy(expected);
    8.27 +    cxListDestroy(list);
    8.28 +    cxListDestroy(expected);
    8.29      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
    8.30  }
    8.31  
    8.32 @@ -624,8 +624,8 @@
    8.33      CxList *expected = cxLinkedListFromArray(cxTestingAllocator, cmp_int, sizeof(int), 3, exp);
    8.34      CU_ASSERT_TRUE(0 == cxListCompare(list, expected))
    8.35  
    8.36 -    cxLinkedListDestroy(list);
    8.37 -    cxLinkedListDestroy(expected);
    8.38 +    cxListDestroy(list);
    8.39 +    cxListDestroy(expected);
    8.40      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
    8.41  }
    8.42  
    8.43 @@ -655,7 +655,7 @@
    8.44      CU_ASSERT_EQUAL(*(int *) cxListAt(list, 1), 10)
    8.45      CU_ASSERT_EQUAL(*(int *) cxListAt(list, 2), 11)
    8.46  
    8.47 -    cxLinkedListDestroy(list);
    8.48 +    cxListDestroy(list);
    8.49      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
    8.50  }
    8.51  
    8.52 @@ -686,8 +686,8 @@
    8.53      CxList *expected = cxLinkedListFromArray(cxTestingAllocator, cmp_int, sizeof(int), 4, exp);
    8.54      CU_ASSERT_TRUE(0 == cxListCompare(list, expected))
    8.55  
    8.56 -    cxLinkedListDestroy(list);
    8.57 -    cxLinkedListDestroy(expected);
    8.58 +    cxListDestroy(list);
    8.59 +    cxListDestroy(expected);
    8.60      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
    8.61  }
    8.62  
    8.63 @@ -716,7 +716,7 @@
    8.64      CU_ASSERT_EQUAL(*(int *) cxListAt(list, 2), 5)
    8.65      CU_ASSERT_EQUAL(*(int *) cxListAt(list, 3), 42)
    8.66  
    8.67 -    cxLinkedListDestroy(list);
    8.68 +    cxListDestroy(list);
    8.69      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
    8.70  }
    8.71  
    8.72 @@ -756,7 +756,7 @@
    8.73  
    8.74      CU_ASSERT_NOT_EQUAL(cxListRemove(list, 0), 0)
    8.75  
    8.76 -    cxLinkedListDestroy(list);
    8.77 +    cxListDestroy(list);
    8.78      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
    8.79  }
    8.80  
    8.81 @@ -800,7 +800,7 @@
    8.82  
    8.83      CU_ASSERT_NOT_EQUAL(cxListRemove(list, 0), 0)
    8.84  
    8.85 -    cxLinkedListDestroy(list);
    8.86 +    cxListDestroy(list);
    8.87      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
    8.88  }
    8.89  
    8.90 @@ -816,7 +816,7 @@
    8.91      CU_ASSERT_EQUAL(*(int *) cxListAt(list, 2), 13)
    8.92      CU_ASSERT_PTR_NULL(cxListAt(list, 3))
    8.93  
    8.94 -    cxLinkedListDestroy(list);
    8.95 +    cxListDestroy(list);
    8.96      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
    8.97  }
    8.98  
    8.99 @@ -835,7 +835,7 @@
   8.100      CU_ASSERT_EQUAL(*(int *) cxListAt(list, 2), 13)
   8.101      CU_ASSERT_PTR_NULL(cxListAt(list, 3))
   8.102  
   8.103 -    cxLinkedListDestroy(list);
   8.104 +    cxListDestroy(list);
   8.105      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
   8.106  }
   8.107  
   8.108 @@ -861,7 +861,7 @@
   8.109      criteria = -5;
   8.110      CU_ASSERT_EQUAL(cxListFind(list, &criteria), 3)
   8.111  
   8.112 -    cxLinkedListDestroy(list);
   8.113 +    cxListDestroy(list);
   8.114      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
   8.115  }
   8.116  
   8.117 @@ -891,7 +891,7 @@
   8.118      b = -5;
   8.119      CU_ASSERT_EQUAL(cxListFind(list, &criteria), 1)
   8.120  
   8.121 -    cxLinkedListDestroy(list);
   8.122 +    cxListDestroy(list);
   8.123      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
   8.124  }
   8.125  
   8.126 @@ -921,8 +921,8 @@
   8.127      cxListSort(list);
   8.128      CU_ASSERT_TRUE(0 == cxListCompare(list, exp))
   8.129  
   8.130 -    cxLinkedListDestroy(list);
   8.131 -    cxLinkedListDestroy(exp);
   8.132 +    cxListDestroy(list);
   8.133 +    cxListDestroy(exp);
   8.134      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
   8.135  }
   8.136  
   8.137 @@ -958,7 +958,7 @@
   8.138          CU_ASSERT_EQUAL(*(int *) cxListAt(list, i), expected[i])
   8.139      }
   8.140  
   8.141 -    cxLinkedListDestroy(list);
   8.142 +    cxListDestroy(list);
   8.143      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
   8.144  }
   8.145  
   8.146 @@ -978,7 +978,7 @@
   8.147      CU_ASSERT_EQUAL(*(int *) cxListAt(list, 2), 4)
   8.148      CU_ASSERT_EQUAL(*(int *) cxListAt(list, 3), 6)
   8.149      CU_ASSERT_EQUAL(*(int *) cxListAt(list, 4), 8)
   8.150 -    cxLinkedListDestroy(list);
   8.151 +    cxListDestroy(list);
   8.152      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
   8.153  }
   8.154  
   8.155 @@ -1041,8 +1041,8 @@
   8.156                                               cmp_int, sizeof(int), 10, expdata);
   8.157  
   8.158      CU_ASSERT_EQUAL(0, cxListCompare(list, expected))
   8.159 -    cxLinkedListDestroy(list);
   8.160 -    cxLinkedListDestroy(expected);
   8.161 +    cxListDestroy(list);
   8.162 +    cxListDestroy(expected);
   8.163      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
   8.164  }
   8.165  
   8.166 @@ -1083,7 +1083,7 @@
   8.167          CU_ASSERT_EQUAL(*(int *) cxListAt(list, i), expdata[i])
   8.168      }
   8.169  
   8.170 -    cxLinkedListDestroy(list);
   8.171 +    cxListDestroy(list);
   8.172      CU_ASSERT_TRUE(cxTestingAllocatorVerify())
   8.173  }
   8.174  

mercurial