# HG changeset patch # User Mike Becker # Date 1716488968 -7200 # Node ID fe0d69d72bcd62c730210397931cef7983e57153 # Parent d4baf4dd55c357627587faae92b9346ef9d5c702 fix members inherited by macro or include are not documented diff -r d4baf4dd55c3 -r fe0d69d72bcd CHANGELOG --- a/CHANGELOG Thu May 23 19:29:14 2024 +0200 +++ b/CHANGELOG Thu May 23 20:29:28 2024 +0200 @@ -11,7 +11,7 @@ * adds cx_sprintf() and several more variants * adds runtime constants to read out the actual SBO sizes * adds improved version of UCX 2 Test framework (now a self-contained header) - * the cx_compare_func symbol is now also declared by compare.h + * the cx_compare_func symbol is now declared by compare.h * fixes wrong link from UCX 2 documentation to UCX 3 documentation * fixes critical bug that produced wrong results when comparing lists of different type but same size * removes flag_removal function from iterator (unfortunately breaks binary compatibility) diff -r d4baf4dd55c3 -r fe0d69d72bcd docs/src/features.md --- a/docs/src/features.md Thu May 23 19:29:14 2024 +0200 +++ b/docs/src/features.md Thu May 23 20:29:28 2024 +0200 @@ -187,7 +187,7 @@ `slot` and `kv_data` fields (for example when iterating over maps). If the predefined fields are insufficient for your use case, you can alternatively create your own iterator structure -and place the `CX_ITERATOR_BASE` macro inside. +and place the `CX_ITERATOR_BASE` macro as first member of that structure. Usually an iterator is not mutating the collection it is iterating over. In some programming languages it is even disallowed to change the collection while iterating with foreach. @@ -204,10 +204,10 @@ Collections in UCX 3 have several common features. If you want to implement an own collection data type that uses the same features, you can use the -`CX_COLLECTION_MEMBERS` macro at the beginning of your struct to roll out all members a usual UCX collection has. +`CX_COLLECTION_BASE` macro at the beginning of your struct to roll out all members a usual UCX collection has. ```c struct my_fancy_collection_s { - CX_COLLECTION_MEMBERS + CX_COLLECTION_BASE; struct my_collection_data_s *data; }; ``` @@ -225,21 +225,11 @@ *Header file:* [list.h](api/list_8h.html) -This header defines a common interface for all list implementations, which is basically as simple as the following -structure. -```c -struct cx_list_s { - CX_COLLECTION_MEMBERS // size, capacity, etc. - cx_list_class const *cl; // The list class definition -}; -``` -The actual structure contains one more class pointer that is used when wrapping a list into a pointer-aware list -with `cxListStorePointers()`. What this means, is that - if you want to implement your own list structure - you -only need to cover the case where the list is storing copies of your objects. +This header defines a common interface for all list implementations. -UCX comes with two common list implementations (linked list and array list) that should cover most use cases. -But if you feel the need to implement an own list, the only thing you need to do is to define a struct where -`struct cx_list_s`, and set an appropriate list class that implements the functionality. +UCX already comes with two common list implementations (linked list and array list) that should cover most use cases. +But if you feel the need to implement an own list, the only thing you need to do is to define a struct with a +`struct cx_list_s` as first member, and set an appropriate list class that implements the functionality. It is strongly recommended that this class is shared among all instances of the same list type, because otherwise the `cxListCompare` function cannot use the optimized implementation of your class and will instead fall back to using iterators to compare the contents element-wise. diff -r d4baf4dd55c3 -r fe0d69d72bcd src/Makefile --- a/src/Makefile Thu May 23 19:29:14 2024 +0200 +++ b/src/Makefile Thu May 23 20:29:28 2024 +0200 @@ -67,7 +67,8 @@ $(CC) -o $@ $(CFLAGS) -c $< $(build_dir)/array_list$(OBJ_EXT): array_list.c cx/array_list.h cx/list.h \ - cx/common.h cx/collection.h cx/allocator.h cx/iterator.h cx/compare.h + cx/common.h cx/collection.h cx/allocator.h cx/iterator.h cx/compare.h \ + cx/compare.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -85,8 +86,8 @@ $(CC) -o $@ $(CFLAGS) -c $< $(build_dir)/hash_map$(OBJ_EXT): hash_map.c cx/hash_map.h cx/map.h \ - cx/common.h cx/collection.h cx/allocator.h cx/iterator.h cx/string.h \ - cx/hash_key.h cx/utils.h + cx/common.h cx/collection.h cx/allocator.h cx/iterator.h cx/compare.h \ + cx/string.h cx/hash_key.h cx/utils.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -96,17 +97,17 @@ $(build_dir)/linked_list$(OBJ_EXT): linked_list.c cx/linked_list.h \ cx/common.h cx/list.h cx/collection.h cx/allocator.h cx/iterator.h \ - cx/utils.h cx/compare.h + cx/compare.h cx/utils.h cx/compare.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(build_dir)/list$(OBJ_EXT): list.c cx/list.h cx/common.h cx/collection.h \ - cx/allocator.h cx/iterator.h + cx/allocator.h cx/iterator.h cx/compare.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(build_dir)/map$(OBJ_EXT): map.c cx/map.h cx/common.h cx/collection.h \ - cx/allocator.h cx/iterator.h cx/string.h cx/hash_key.h + cx/allocator.h cx/iterator.h cx/compare.h cx/string.h cx/hash_key.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -130,7 +131,7 @@ $(CC) -o $@ $(CFLAGS) -c $< $(build_dir)/tree$(OBJ_EXT): tree.c cx/tree.h cx/common.h cx/iterator.h \ - cx/array_list.h cx/list.h cx/collection.h cx/allocator.h + cx/array_list.h cx/list.h cx/collection.h cx/allocator.h cx/compare.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< diff -r d4baf4dd55c3 -r fe0d69d72bcd src/array_list.c --- a/src/array_list.c Thu May 23 19:29:14 2024 +0200 +++ b/src/array_list.c Thu May 23 20:29:28 2024 +0200 @@ -191,21 +191,21 @@ char *ptr = arl->data; - if (list->simple_destructor) { - for (size_t i = 0; i < list->size; i++) { + if (list->base.simple_destructor) { + for (size_t i = 0; i < list->base.size; i++) { cx_invoke_simple_destructor(list, ptr); - ptr += list->item_size; + ptr += list->base.item_size; } } - if (list->advanced_destructor) { - for (size_t i = 0; i < list->size; i++) { + if (list->base.advanced_destructor) { + for (size_t i = 0; i < list->base.size; i++) { cx_invoke_advanced_destructor(list, ptr); - ptr += list->item_size; + ptr += list->base.item_size; } } - cxFree(list->allocator, arl->data); - cxFree(list->allocator, list); + cxFree(list->base.allocator, arl->data); + cxFree(list->base.allocator, list); } static size_t cx_arl_insert_array( @@ -215,25 +215,25 @@ size_t n ) { // out of bounds and special case check - if (index > list->size || n == 0) return 0; + if (index > list->base.size || n == 0) return 0; // get a correctly typed pointer to the list cx_array_list *arl = (cx_array_list *) list; // do we need to move some elements? - if (index < list->size) { + if (index < list->base.size) { char const *first_to_move = (char const *) arl->data; - first_to_move += index * list->item_size; - size_t elems_to_move = list->size - index; + first_to_move += index * list->base.item_size; + size_t elems_to_move = list->base.size - index; size_t start_of_moved = index + n; if (CX_ARRAY_SUCCESS != cx_array_copy( &arl->data, - &list->size, + &list->base.size, &arl->capacity, start_of_moved, first_to_move, - list->item_size, + list->base.item_size, elems_to_move, &arl->reallocator )) { @@ -249,11 +249,11 @@ // place the new elements if (CX_ARRAY_SUCCESS == cx_array_copy( &arl->data, - &list->size, + &list->base.size, &arl->capacity, index, array, - list->item_size, + list->base.item_size, n, &arl->reallocator )) { @@ -278,7 +278,7 @@ int prepend ) { struct cx_list_s *list = iter->src_handle.m; - if (iter->index < list->size) { + if (iter->index < list->base.size) { int result = cx_arl_insert_element( list, iter->index + 1 - prepend, @@ -286,12 +286,12 @@ ); if (result == 0 && prepend != 0) { iter->index++; - iter->elem_handle = ((char *) iter->elem_handle) + list->item_size; + iter->elem_handle = ((char *) iter->elem_handle) + list->base.item_size; } return result; } else { - int result = cx_arl_insert_element(list, list->size, elem); - iter->index = list->size; + int result = cx_arl_insert_element(list, list->base.size, elem); + iter->index = list->base.size; return result; } } @@ -303,28 +303,28 @@ cx_array_list *arl = (cx_array_list *) list; // out-of-bounds check - if (index >= list->size) { + if (index >= list->base.size) { return 1; } // content destruction - cx_invoke_destructor(list, ((char *) arl->data) + index * list->item_size); + cx_invoke_destructor(list, ((char *) arl->data) + index * list->base.item_size); // short-circuit removal of last element - if (index == list->size - 1) { - list->size--; + if (index == list->base.size - 1) { + list->base.size--; return 0; } // just move the elements starting at index to the left int result = cx_array_copy( &arl->data, - &list->size, + &list->base.size, &arl->capacity, index, - ((char *) arl->data) + (index + 1) * list->item_size, - list->item_size, - list->size - index - 1, + ((char *) arl->data) + (index + 1) * list->base.item_size, + list->base.item_size, + list->base.size - index - 1, &arl->reallocator ); @@ -332,32 +332,32 @@ assert(result == 0); // decrease the size - list->size--; + list->base.size--; return 0; } static void cx_arl_clear(struct cx_list_s *list) { - if (list->size == 0) return; + if (list->base.size == 0) return; cx_array_list *arl = (cx_array_list *) list; char *ptr = arl->data; - if (list->simple_destructor) { - for (size_t i = 0; i < list->size; i++) { + if (list->base.simple_destructor) { + for (size_t i = 0; i < list->base.size; i++) { cx_invoke_simple_destructor(list, ptr); - ptr += list->item_size; + ptr += list->base.item_size; } } - if (list->advanced_destructor) { - for (size_t i = 0; i < list->size; i++) { + if (list->base.advanced_destructor) { + for (size_t i = 0; i < list->base.size; i++) { cx_invoke_advanced_destructor(list, ptr); - ptr += list->item_size; + ptr += list->base.item_size; } } - memset(arl->data, 0, list->size * list->item_size); - list->size = 0; + memset(arl->data, 0, list->base.size * list->base.item_size); + list->base.size = 0; } static int cx_arl_swap( @@ -365,9 +365,9 @@ size_t i, size_t j ) { - if (i >= list->size || j >= list->size) return 1; + if (i >= list->base.size || j >= list->base.size) return 1; cx_array_list *arl = (cx_array_list *) list; - cx_array_swap(arl->data, list->item_size, i, j); + cx_array_swap(arl->data, list->base.item_size, i, j); return 0; } @@ -375,10 +375,10 @@ struct cx_list_s const *list, size_t index ) { - if (index < list->size) { + if (index < list->base.size) { cx_array_list const *arl = (cx_array_list const *) list; char *space = arl->data; - return space + index * list->item_size; + return space + index * list->base.item_size; } else { return NULL; } @@ -389,12 +389,12 @@ void const *elem, bool remove ) { - assert(list->cmpfunc != NULL); - assert(list->size < SIZE_MAX / 2); + assert(list->base.cmpfunc != NULL); + assert(list->base.size < SIZE_MAX / 2); char *cur = ((cx_array_list const *) list)->data; - for (ssize_t i = 0; i < (ssize_t) list->size; i++) { - if (0 == list->cmpfunc(elem, cur)) { + for (ssize_t i = 0; i < (ssize_t) list->base.size; i++) { + if (0 == list->base.cmpfunc(elem, cur)) { if (remove) { if (0 == cx_arl_remove(list, i)) { return i; @@ -405,18 +405,18 @@ return i; } } - cur += list->item_size; + cur += list->base.item_size; } return -1; } static void cx_arl_sort(struct cx_list_s *list) { - assert(list->cmpfunc != NULL); + assert(list->base.cmpfunc != NULL); qsort(((cx_array_list *) list)->data, - list->size, - list->item_size, - list->cmpfunc + list->base.size, + list->base.item_size, + list->base.cmpfunc ); } @@ -424,37 +424,37 @@ struct cx_list_s const *list, struct cx_list_s const *other ) { - assert(list->cmpfunc != NULL); - if (list->size == other->size) { + assert(list->base.cmpfunc != NULL); + if (list->base.size == other->base.size) { char const *left = ((cx_array_list const *) list)->data; char const *right = ((cx_array_list const *) other)->data; - for (size_t i = 0; i < list->size; i++) { - int d = list->cmpfunc(left, right); + for (size_t i = 0; i < list->base.size; i++) { + int d = list->base.cmpfunc(left, right); if (d != 0) { return d; } - left += list->item_size; - right += other->item_size; + left += list->base.item_size; + right += other->base.item_size; } return 0; } else { - return list->size < other->size ? -1 : 1; + return list->base.size < other->base.size ? -1 : 1; } } static void cx_arl_reverse(struct cx_list_s *list) { - if (list->size < 2) return; + if (list->base.size < 2) return; void *data = ((cx_array_list const *) list)->data; - size_t half = list->size / 2; + size_t half = list->base.size / 2; for (size_t i = 0; i < half; i++) { - cx_array_swap(data, list->item_size, i, list->size - 1 - i); + cx_array_swap(data, list->base.item_size, i, list->base.size - 1 - i); } } static bool cx_arl_iter_valid(void const *it) { struct cx_iterator_s const *iter = it; struct cx_list_s const *list = iter->src_handle.c; - return iter->index < list->size; + return iter->index < list->base.size; } static void *cx_arl_iter_current(void const *it) { @@ -464,28 +464,28 @@ static void cx_arl_iter_next(void *it) { struct cx_iterator_s *iter = it; - if (iter->remove) { - iter->remove = false; + if (iter->base.remove) { + iter->base.remove = false; cx_arl_remove(iter->src_handle.m, iter->index); } else { iter->index++; iter->elem_handle = ((char *) iter->elem_handle) - + ((struct cx_list_s const *) iter->src_handle.c)->item_size; + + ((struct cx_list_s const *) iter->src_handle.c)->base.item_size; } } static void cx_arl_iter_prev(void *it) { struct cx_iterator_s *iter = it; cx_array_list const* list = iter->src_handle.c; - if (iter->remove) { - iter->remove = false; + if (iter->base.remove) { + iter->base.remove = false; cx_arl_remove(iter->src_handle.m, iter->index); } iter->index--; - if (iter->index < list->base.size) { + if (iter->index < list->base.base.size) { iter->elem_handle = ((char *) list->data) - + iter->index * list->base.item_size; + + iter->index * list->base.base.item_size; } } @@ -500,13 +500,13 @@ iter.index = index; iter.src_handle.c = list; iter.elem_handle = cx_arl_at(list, index); - iter.elem_size = list->item_size; - iter.elem_count = list->size; - iter.valid = cx_arl_iter_valid; - iter.current = cx_arl_iter_current; - iter.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next; - iter.remove = false; - iter.mutating = false; + iter.elem_size = list->base.item_size; + iter.elem_count = list->base.size; + iter.base.valid = cx_arl_iter_valid; + iter.base.current = cx_arl_iter_current; + iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next; + iter.base.remove = false; + iter.base.mutating = false; return iter; } @@ -541,15 +541,15 @@ if (list == NULL) return NULL; list->base.cl = &cx_array_list_class; - list->base.allocator = allocator; + list->base.base.allocator = allocator; list->capacity = initial_capacity; if (item_size > 0) { - list->base.item_size = item_size; - list->base.cmpfunc = comparator; + list->base.base.item_size = item_size; + list->base.base.cmpfunc = comparator; } else { item_size = sizeof(void *); - list->base.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator; + list->base.base.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator; cxListStorePointers((CxList *) list); } diff -r d4baf4dd55c3 -r fe0d69d72bcd src/cx/collection.h --- a/src/cx/collection.h Thu May 23 19:29:14 2024 +0200 +++ b/src/cx/collection.h Thu May 23 20:29:28 2024 +0200 @@ -38,6 +38,7 @@ #include "allocator.h" #include "iterator.h" +#include "compare.h" #ifdef __cplusplus extern "C" { @@ -48,60 +49,55 @@ */ #define CX_STORE_POINTERS 0 -#ifndef CX_COMPARE_FUNC_DEFINED -#define CX_COMPARE_FUNC_DEFINED /** - * A comparator function comparing two collection elements. + * Base attributes of a collection. */ -typedef int(*cx_compare_func)( - void const *left, - void const *right -); -#endif // CX_COMPARE_FUNC_DEFINED +struct cx_collection_s { + /** + * The allocator to use. + */ + CxAllocator const *allocator; + /** + * The comparator function for the elements. + */ + cx_compare_func cmpfunc; + /** + * The size of each element. + */ + size_t item_size; + /** + * The number of currently stored elements. + */ + size_t size; + /** + * An optional simple destructor for the collection's elements. + * + * @attention Read the documentation of the particular collection implementation + * whether this destructor shall only destroy the contents or also free the memory. + */ + cx_destructor_func simple_destructor; + /** + * An optional advanced destructor for the collection's elements. + * + * @attention Read the documentation of the particular collection implementation + * whether this destructor shall only destroy the contents or also free the memory. + */ + cx_destructor_func2 advanced_destructor; + /** + * The pointer to additional data that is passed to the advanced destructor. + */ + void *destructor_data; + /** + * Indicates if this list is supposed to store pointers + * instead of copies of the actual objects. + */ + bool store_pointer; +}; /** * Use this macro to declare common members for a collection structure. */ -#define CX_COLLECTION_MEMBERS \ - /** \ - * The allocator to use. \ - */ \ - CxAllocator const *allocator; \ - /** \ - * The comparator function for the elements. \ - */ \ - cx_compare_func cmpfunc; \ - /** \ - * The size of each element. \ - */ \ - size_t item_size; \ - /** \ - * The number of currently stored elements. \ - */ \ - size_t size; \ - /** \ - * An optional simple destructor for the collection's elements. \ - * \ - * @attention Read the documentation of the particular collection implementation \ - * whether this destructor shall only destroy the contents or also free the memory. \ - */ \ - cx_destructor_func simple_destructor; \ - /** \ - * An optional advanced destructor for the collection's elements. \ - * \ - * @attention Read the documentation of the particular collection implementation \ - * whether this destructor shall only destroy the contents or also free the memory. \ - */ \ - cx_destructor_func2 advanced_destructor; \ - /** \ - * The pointer to additional data that is passed to the advanced destructor. \ - */ \ - void *destructor_data; \ - /** \ - * Indicates if this instance of a collection is supposed to store pointers \ - * instead of copies of the actual objects. \ - */ \ - bool store_pointer; +#define CX_COLLECTION_BASE struct cx_collection_s base /** * Invokes the simple destructor function for a specific element. @@ -113,7 +109,7 @@ * @param e the element */ #define cx_invoke_simple_destructor(c, e) \ - (c)->simple_destructor((c)->store_pointer ? (*((void **) (e))) : (e)) + (c)->base.simple_destructor((c)->base.store_pointer ? (*((void **) (e))) : (e)) /** * Invokes the advanced destructor function for a specific element. @@ -125,8 +121,8 @@ * @param e the element */ #define cx_invoke_advanced_destructor(c, e) \ - (c)->advanced_destructor((c)->destructor_data, \ - (c)->store_pointer ? (*((void **) (e))) : (e)) + (c)->base.advanced_destructor((c)->base.destructor_data, \ + (c)->base.store_pointer ? (*((void **) (e))) : (e)) /** @@ -139,8 +135,8 @@ * @param e the element */ #define cx_invoke_destructor(c, e) \ - if ((c)->simple_destructor) cx_invoke_simple_destructor(c,e); \ - if ((c)->advanced_destructor) cx_invoke_advanced_destructor(c,e) + if ((c)->base.simple_destructor) cx_invoke_simple_destructor(c,e); \ + if ((c)->base.advanced_destructor) cx_invoke_advanced_destructor(c,e) #ifdef __cplusplus } // extern "C" diff -r d4baf4dd55c3 -r fe0d69d72bcd src/cx/collection_base.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cx/collection_base.h Thu May 23 20:29:28 2024 +0200 @@ -0,0 +1,35 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 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. + */ +/** + * \file collection_base.h + * \brief Include this file in your collection struct to declare common members. + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + diff -r d4baf4dd55c3 -r fe0d69d72bcd src/cx/iterator.h --- a/src/cx/iterator.h Thu May 23 19:29:14 2024 +0200 +++ b/src/cx/iterator.h Thu May 23 20:29:28 2024 +0200 @@ -38,45 +38,54 @@ #include "common.h" -#define CX_ITERATOR_BASE \ - /** \ - * True iff the iterator points to valid data. \ - */ \ - __attribute__ ((__nonnull__)) \ - bool (*valid)(void const *); \ - /** \ - * Returns a pointer to the current element. \ - * \ - * When valid returns false, the behavior of this function is undefined. \ - */ \ - __attribute__ ((__nonnull__)) \ - void *(*current)(void const *); \ - /** \ - * Original implementation in case the function needs to be wrapped. \ - */ \ - __attribute__ ((__nonnull__)) \ - void *(*current_impl)(void const *); \ - /** \ - * Advances the iterator. \ - * \ - * When valid returns false, the behavior of this function is undefined. \ - */ \ - __attribute__ ((__nonnull__)) \ - void (*next)(void *); \ - /** \ - * Indicates whether this iterator may remove elements. \ - */ \ - bool mutating; \ - /** \ - * Internal flag for removing the current element when advancing. \ - */ \ +struct cx_iterator_base_s { + /** + * True iff the iterator points to valid data. + */ + __attribute__ ((__nonnull__)) + bool (*valid)(void const *); + + /** + * Returns a pointer to the current element. + * + * When valid returns false, the behavior of this function is undefined. + */ + __attribute__ ((__nonnull__)) + void *(*current)(void const *); + + /** + * Original implementation in case the function needs to be wrapped. + */ + __attribute__ ((__nonnull__)) + void *(*current_impl)(void const *); + + /** + * Advances the iterator. + * + * When valid returns false, the behavior of this function is undefined. + */ + __attribute__ ((__nonnull__)) + void (*next)(void *); + /** + * Indicates whether this iterator may remove elements. + */ + bool mutating; + /** + * Internal flag for removing the current element when advancing. + */ bool remove; +}; + +/** + * Declares base attributes for an iterator. + */ +#define CX_ITERATOR_BASE struct cx_iterator_base_s base /** * Internal iterator struct - use CxIterator. */ struct cx_iterator_s { - CX_ITERATOR_BASE + CX_ITERATOR_BASE; /** * Handle for the current element. @@ -157,7 +166,7 @@ * @param iter the iterator * @return true iff the iterator points to valid data */ -#define cxIteratorValid(iter) (iter).valid(&(iter)) +#define cxIteratorValid(iter) (iter).base.valid(&(iter)) /** * Returns a pointer to the current element. @@ -167,21 +176,21 @@ * @param iter the iterator * @return a pointer to the current element */ -#define cxIteratorCurrent(iter) (iter).current(&iter) +#define cxIteratorCurrent(iter) (iter).base.current(&iter) /** * Advances the iterator to the next element. * * @param iter the iterator */ -#define cxIteratorNext(iter) (iter).next(&iter) +#define cxIteratorNext(iter) (iter).base.next(&iter) /** * Flags the current element for removal, if this iterator is mutating. * * @param iter the iterator */ -#define cxIteratorFlagRemoval(iter) (iter).remove |= (iter).mutating +#define cxIteratorFlagRemoval(iter) (iter).base.remove |= (iter).base.mutating /** * Loops over an iterator. diff -r d4baf4dd55c3 -r fe0d69d72bcd src/cx/list.h --- a/src/cx/list.h Thu May 23 19:29:14 2024 +0200 +++ b/src/cx/list.h Thu May 23 20:29:28 2024 +0200 @@ -52,7 +52,7 @@ * Structure for holding the base data of a list. */ struct cx_list_s { - CX_COLLECTION_MEMBERS + CX_COLLECTION_BASE; /** * The list class definition. */ @@ -213,7 +213,7 @@ */ __attribute__((__nonnull__)) static inline bool cxListIsStoringPointers(CxList const *list) { - return list->store_pointer; + return list->base.store_pointer; } /** @@ -224,7 +224,7 @@ */ __attribute__((__nonnull__)) static inline size_t cxListSize(CxList const *list) { - return list->size; + return list->base.size; } /** @@ -240,7 +240,7 @@ CxList *list, void const *elem ) { - return list->cl->insert_element(list, list->size, elem); + return list->cl->insert_element(list, list->base.size, elem); } /** @@ -265,7 +265,7 @@ void const *array, size_t n ) { - return list->cl->insert_array(list, list->size, array, n); + return list->cl->insert_array(list, list->base.size, array, n); } /** @@ -547,7 +547,7 @@ */ __attribute__((__nonnull__, __warn_unused_result__)) static inline CxIterator cxListBackwardsIterator(CxList const *list) { - return list->cl->iterator(list, list->size - 1, true); + return list->cl->iterator(list, list->base.size - 1, true); } /** @@ -562,7 +562,7 @@ */ __attribute__((__nonnull__, __warn_unused_result__)) static inline CxIterator cxListMutBackwardsIterator(CxList *list) { - return cxListMutBackwardsIteratorAt(list, list->size - 1); + return cxListMutBackwardsIteratorAt(list, list->base.size - 1); } /** diff -r d4baf4dd55c3 -r fe0d69d72bcd src/cx/map.h --- a/src/cx/map.h Thu May 23 19:29:14 2024 +0200 +++ b/src/cx/map.h Thu May 23 20:29:28 2024 +0200 @@ -56,7 +56,10 @@ /** Structure for the UCX map. */ struct cx_map_s { - CX_COLLECTION_MEMBERS + /** + * Base attributes. + */ + CX_COLLECTION_BASE; /** The map class definition. */ cx_map_class *cl; }; @@ -163,7 +166,7 @@ */ __attribute__((__nonnull__)) static inline void cxMapStoreObjects(CxMap *map) { - map->store_pointer = false; + map->base.store_pointer = false; } /** @@ -180,8 +183,8 @@ */ __attribute__((__nonnull__)) static inline void cxMapStorePointers(CxMap *map) { - map->store_pointer = true; - map->item_size = sizeof(void *); + map->base.store_pointer = true; + map->base.item_size = sizeof(void *); } @@ -1050,7 +1053,7 @@ CxMap *map, CxHashKey key ) { - return map->cl->remove(map, key, !map->store_pointer); + return map->cl->remove(map, key, !map->base.store_pointer); } /** @@ -1066,7 +1069,7 @@ CxMap *map, cxstring key ) { - return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer); + return map->cl->remove(map, cx_hash_key_cxstr(key), !map->base.store_pointer); } /** @@ -1082,7 +1085,7 @@ CxMap *map, cxmutstr key ) { - return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer); + return map->cl->remove(map, cx_hash_key_cxstr(key), !map->base.store_pointer); } /** @@ -1098,7 +1101,7 @@ CxMap *map, char const *key ) { - return map->cl->remove(map, cx_hash_key_str(key), !map->store_pointer); + return map->cl->remove(map, cx_hash_key_str(key), !map->base.store_pointer); } /** diff -r d4baf4dd55c3 -r fe0d69d72bcd src/cx/tree.h --- a/src/cx/tree.h Thu May 23 19:29:14 2024 +0200 +++ b/src/cx/tree.h Thu May 23 20:29:28 2024 +0200 @@ -58,7 +58,10 @@ * @see CxIterator */ typedef struct cx_tree_iterator_s { - CX_ITERATOR_BASE + /** + * Base members. + */ + CX_ITERATOR_BASE; /** * Indicates whether the subtree below the current node shall be skipped. */ @@ -154,7 +157,10 @@ * @see CxIterator */ typedef struct cx_tree_visitor_s { - CX_ITERATOR_BASE + /** + * Base members. + */ + CX_ITERATOR_BASE; /** * Indicates whether the subtree below the current node shall be skipped. */ diff -r d4baf4dd55c3 -r fe0d69d72bcd src/hash_map.c --- a/src/hash_map.c Thu May 23 19:29:14 2024 +0200 +++ b/src/hash_map.c Thu May 23 20:29:28 2024 +0200 @@ -53,9 +53,9 @@ // invoke the destructor cx_invoke_destructor(map, elem->data); // free the key data - cxFree(map->allocator, (void *) elem->key.data); + cxFree(map->base.allocator, (void *) elem->key.data); // free the node - cxFree(map->allocator, elem); + cxFree(map->base.allocator, elem); // proceed elem = next; } while (elem != NULL); @@ -64,7 +64,7 @@ hash_map->buckets[i] = NULL; } } - map->size = 0; + map->base.size = 0; } static void cx_hash_map_destructor(struct cx_map_s *map) { @@ -72,10 +72,10 @@ // free the buckets cx_hash_map_clear(map); - cxFree(map->allocator, hash_map->buckets); + cxFree(map->base.allocator, hash_map->buckets); // free the map structure - cxFree(map->allocator, map); + cxFree(map->base.allocator, map); } static int cx_hash_map_put( @@ -84,7 +84,7 @@ void *value ) { struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; - CxAllocator const *allocator = map->allocator; + CxAllocator const *allocator = map->base.allocator; unsigned hash = key.hash; if (hash == 0) { @@ -104,26 +104,26 @@ if (elm != NULL && elm->key.hash == hash && elm->key.len == key.len && memcmp(elm->key.data, key.data, key.len) == 0) { // overwrite existing element - if (map->store_pointer) { + if (map->base.store_pointer) { memcpy(elm->data, &value, sizeof(void *)); } else { - memcpy(elm->data, value, map->item_size); + memcpy(elm->data, value, map->base.item_size); } } else { // allocate new element struct cx_hash_map_element_s *e = cxMalloc( allocator, - sizeof(struct cx_hash_map_element_s) + map->item_size + sizeof(struct cx_hash_map_element_s) + map->base.item_size ); if (e == NULL) { return -1; } // write the value - if (map->store_pointer) { + if (map->base.store_pointer) { memcpy(e->data, &value, sizeof(void *)); } else { - memcpy(e->data, value, map->item_size); + memcpy(e->data, value, map->base.item_size); } // copy the key @@ -145,7 +145,7 @@ e->next = elm; // increase the size - map->size++; + map->base.size++; } return 0; @@ -164,10 +164,10 @@ prev->next = elm->next; } // free element - cxFree(hash_map->base.allocator, (void *) elm->key.data); - cxFree(hash_map->base.allocator, elm); + cxFree(hash_map->base.base.allocator, (void *) elm->key.data); + cxFree(hash_map->base.base.allocator, elm); // decrease size - hash_map->base.size--; + hash_map->base.base.size--; } /** @@ -203,7 +203,7 @@ if (destroy) { cx_invoke_destructor(map, elm->data); } else { - if (map->store_pointer) { + if (map->base.store_pointer) { data = *(void **) elm->data; } else { data = elm->data; @@ -254,7 +254,7 @@ struct cx_iterator_s const *iter = it; struct cx_hash_map_s const *map = iter->src_handle.c; struct cx_hash_map_element_s *elm = iter->elem_handle; - if (map->base.store_pointer) { + if (map->base.base.store_pointer) { return *(void **) elm->data; } else { return elm->data; @@ -272,10 +272,10 @@ struct cx_hash_map_s *map = iter->src_handle.m; // remove current element, if asked - if (iter->remove) { + if (iter->base.remove) { // clear the flag - iter->remove = false; + iter->base.remove = false; // determine the next element struct cx_hash_map_element_s *next = elm->next; @@ -315,7 +315,7 @@ iter->kv_data.value = NULL; } else { iter->kv_data.key = &elm->key; - if (map->base.store_pointer) { + if (map->base.base.store_pointer) { iter->kv_data.value = *(void **) elm->data; } else { iter->kv_data.value = elm->data; @@ -330,34 +330,34 @@ CxIterator iter; iter.src_handle.c = map; - iter.elem_count = map->size; + iter.elem_count = map->base.size; switch (type) { case CX_MAP_ITERATOR_PAIRS: iter.elem_size = sizeof(CxMapEntry); - iter.current = cx_hash_map_iter_current_entry; + iter.base.current = cx_hash_map_iter_current_entry; break; case CX_MAP_ITERATOR_KEYS: iter.elem_size = sizeof(CxHashKey); - iter.current = cx_hash_map_iter_current_key; + iter.base.current = cx_hash_map_iter_current_key; break; case CX_MAP_ITERATOR_VALUES: - iter.elem_size = map->item_size; - iter.current = cx_hash_map_iter_current_value; + iter.elem_size = map->base.item_size; + iter.base.current = cx_hash_map_iter_current_value; break; default: assert(false); } - iter.valid = cx_hash_map_iter_valid; - iter.next = cx_hash_map_iter_next; - iter.remove = false; - iter.mutating = false; + iter.base.valid = cx_hash_map_iter_valid; + iter.base.next = cx_hash_map_iter_next; + iter.base.remove = false; + iter.base.mutating = false; iter.slot = 0; iter.index = 0; - if (map->size > 0) { + if (map->base.size > 0) { struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; struct cx_hash_map_element_s *elm = hash_map->buckets[0]; while (elm == NULL) { @@ -365,7 +365,7 @@ } iter.elem_handle = elm; iter.kv_data.key = &elm->key; - if (map->store_pointer) { + if (map->base.store_pointer) { iter.kv_data.value = *(void **) elm->data; } else { iter.kv_data.value = elm->data; @@ -413,14 +413,14 @@ // initialize base members map->base.cl = &cx_hash_map_class; - map->base.allocator = allocator; + map->base.base.allocator = allocator; if (itemsize > 0) { - map->base.store_pointer = false; - map->base.item_size = itemsize; + map->base.base.store_pointer = false; + map->base.base.item_size = itemsize; } else { - map->base.store_pointer = true; - map->base.item_size = sizeof(void *); + map->base.base.store_pointer = true; + map->base.base.item_size = sizeof(void *); } return (CxMap *) map; @@ -428,11 +428,11 @@ int cxMapRehash(CxMap *map) { struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; - if (map->size > ((hash_map->bucket_count * 3) >> 2)) { + if (map->base.size > ((hash_map->bucket_count * 3) >> 2)) { - size_t new_bucket_count = (map->size * 5) >> 1; + size_t new_bucket_count = (map->base.size * 5) >> 1; struct cx_hash_map_element_s **new_buckets = cxCalloc( - map->allocator, + map->base.allocator, new_bucket_count, sizeof(struct cx_hash_map_element_s *) ); @@ -472,7 +472,7 @@ // assign result to the map hash_map->bucket_count = new_bucket_count; - cxFree(map->allocator, hash_map->buckets); + cxFree(map->base.allocator, hash_map->buckets); hash_map->buckets = new_buckets; } return 0; diff -r d4baf4dd55c3 -r fe0d69d72bcd src/iterator.c --- a/src/iterator.c Thu May 23 19:29:14 2024 +0200 +++ b/src/iterator.c Thu May 23 20:29:28 2024 +0200 @@ -42,8 +42,8 @@ static void cx_iter_next_fast(void *it) { struct cx_iterator_s *iter = it; - if (iter->remove) { - iter->remove = false; + if (iter->base.remove) { + iter->base.remove = false; iter->elem_count--; // only move the last element when we are not currently aiming // at the last element already @@ -60,8 +60,8 @@ static void cx_iter_next_slow(void *it) { struct cx_iterator_s *iter = it; - if (iter->remove) { - iter->remove = false; + if (iter->base.remove) { + iter->base.remove = false; iter->elem_count--; // number of elements to move @@ -92,11 +92,11 @@ iter.elem_handle = array; iter.elem_size = elem_size; iter.elem_count = array == NULL ? 0 : elem_count; - iter.valid = cx_iter_valid; - iter.current = cx_iter_current; - iter.next = remove_keeps_order ? cx_iter_next_slow : cx_iter_next_fast; - iter.remove = false; - iter.mutating = true; + iter.base.valid = cx_iter_valid; + iter.base.current = cx_iter_current; + iter.base.next = remove_keeps_order ? cx_iter_next_slow : cx_iter_next_fast; + iter.base.remove = false; + iter.base.mutating = true; return iter; } @@ -107,6 +107,6 @@ size_t elem_count ) { CxIterator iter = cxMutIterator((void*)array, elem_size, elem_count, false); - iter.mutating = false; + iter.base.mutating = false; return iter; } diff -r d4baf4dd55c3 -r fe0d69d72bcd src/linked_list.c --- a/src/linked_list.c Thu May 23 19:29:14 2024 +0200 +++ b/src/linked_list.c Thu May 23 20:29:28 2024 +0200 @@ -501,10 +501,10 @@ cx_linked_list const *list, size_t index ) { - if (index >= list->base.size) { + if (index >= list->base.base.size) { return NULL; - } else if (index > list->base.size / 2) { - return cx_linked_list_at(list->end, list->base.size - 1, CX_LL_LOC_PREV, index); + } else if (index > list->base.base.size / 2) { + return cx_linked_list_at(list->end, list->base.base.size - 1, CX_LL_LOC_PREV, index); } else { return cx_linked_list_at(list->begin, 0, CX_LL_LOC_NEXT, index); } @@ -517,15 +517,15 @@ ) { // create the new new_node - cx_linked_list_node *new_node = cxMalloc(list->allocator, - sizeof(cx_linked_list_node) + list->item_size); + cx_linked_list_node *new_node = cxMalloc(list->base.allocator, + sizeof(cx_linked_list_node) + list->base.item_size); // sortir if failed if (new_node == NULL) return 1; // initialize new new_node new_node->prev = new_node->next = NULL; - memcpy(new_node->payload, elem, list->item_size); + memcpy(new_node->payload, elem, list->base.item_size); // insert cx_linked_list *ll = (cx_linked_list *) list; @@ -536,7 +536,7 @@ ); // increase the size and return - list->size++; + list->base.size++; return 0; } @@ -547,7 +547,7 @@ size_t n ) { // out-of bounds and corner case check - if (index > list->size || n == 0) return 0; + if (index > list->base.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); @@ -566,7 +566,7 @@ // we can add the remaining nodes and immedately advance to the inserted node char const *source = array; for (size_t i = 1; i < n; i++) { - source += list->item_size; + source += list->base.item_size; if (0 != cx_ll_insert_at(list, node, source)) { return i; } @@ -601,27 +601,27 @@ CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); // adjust size - list->size--; + list->base.size--; // free and return - cxFree(list->allocator, node); + cxFree(list->base.allocator, node); return 0; } static void cx_ll_clear(struct cx_list_s *list) { - if (list->size == 0) return; + if (list->base.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->allocator, node); + cxFree(list->base.allocator, node); node = next; } ll->begin = ll->end = NULL; - list->size = 0; + list->base.size = 0; } #ifndef CX_LINKED_LIST_SWAP_SBO_SIZE @@ -634,12 +634,12 @@ size_t i, size_t j ) { - if (i >= list->size || j >= list->size) return 1; + if (i >= list->base.size || j >= list->base.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->size / 2; + size_t mid = list->base.size / 2; size_t left, right; if (i < j) { left = i; @@ -671,7 +671,7 @@ // chose the closest to begin / end size_t closest; size_t other; - size_t diff2boundary = list->size - right - 1; + size_t diff2boundary = list->base.size - right - 1; if (left <= diff2boundary) { closest = left; other = right; @@ -707,7 +707,7 @@ } } - if (list->item_size > CX_LINKED_LIST_SWAP_SBO_SIZE) { + if (list->base.item_size > CX_LINKED_LIST_SWAP_SBO_SIZE) { cx_linked_list_node *prev = nleft->prev; cx_linked_list_node *next = nright->next; cx_linked_list_node *midstart = nleft->next; @@ -739,9 +739,9 @@ } else { // swap payloads to avoid relinking char buf[CX_LINKED_LIST_SWAP_SBO_SIZE]; - memcpy(buf, nleft->payload, list->item_size); - memcpy(nleft->payload, nright->payload, list->item_size); - memcpy(nright->payload, buf, list->item_size); + memcpy(buf, nleft->payload, list->base.item_size); + memcpy(nleft->payload, nright->payload, list->base.item_size); + memcpy(nright->payload, buf, list->base.item_size); } return 0; @@ -768,21 +768,21 @@ (void **) &node, ll->begin, CX_LL_LOC_NEXT, CX_LL_LOC_DATA, - list->cmpfunc, elem + list->base.cmpfunc, elem ); if (node != NULL) { 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->size--; - cxFree(list->allocator, node); + list->base.size--; + cxFree(list->base.allocator, node); } return index; } else { return cx_linked_list_find( ((cx_linked_list *) list)->begin, CX_LL_LOC_NEXT, CX_LL_LOC_DATA, - list->cmpfunc, elem + list->base.cmpfunc, elem ); } } @@ -791,7 +791,7 @@ 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->cmpfunc); + list->base.cmpfunc); } static void cx_ll_reverse(struct cx_list_s *list) { @@ -807,7 +807,7 @@ 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->cmpfunc); + list->base.cmpfunc); } static bool cx_ll_iter_valid(void const *it) { @@ -817,8 +817,8 @@ static void cx_ll_iter_next(void *it) { struct cx_iterator_s *iter = it; - if (iter->remove) { - iter->remove = false; + 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; @@ -826,8 +826,8 @@ 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->size--; - cxFree(list->allocator, node); + list->base.size--; + cxFree(list->base.allocator, node); } else { iter->index++; cx_linked_list_node *node = iter->elem_handle; @@ -837,8 +837,8 @@ static void cx_ll_iter_prev(void *it) { struct cx_iterator_s *iter = it; - if (iter->remove) { - iter->remove = false; + 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; @@ -847,8 +847,8 @@ 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->size--; - cxFree(list->allocator, node); + list->base.size--; + cxFree(list->base.allocator, node); } else { iter->index--; cx_linked_list_node *node = iter->elem_handle; @@ -871,13 +871,13 @@ iter.index = index; iter.src_handle.c = list; iter.elem_handle = cx_ll_node_at((cx_linked_list const *) list, index); - iter.elem_size = list->item_size; - iter.elem_count = list->size; - iter.valid = cx_ll_iter_valid; - iter.current = cx_ll_iter_current; - iter.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next; - iter.mutating = false; - iter.remove = false; + iter.elem_size = list->base.item_size; + iter.elem_count = list->base.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; } @@ -895,8 +895,8 @@ iter->index += prepend * (0 == result); return result; } else { - int result = cx_ll_insert_element(list, list->size, elem); - iter->index = list->size; + int result = cx_ll_insert_element(list, list->base.size, elem); + iter->index = list->base.size; return result; } } @@ -908,11 +908,11 @@ while (node) { cx_invoke_destructor(list, node->payload); void *next = node->next; - cxFree(list->allocator, node); + cxFree(list->base.allocator, node); node = next; } - cxFree(list->allocator, list); + cxFree(list->base.allocator, list); } static cx_list_class cx_linked_list_class = { @@ -944,13 +944,13 @@ if (list == NULL) return NULL; list->base.cl = &cx_linked_list_class; - list->base.allocator = allocator; + list->base.base.allocator = allocator; if (item_size > 0) { - list->base.item_size = item_size; - list->base.cmpfunc = comparator; + list->base.base.item_size = item_size; + list->base.base.cmpfunc = comparator; } else { - list->base.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator; + list->base.base.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator; cxListStorePointers((CxList *) list); } diff -r d4baf4dd55c3 -r fe0d69d72bcd src/list.c --- a/src/list.c Thu May 23 19:29:14 2024 +0200 +++ b/src/list.c Thu May 23 20:29:28 2024 +0200 @@ -47,14 +47,14 @@ static void cx_pl_hack_cmpfunc(struct cx_list_s const *list) { // cast away const - this is the hacky thing - struct cx_list_s *l = (struct cx_list_s *) list; + struct cx_collection_s *l = (struct cx_collection_s*) &list->base; cx_pl_cmpfunc_impl = l->cmpfunc; l->cmpfunc = cx_pl_cmpfunc; } static void cx_pl_unhack_cmpfunc(struct cx_list_s const *list) { // cast away const - this is the hacky thing - struct cx_list_s *l = (struct cx_list_s *) list; + struct cx_collection_s *l = (struct cx_collection_s*) &list->base; l->cmpfunc = cx_pl_cmpfunc_impl; } @@ -148,7 +148,7 @@ static void *cx_pl_iter_current(void const *it) { struct cx_iterator_s const *iter = it; - void **ptr = iter->current_impl(it); + void **ptr = iter->base.current_impl(it); return ptr == NULL ? NULL : *ptr; } @@ -158,8 +158,8 @@ bool backwards ) { struct cx_iterator_s iter = list->climpl->iterator(list, index, backwards); - iter.current_impl = iter.current; - iter.current = cx_pl_iter_current; + iter.base.current_impl = iter.base.current; + iter.base.current = cx_pl_iter_current; return iter; } @@ -180,7 +180,7 @@ }; void cxListStoreObjects(CxList *list) { - list->store_pointer = false; + list->base.store_pointer = false; if (list->climpl != NULL) { list->cl = list->climpl; list->climpl = NULL; @@ -188,8 +188,8 @@ } void cxListStorePointers(CxList *list) { - list->item_size = sizeof(void *); - list->store_pointer = true; + list->base.item_size = sizeof(void *); + list->base.store_pointer = true; list->climpl = list->cl; list->cl = &cx_pointer_list_class; } @@ -221,7 +221,7 @@ __attribute__((__unused__)) struct cx_list_s const *list, struct cx_list_s const *other ) { - if (other->size == 0) return 0; + if (other->base.size == 0) return 0; return -1; } @@ -237,7 +237,7 @@ CxIterator iter = {0}; iter.src_handle.c = list; iter.index = index; - iter.valid = cx_emptyl_iter_valid; + iter.base.valid = cx_emptyl_iter_valid; return iter; } @@ -258,14 +258,16 @@ }; CxList cx_empty_list = { - NULL, - NULL, - 0, - 0, - NULL, - NULL, - NULL, - false, + { + NULL, + NULL, + 0, + 0, + NULL, + NULL, + NULL, + false + }, &cx_empty_list_class, NULL }; @@ -284,7 +286,7 @@ ) { if ( // if one is storing pointers but the other is not - (list->store_pointer ^ other->store_pointer) || + (list->base.store_pointer ^ other->base.store_pointer) || // if one class is wrapped but the other is not ((list->climpl == NULL) ^ (other->climpl == NULL)) || @@ -294,13 +296,13 @@ (other->climpl != NULL ? other->climpl->compare : other->cl->compare)) ) { // lists are definitely different - cannot use internal compare function - if (list->size == other->size) { + if (list->base.size == other->base.size) { CxIterator left = list->cl->iterator(list, 0, false); CxIterator right = other->cl->iterator(other, 0, false); - for (size_t i = 0; i < list->size; i++) { + for (size_t i = 0; i < list->base.size; i++) { void *leftValue = cxIteratorCurrent(left); void *rightValue = cxIteratorCurrent(right); - int d = list->cmpfunc(leftValue, rightValue); + int d = list->base.cmpfunc(leftValue, rightValue); if (d != 0) { return d; } @@ -309,7 +311,7 @@ } return 0; } else { - return list->size < other->size ? -1 : 1; + return list->base.size < other->base.size ? -1 : 1; } } else { // lists are compatible @@ -322,7 +324,7 @@ size_t index ) { CxIterator it = list->cl->iterator(list, index, false); - it.mutating = true; + it.base.mutating = true; return it; } @@ -331,6 +333,6 @@ size_t index ) { CxIterator it = list->cl->iterator(list, index, true); - it.mutating = true; + it.base.mutating = true; return it; } diff -r d4baf4dd55c3 -r fe0d69d72bcd src/map.c --- a/src/map.c Thu May 23 19:29:14 2024 +0200 +++ b/src/map.c Thu May 23 20:29:28 2024 +0200 @@ -52,7 +52,7 @@ ) { CxIterator iter = {0}; iter.src_handle.c = map; - iter.valid = cx_empty_map_iter_valid; + iter.base.valid = cx_empty_map_iter_valid; return iter; } @@ -66,14 +66,16 @@ }; CxMap cx_empty_map = { - NULL, - NULL, - 0, - 0, - NULL, - NULL, - NULL, - false, + { + NULL, + NULL, + 0, + 0, + NULL, + NULL, + NULL, + false + }, &cx_empty_map_class }; @@ -83,18 +85,18 @@ CxIterator cxMapMutIteratorValues(CxMap *map) { CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES); - it.mutating = true; + it.base.mutating = true; return it; } CxIterator cxMapMutIteratorKeys(CxMap *map) { CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS); - it.mutating = true; + it.base.mutating = true; return it; } CxIterator cxMapMutIterator(CxMap *map) { CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS); - it.mutating = true; + it.base.mutating = true; return it; } diff -r d4baf4dd55c3 -r fe0d69d72bcd src/tree.c --- a/src/tree.c Thu May 23 19:29:14 2024 +0200 +++ b/src/tree.c Thu May 23 20:29:28 2024 +0200 @@ -280,12 +280,12 @@ iter.skip = false; // assign base iterator functions - iter.mutating = false; - iter.remove = false; - iter.current_impl = NULL; - iter.valid = cx_tree_iter_valid; - iter.next = cx_tree_iter_next; - iter.current = cx_tree_iter_current; + iter.base.mutating = false; + iter.base.remove = false; + iter.base.current_impl = NULL; + iter.base.valid = cx_tree_iter_valid; + iter.base.next = cx_tree_iter_next; + iter.base.current = cx_tree_iter_current; return iter; } @@ -389,12 +389,12 @@ iter.queue_last = NULL; // assign base iterator functions - iter.mutating = false; - iter.remove = false; - iter.current_impl = NULL; - iter.valid = cx_tree_visitor_valid; - iter.next = cx_tree_visitor_next; - iter.current = cx_tree_visitor_current; + iter.base.mutating = false; + iter.base.remove = false; + iter.base.current_impl = NULL; + iter.base.valid = cx_tree_visitor_valid; + iter.base.next = cx_tree_visitor_next; + iter.base.current = cx_tree_visitor_current; return iter; } diff -r d4baf4dd55c3 -r fe0d69d72bcd tests/Makefile --- a/tests/Makefile Thu May 23 19:29:14 2024 +0200 +++ b/tests/Makefile Thu May 23 20:29:28 2024 +0200 @@ -73,8 +73,8 @@ $(TEST_DIR)/test_hash_map$(OBJ_EXT): test_hash_map.c ../src/cx/test.h \ util_allocator.h ../src/cx/allocator.h ../src/cx/common.h \ ../src/cx/hash_map.h ../src/cx/map.h ../src/cx/collection.h \ - ../src/cx/allocator.h ../src/cx/iterator.h ../src/cx/string.h \ - ../src/cx/hash_key.h + ../src/cx/allocator.h ../src/cx/iterator.h ../src/cx/compare.h \ + ../src/cx/string.h ../src/cx/hash_key.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -87,7 +87,7 @@ util_allocator.h ../src/cx/allocator.h ../src/cx/common.h \ ../src/cx/compare.h ../src/cx/utils.h ../src/cx/array_list.h \ ../src/cx/list.h ../src/cx/collection.h ../src/cx/allocator.h \ - ../src/cx/iterator.h ../src/cx/linked_list.h + ../src/cx/iterator.h ../src/cx/compare.h ../src/cx/linked_list.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< diff -r d4baf4dd55c3 -r fe0d69d72bcd tests/test_hash_map.c --- a/tests/test_hash_map.c Thu May 23 19:29:14 2024 +0200 +++ b/tests/test_hash_map.c Thu May 23 20:29:28 2024 +0200 @@ -42,19 +42,19 @@ for(size_t i = 0 ; i < hmap->bucket_count ; i++) { CX_TEST_ASSERT(hmap->buckets[i] == NULL); } - CX_TEST_ASSERT(map->item_size == 1); - CX_TEST_ASSERT(map->size == 0); - CX_TEST_ASSERT(map->allocator == allocator); - CX_TEST_ASSERT(!map->store_pointer); - CX_TEST_ASSERT(map->cmpfunc == NULL); - CX_TEST_ASSERT(map->simple_destructor == NULL); - CX_TEST_ASSERT(map->advanced_destructor == NULL); - CX_TEST_ASSERT(map->destructor_data == NULL); + CX_TEST_ASSERT(map->base.item_size == 1); + CX_TEST_ASSERT(map->base.size == 0); + CX_TEST_ASSERT(map->base.allocator == allocator); + CX_TEST_ASSERT(!map->base.store_pointer); + CX_TEST_ASSERT(map->base.cmpfunc == NULL); + CX_TEST_ASSERT(map->base.simple_destructor == NULL); + CX_TEST_ASSERT(map->base.advanced_destructor == NULL); + CX_TEST_ASSERT(map->base.destructor_data == NULL); cxMapStorePointers(map); - CX_TEST_ASSERT(map->store_pointer); - CX_TEST_ASSERT(map->item_size == sizeof(void *)); + CX_TEST_ASSERT(map->base.store_pointer); + CX_TEST_ASSERT(map->base.item_size == sizeof(void *)); cxMapStoreObjects(map); - CX_TEST_ASSERT(!map->store_pointer); + CX_TEST_ASSERT(!map->base.store_pointer); cxMapDestroy(map); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); @@ -73,10 +73,10 @@ for (size_t i = 0; i < hmap->bucket_count; i++) { CX_TEST_ASSERT(hmap->buckets[i] == NULL); } - CX_TEST_ASSERT(map->size == 0); - CX_TEST_ASSERT(map->allocator == allocator); - CX_TEST_ASSERT(map->store_pointer); - CX_TEST_ASSERT(map->item_size == sizeof(void *)); + CX_TEST_ASSERT(map->base.size == 0); + CX_TEST_ASSERT(map->base.allocator == allocator); + CX_TEST_ASSERT(map->base.store_pointer); + CX_TEST_ASSERT(map->base.item_size == sizeof(void *)); cxMapDestroy(map); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); @@ -106,7 +106,7 @@ int result = cxMapRehash(map); CX_TEST_ASSERT(result == 0); CX_TEST_ASSERT(((struct cx_hash_map_s *)map)->bucket_count == 25); - CX_TEST_ASSERT(map->size == 10); + CX_TEST_ASSERT(map->base.size == 10); CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key 1"), "val 1")); CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key 2"), "val 2")); @@ -161,11 +161,11 @@ cxMapPut(map, "key 2", (void *) "val 2"); cxMapPut(map, "key 3", (void *) "val 3"); - CX_TEST_ASSERT(map->size == 3); + CX_TEST_ASSERT(map->base.size == 3); cxMapClear(map); - CX_TEST_ASSERT(map->size == 0); + CX_TEST_ASSERT(map->base.size == 0); CX_TEST_ASSERT(cxMapGet(map, "key 1") == NULL); CX_TEST_ASSERT(cxMapGet(map, "key 2") == NULL); CX_TEST_ASSERT(cxMapGet(map, "key 3") == NULL); @@ -208,7 +208,7 @@ // remove a string cxMapRemove(map, "s2"); - CX_TEST_ASSERT(map->size == 3); + CX_TEST_ASSERT(map->base.size == 3); // iterate bool s3found = false, s4found = false, s5found = false; @@ -244,8 +244,8 @@ cx_foreach(CxMapEntry*, entry, iter) { if (((char const *)entry->key->data)[4] % 2 == 1) cxIteratorFlagRemoval(iter); } - CX_TEST_ASSERT(map->size == 3); - CX_TEST_ASSERT(iter.index == map->size); + CX_TEST_ASSERT(map->base.size == 3); + CX_TEST_ASSERT(iter.index == map->base.size); CX_TEST_ASSERT(cxMapGet(map, "key 1") == NULL); CX_TEST_ASSERT(cxMapGet(map, "key 2") != NULL); @@ -348,7 +348,7 @@ CxAllocator *allocator = &talloc.base; CX_TEST_DO { CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0); - map->simple_destructor = test_simple_destructor; + map->base.simple_destructor = test_simple_destructor; CX_TEST_CALL_SUBROUTINE(verify_any_destructor, map); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); } @@ -361,7 +361,7 @@ CxAllocator *allocator = &talloc.base; CX_TEST_DO { CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0); - map->advanced_destructor = test_advanced_destructor; + map->base.advanced_destructor = test_advanced_destructor; CX_TEST_CALL_SUBROUTINE(verify_any_destructor, map); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); } @@ -370,7 +370,7 @@ CX_TEST(test_empty_map_size) { CX_TEST_DO { - CX_TEST_ASSERT(cxEmptyMap->size == 0); + CX_TEST_ASSERT(cxEmptyMap->base.size == 0); } } @@ -430,7 +430,7 @@ cxMapPut(map, cx_mutstr("foo"), "bar"); cxMapPut(map, cx_str("hallo"), "welt"); - CX_TEST_ASSERT(map->size == 3); + CX_TEST_ASSERT(map->base.size == 3); CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "test"), "test")); CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "foo"), "bar")); CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "hallo"), "welt")); @@ -441,16 +441,16 @@ cxMapDetach(map, hallo); cxMapPut(map, cx_hash_key_str("key"), "value"); - CX_TEST_ASSERT(map->size == 2); + CX_TEST_ASSERT(map->base.size == 2); CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key"), "value")); CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "foo"), "bar")); void *r; r = cxMapRemoveAndGet(map, "key"); r = cxMapRemoveAndGet(map, cx_str("foo")); - if (r != NULL) map->size = 47; + if (r != NULL) map->base.size = 47; - CX_TEST_ASSERT(map->size == 0); + CX_TEST_ASSERT(map->base.size == 0); cxMapDestroy(map); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); @@ -539,21 +539,21 @@ static CX_TEST_SUBROUTINE(verify_map_contents, CxMap *map) { // verify that the reference map has same size (i.e. no other keys are mapped) - CX_TEST_ASSERT(map->size == test_map_reference_size()); + CX_TEST_ASSERT(map->base.size == test_map_reference_size()); // verify key iterator { // collect the keys from the map iterator CxIterator keyiter = cxMapIteratorKeys(map); CX_TEST_ASSERT(keyiter.elem_size == sizeof(CxHashKey)); - CX_TEST_ASSERT(keyiter.elem_count == map->size); - CxHashKey *keys = calloc(map->size, sizeof(CxHashKey)); + CX_TEST_ASSERT(keyiter.elem_count == map->base.size); + CxHashKey *keys = calloc(map->base.size, sizeof(CxHashKey)); cx_foreach(CxHashKey*, elem, keyiter) { keys[keyiter.index] = *elem; } - CX_TEST_ASSERT(keyiter.index == map->size); + CX_TEST_ASSERT(keyiter.index == map->base.size); // verify that all keys are mapped to values in reference map - for (size_t i = 0 ; i < map->size ; i++) { + for (size_t i = 0 ; i < map->base.size ; i++) { cxmutstr ksz = cx_strdup(cx_strn(keys[i].data, keys[i].len)); CX_TEST_ASSERT(test_map_reference_get(ksz.ptr) != NULL); cx_strfree(&ksz); @@ -566,15 +566,15 @@ // by using that the values in our test data are unique strings // we can re-use a similar approach as above CxIterator valiter = cxMapIteratorValues(map); - CX_TEST_ASSERT(valiter.elem_size == map->item_size); - CX_TEST_ASSERT(valiter.elem_count == map->size); - char const** values = calloc(map->size, sizeof(char const*)); + CX_TEST_ASSERT(valiter.elem_size == map->base.item_size); + CX_TEST_ASSERT(valiter.elem_count == map->base.size); + char const** values = calloc(map->base.size, sizeof(char const*)); cx_foreach(char const*, elem, valiter) { values[valiter.index] = elem; } - CX_TEST_ASSERT(valiter.index == map->size); + CX_TEST_ASSERT(valiter.index == map->base.size); // verify that all values are present in the reference map - for (size_t i = 0 ; i < map->size ; i++) { + for (size_t i = 0 ; i < map->base.size ; i++) { bool found = false; for (size_t j = 0; j < test_map_reference_len ; j++) { if (test_map_reference[j].value == values[i]) { @@ -591,16 +591,16 @@ { CxIterator pairiter = cxMapIterator(map); CX_TEST_ASSERT(pairiter.elem_size == sizeof(CxMapEntry)); - CX_TEST_ASSERT(pairiter.elem_count == map->size); - struct test_map_kv *pairs = calloc(map->size, sizeof(struct test_map_kv)); + CX_TEST_ASSERT(pairiter.elem_count == map->base.size); + struct test_map_kv *pairs = calloc(map->base.size, sizeof(struct test_map_kv)); cx_foreach(CxMapEntry*, entry, pairiter) { CxHashKey const *key = entry->key; pairs[pairiter.index].key = cx_strdup(cx_strn(key->data, key->len)).ptr; pairs[pairiter.index].value = entry->value; } - CX_TEST_ASSERT(pairiter.index == map->size); + CX_TEST_ASSERT(pairiter.index == map->base.size); // verify that all pairs are present in the reference map - for (size_t i = 0 ; i < map->size ; i++) { + for (size_t i = 0 ; i < map->base.size ; i++) { CX_TEST_ASSERT(test_map_reference_get(pairs[i].key) == pairs[i].value); // this was strdup'ed free((void*)pairs[i].key); diff -r d4baf4dd55c3 -r fe0d69d72bcd tests/test_list.c --- a/tests/test_list.c Thu May 23 19:29:14 2024 +0200 +++ b/tests/test_list.c Thu May 23 20:29:28 2024 +0200 @@ -622,7 +622,7 @@ CX_TEST(test_empty_list_size) { CX_TEST_DO { - CX_TEST_ASSERT(cxEmptyList->size == 0); + CX_TEST_ASSERT(cxEmptyList->base.size == 0); CX_TEST_ASSERT(cxListSize(cxEmptyList) == 0); } } @@ -706,13 +706,13 @@ CX_TEST_DO { CxList *list = cxLinkedListCreate(alloc, cx_cmp_int, sizeof(int)); CX_TEST_ASSERT(list != NULL); - CX_TEST_ASSERT(list->item_size == sizeof(int)); - CX_TEST_ASSERT(list->simple_destructor == NULL); - CX_TEST_ASSERT(list->advanced_destructor == NULL); - CX_TEST_ASSERT(list->destructor_data == NULL); + CX_TEST_ASSERT(list->base.item_size == sizeof(int)); + CX_TEST_ASSERT(list->base.simple_destructor == NULL); + CX_TEST_ASSERT(list->base.advanced_destructor == NULL); + CX_TEST_ASSERT(list->base.destructor_data == NULL); CX_TEST_ASSERT(cxListSize(list) == 0); - CX_TEST_ASSERT(list->allocator == alloc); - CX_TEST_ASSERT(list->cmpfunc == cx_cmp_int); + CX_TEST_ASSERT(list->base.allocator == alloc); + CX_TEST_ASSERT(list->base.cmpfunc == cx_cmp_int); CX_TEST_ASSERT(!cxListIsStoringPointers(list)); cxListDestroy(list); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); @@ -724,13 +724,13 @@ CxList *list = cxLinkedListCreateSimple(sizeof(int)); CX_TEST_DO { CX_TEST_ASSERT(list != NULL); - CX_TEST_ASSERT(list->item_size == sizeof(int)); - CX_TEST_ASSERT(list->simple_destructor == NULL); - CX_TEST_ASSERT(list->advanced_destructor == NULL); - CX_TEST_ASSERT(list->destructor_data == NULL); + CX_TEST_ASSERT(list->base.item_size == sizeof(int)); + CX_TEST_ASSERT(list->base.simple_destructor == NULL); + CX_TEST_ASSERT(list->base.advanced_destructor == NULL); + CX_TEST_ASSERT(list->base.destructor_data == NULL); CX_TEST_ASSERT(cxListSize(list) == 0); - CX_TEST_ASSERT(list->allocator == cxDefaultAllocator); - CX_TEST_ASSERT(list->cmpfunc == NULL); + CX_TEST_ASSERT(list->base.allocator == cxDefaultAllocator); + CX_TEST_ASSERT(list->base.cmpfunc == NULL); CX_TEST_ASSERT(!cxListIsStoringPointers(list)); } cxListDestroy(list); @@ -741,7 +741,7 @@ CX_TEST_DO { CX_TEST_ASSERT(!cxListIsStoringPointers(list)); cxListStorePointers(list); - CX_TEST_ASSERT(list->item_size == sizeof(void *)); + CX_TEST_ASSERT(list->base.item_size == sizeof(void *)); CX_TEST_ASSERT(list->cl != NULL); CX_TEST_ASSERT(list->climpl != NULL); CX_TEST_ASSERT(cxListIsStoringPointers(list)); @@ -757,13 +757,13 @@ CxList *list = cxLinkedListCreateSimple(CX_STORE_POINTERS); CX_TEST_DO { CX_TEST_ASSERT(list != NULL); - CX_TEST_ASSERT(list->item_size == sizeof(void*)); - CX_TEST_ASSERT(list->simple_destructor == NULL); - CX_TEST_ASSERT(list->advanced_destructor == NULL); - CX_TEST_ASSERT(list->destructor_data == NULL); + CX_TEST_ASSERT(list->base.item_size == sizeof(void*)); + CX_TEST_ASSERT(list->base.simple_destructor == NULL); + CX_TEST_ASSERT(list->base.advanced_destructor == NULL); + CX_TEST_ASSERT(list->base.destructor_data == NULL); CX_TEST_ASSERT(cxListSize(list) == 0); - CX_TEST_ASSERT(list->allocator == cxDefaultAllocator); - CX_TEST_ASSERT(list->cmpfunc == cx_cmp_ptr); + CX_TEST_ASSERT(list->base.allocator == cxDefaultAllocator); + CX_TEST_ASSERT(list->base.cmpfunc == cx_cmp_ptr); CX_TEST_ASSERT(cxListIsStoringPointers(list)); } cxListDestroy(list); @@ -776,13 +776,13 @@ CX_TEST_DO { CxList *list = cxArrayListCreate(alloc, cx_cmp_int, sizeof(int), 8); CX_TEST_ASSERT(list != NULL); - CX_TEST_ASSERT(list->item_size == sizeof(int)); - CX_TEST_ASSERT(list->simple_destructor == NULL); - CX_TEST_ASSERT(list->advanced_destructor == NULL); - CX_TEST_ASSERT(list->destructor_data == NULL); + CX_TEST_ASSERT(list->base.item_size == sizeof(int)); + CX_TEST_ASSERT(list->base.simple_destructor == NULL); + CX_TEST_ASSERT(list->base.advanced_destructor == NULL); + CX_TEST_ASSERT(list->base.destructor_data == NULL); CX_TEST_ASSERT(cxListSize(list) == 0); - CX_TEST_ASSERT(list->allocator == alloc); - CX_TEST_ASSERT(list->cmpfunc == cx_cmp_int); + CX_TEST_ASSERT(list->base.allocator == alloc); + CX_TEST_ASSERT(list->base.cmpfunc == cx_cmp_int); CX_TEST_ASSERT(!cxListIsStoringPointers(list)); cxListDestroy(list); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); @@ -794,13 +794,13 @@ CxList *list = cxArrayListCreateSimple(sizeof(int), 8); CX_TEST_DO { CX_TEST_ASSERT(list != NULL); - CX_TEST_ASSERT(list->item_size == sizeof(int)); - CX_TEST_ASSERT(list->simple_destructor == NULL); - CX_TEST_ASSERT(list->advanced_destructor == NULL); - CX_TEST_ASSERT(list->destructor_data == NULL); + CX_TEST_ASSERT(list->base.item_size == sizeof(int)); + CX_TEST_ASSERT(list->base.simple_destructor == NULL); + CX_TEST_ASSERT(list->base.advanced_destructor == NULL); + CX_TEST_ASSERT(list->base.destructor_data == NULL); CX_TEST_ASSERT(cxListSize(list) == 0); - CX_TEST_ASSERT(list->allocator == cxDefaultAllocator); - CX_TEST_ASSERT(list->cmpfunc == NULL); + CX_TEST_ASSERT(list->base.allocator == cxDefaultAllocator); + CX_TEST_ASSERT(list->base.cmpfunc == NULL); CX_TEST_ASSERT(!cxListIsStoringPointers(list)); } cxListDestroy(list); @@ -810,13 +810,13 @@ CxList *list = cxArrayListCreateSimple(CX_STORE_POINTERS, 8); CX_TEST_DO { CX_TEST_ASSERT(list != NULL); - CX_TEST_ASSERT(list->item_size == sizeof(void*)); - CX_TEST_ASSERT(list->simple_destructor == NULL); - CX_TEST_ASSERT(list->advanced_destructor == NULL); - CX_TEST_ASSERT(list->destructor_data == NULL); + CX_TEST_ASSERT(list->base.item_size == sizeof(void*)); + CX_TEST_ASSERT(list->base.simple_destructor == NULL); + CX_TEST_ASSERT(list->base.advanced_destructor == NULL); + CX_TEST_ASSERT(list->base.destructor_data == NULL); CX_TEST_ASSERT(cxListSize(list) == 0); - CX_TEST_ASSERT(list->allocator == cxDefaultAllocator); - CX_TEST_ASSERT(list->cmpfunc == cx_cmp_ptr); + CX_TEST_ASSERT(list->base.allocator == cxDefaultAllocator); + CX_TEST_ASSERT(list->base.cmpfunc == cx_cmp_ptr); CX_TEST_ASSERT(cxListIsStoringPointers(list)); } cxListDestroy(list); @@ -848,7 +848,7 @@ CX_TEST_DO { int item = 0; CxList *list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, CX_STORE_POINTERS); - list->simple_destructor = test_fake_simple_int_destr; + list->base.simple_destructor = test_fake_simple_int_destr; cxListAdd(list, &item); cxListDestroy(list); CX_TEST_ASSERT(item == 42); @@ -862,8 +862,8 @@ CX_TEST_DO { void *item = cxMalloc(alloc, sizeof(int)); CxList *list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, CX_STORE_POINTERS); - list->destructor_data = alloc; - list->advanced_destructor = (cx_destructor_func2) cxFree; + list->base.destructor_data = alloc; + list->base.advanced_destructor = (cx_destructor_func2) cxFree; cxListAdd(list, item); CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc)); cxListDestroy(list); @@ -894,7 +894,7 @@ CX_TEST_DO { int item = 0; CxList *list = cxArrayListCreate(cxDefaultAllocator, cx_cmp_int, CX_STORE_POINTERS, 4); - list->simple_destructor = test_fake_simple_int_destr; + list->base.simple_destructor = test_fake_simple_int_destr; cxListAdd(list, &item); cxListDestroy(list); CX_TEST_ASSERT(item == 42); @@ -908,8 +908,8 @@ CX_TEST_DO { void *item = cxMalloc(alloc, sizeof(int)); CxList *list = cxArrayListCreate(cxDefaultAllocator, cx_cmp_int, CX_STORE_POINTERS, 4); - list->destructor_data = alloc; - list->advanced_destructor = (cx_destructor_func2) cxFree; + list->base.destructor_data = alloc; + list->base.advanced_destructor = (cx_destructor_func2) cxFree; cxListAdd(list, item); CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc)); cxListDestroy(list); @@ -1207,8 +1207,8 @@ int *testdata = int_test_data_added_to_list(list, isptrlist, len); CxIterator iter = cxListIterator(list); - CX_TEST_ASSERT(iter.elem_size == list->item_size); - CX_TEST_ASSERT(iter.elem_count == list->size); + CX_TEST_ASSERT(iter.elem_size == list->base.item_size); + CX_TEST_ASSERT(iter.elem_count == list->base.size); size_t i = 0; cx_foreach(int*, x, iter) { CX_TEST_ASSERT(i == iter.index); @@ -1225,8 +1225,8 @@ CX_TEST_ASSERT(i == 0); i = len / 2; CxIterator mut_iter = cxListMutIteratorAt(list, i); - CX_TEST_ASSERT(mut_iter.elem_size == list->item_size); - CX_TEST_ASSERT(mut_iter.elem_count == list->size); + CX_TEST_ASSERT(mut_iter.elem_size == list->base.item_size); + CX_TEST_ASSERT(mut_iter.elem_count == list->base.size); size_t j = 0; cx_foreach(int*, x, mut_iter) { CX_TEST_ASSERT(mut_iter.index == len / 2 + j / 2); @@ -1395,7 +1395,7 @@ roll_out_test_combos(simple_destr, { const size_t len = 60; int *testdata = int_test_data_added_to_list(list, isptrlist, len); - list->simple_destructor = simple_destr_test_fun; + list->base.simple_destructor = simple_destr_test_fun; CX_TEST_CALL_SUBROUTINE(test_list_verify_destructor, list, testdata, len); free(testdata); }) @@ -1403,7 +1403,7 @@ roll_out_test_combos(advanced_destr, { const size_t len = 75; int *testdata = int_test_data_added_to_list(list, isptrlist, len); - list->advanced_destructor = advanced_destr_test_fun; + list->base.advanced_destructor = advanced_destr_test_fun; CX_TEST_CALL_SUBROUTINE(test_list_verify_destructor, list, testdata, len); free(testdata); }) diff -r d4baf4dd55c3 -r fe0d69d72bcd tests/test_tree.c --- a/tests/test_tree.c Thu May 23 19:29:14 2024 +0200 +++ b/tests/test_tree.c Thu May 23 20:29:28 2024 +0200 @@ -260,8 +260,8 @@ CX_TEST_ASSERT(!iter.exiting); CX_TEST_ASSERT(iter.counter == 1); CX_TEST_ASSERT(iter.node == &root); - CX_TEST_ASSERT(!iter.mutating); - CX_TEST_ASSERT(!iter.remove); + CX_TEST_ASSERT(!iter.base.mutating); + CX_TEST_ASSERT(!iter.base.remove); CX_TEST_ASSERT(iter.stack != NULL); CX_TEST_ASSERT(iter.stack_capacity > 0); CX_TEST_ASSERT(iter.stack_size == 1); @@ -517,8 +517,8 @@ CxTreeVisitor iter = cx_tree_visitor(&root, tree_child_list); CX_TEST_ASSERT(iter.counter == 1); CX_TEST_ASSERT(iter.node == &root); - CX_TEST_ASSERT(!iter.mutating); - CX_TEST_ASSERT(!iter.remove); + CX_TEST_ASSERT(!iter.base.mutating); + CX_TEST_ASSERT(!iter.base.remove); CX_TEST_ASSERT(iter.queue_next != NULL); CX_TEST_ASSERT(iter.queue_last != NULL); CX_TEST_ASSERT(iter.depth == 1);