--- a/ucx/cx/list.h Tue Oct 14 21:02:26 2025 +0200 +++ b/ucx/cx/list.h Sat Nov 08 23:06:11 2025 +0100 @@ -80,46 +80,41 @@ /** * Member function for inserting a single element. - * The data pointer may be @c NULL in which case the function shall only allocate memory. + * The data pointer may be @c NULL, in which case the function shall only allocate memory. * Returns a pointer to the allocated memory or @c NULL if allocation fails. */ - void *(*insert_element)( - struct cx_list_s *list, - size_t index, - const void *data - ); + void *(*insert_element)(struct cx_list_s *list, size_t index, const void *data); /** * Member function for inserting multiple elements. * + * The data pointer may be @c NULL, in which case the function shall only allocate memory. + * Returns the number of successfully inserted or allocated elements. + * * @see cx_list_default_insert_array() */ - size_t (*insert_array)( - struct cx_list_s *list, - size_t index, - const void *data, - size_t n - ); + size_t (*insert_array)(struct cx_list_s *list, size_t index, const void *data, size_t n); /** * Member function for inserting sorted elements into a sorted list. + * Returns the number of successfully inserted elements. * * @see cx_list_default_insert_sorted() */ - size_t (*insert_sorted)( - struct cx_list_s *list, - const void *sorted_data, - size_t n - ); + size_t (*insert_sorted)(struct cx_list_s *list, const void *sorted_data, size_t n); + + /** + * Member function for inserting multiple elements if they do not exist. + * Implementations shall return the number of successfully processed elements + * (including those which were not added because they are already contained). + * @see cx_list_default_insert_unique() + */ + size_t (*insert_unique)(struct cx_list_s *list, const void *sorted_data, size_t n); /** * Member function for inserting an element relative to an iterator position. */ - int (*insert_iter)( - struct cx_iterator_s *iter, - const void *elem, - int prepend - ); + int (*insert_iter)(struct cx_iterator_s *iter, const void *elem, int prepend); /** * Member function for removing elements. @@ -131,12 +126,7 @@ * The function SHALL return the actual number of elements removed, which * might be lower than @p num when going out of bounds. */ - size_t (*remove)( - struct cx_list_s *list, - size_t index, - size_t num, - void *targetbuf - ); + size_t (*remove)(struct cx_list_s *list, size_t index, size_t num, void *targetbuf); /** * Member function for removing all elements. @@ -148,28 +138,17 @@ * * @see cx_list_default_swap() */ - int (*swap)( - struct cx_list_s *list, - size_t i, - size_t j - ); + int (*swap)(struct cx_list_s *list, size_t i, size_t j); /** * Member function for element lookup. */ - void *(*at)( - const struct cx_list_s *list, - size_t index - ); + void *(*at)(const struct cx_list_s *list, size_t index); /** * Member function for finding and optionally removing an element. */ - size_t (*find_remove)( - struct cx_list_s *list, - const void *elem, - bool remove - ); + size_t (*find_remove)(struct cx_list_s *list, const void *elem, bool remove); /** * Member function for sorting the list. @@ -181,12 +160,9 @@ /** * Optional member function for comparing this list * to another list of the same type. - * If set to @c NULL, comparison won't be optimized. + * If set to @c NULL, the comparison won't be optimized. */ - int (*compare)( - const struct cx_list_s *list, - const struct cx_list_s *other - ); + int (*compare)(const struct cx_list_s *list, const struct cx_list_s *other); /** * Member function for reversing the order of the items. @@ -196,11 +172,7 @@ /** * Member function for returning an iterator pointing to the specified index. */ - struct cx_iterator_s (*iterator)( - const struct cx_list_s *list, - size_t index, - bool backward - ); + struct cx_iterator_s (*iterator)(const struct cx_list_s *list, size_t index, bool backward); }; /** @@ -213,11 +185,10 @@ * * Writing to that list is not allowed. * - * You can use this is a placeholder for initializing CxList pointers + * You can use this as a placeholder for initializing CxList pointers * for which you do not want to reserve memory right from the beginning. */ -cx_attr_export -extern CxList *const cxEmptyList; +CX_EXPORT extern CxList *const cxEmptyList; /** * Default implementation of an array insert. @@ -234,13 +205,8 @@ * @return the number of elements actually inserted */ cx_attr_nonnull -cx_attr_export -size_t cx_list_default_insert_array( - struct cx_list_s *list, - size_t index, - const void *data, - size_t n -); +CX_EXPORT size_t cx_list_default_insert_array(struct cx_list_s *list, + size_t index, const void *data, size_t n); /** * Default implementation of a sorted insert. @@ -259,12 +225,28 @@ * @return the number of elements actually inserted */ cx_attr_nonnull -cx_attr_export -size_t cx_list_default_insert_sorted( - struct cx_list_s *list, - const void *sorted_data, - size_t n -); +CX_EXPORT size_t cx_list_default_insert_sorted(struct cx_list_s *list, + const void *sorted_data, size_t n); + +/** + * Default implementation of an array insert where only elements are inserted when they don't exist in the list. + * + * This function is similar to cx_list_default_insert_sorted(), except it skips elements that are already in the list. + * + * @note The return value of this function denotes the number of elements from the @p sorted_data that are definitely + * contained in the list after completing the call. It is @em not the number of elements that were newly inserted. + * That means, when no error occurred, the return value should be @p n. + * + * Use this in your own list class if you do not want to implement an optimized version for your list. + * + * @param list the list + * @param sorted_data a pointer to the array of pre-sorted data to insert + * @param n the number of elements to insert + * @return the number of elements from the @p sorted_data that are definitely present in the list after this call + */ +cx_attr_nonnull +CX_EXPORT size_t cx_list_default_insert_unique(struct cx_list_s *list, + const void *sorted_data, size_t n); /** * Default unoptimized sort implementation. @@ -278,8 +260,7 @@ * @param list the list that shall be sorted */ cx_attr_nonnull -cx_attr_export -void cx_list_default_sort(struct cx_list_s *list); +CX_EXPORT void cx_list_default_sort(struct cx_list_s *list); /** * Default unoptimized swap implementation. @@ -295,20 +276,19 @@ * allocation for the temporary buffer fails */ cx_attr_nonnull -cx_attr_export -int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j); +CX_EXPORT int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j); /** * Initializes a list struct. * * Only use this function if you are creating your own list implementation. * The purpose of this function is to be called in the initialization code - * of your list, to set certain members correctly. + * of your list to set certain members correctly. * * This is particularly important when you want your list to support * #CX_STORE_POINTERS as @p elem_size. This function will wrap the list * class accordingly and make sure that you can implement your list as if - * it was only storing objects and the wrapper will automatically enable + * it was only storing objects, and the wrapper will automatically enable * the feature of storing pointers. * * @par Example @@ -343,14 +323,9 @@ * @param elem_size the size of one element */ cx_attr_nonnull_arg(1, 2, 3) -cx_attr_export -void cx_list_init( - struct cx_list_s *list, - struct cx_list_class_s *cl, - const struct cx_allocator_s *allocator, - cx_compare_func comparator, - size_t elem_size -); +CX_EXPORT void cx_list_init(struct cx_list_s *list, + struct cx_list_class_s *cl, const struct cx_allocator_s *allocator, + cx_compare_func comparator, size_t elem_size); /** * Returns the number of elements currently stored in the list. @@ -359,9 +334,7 @@ * @return the number of currently stored elements */ cx_attr_nonnull -static inline size_t cxListSize(const CxList *list) { - return list->collection.size; -} +CX_EXPORT size_t cxListSize(const CxList *list); /** * Adds an item to the end of the list. @@ -374,13 +347,7 @@ * @see cxListEmplace() */ cx_attr_nonnull -static inline int cxListAdd( - CxList *list, - const void *elem -) { - list->collection.sorted = false; - return list->cl->insert_element(list, list->collection.size, elem) == NULL; -} +CX_EXPORT int cxListAdd(CxList *list, const void *elem); /** * Adds multiple items to the end of the list. @@ -390,28 +357,22 @@ * If there is not enough memory to add all elements, the returned value is * less than @p n. * - * If this list is storing pointers instead of objects @p array is expected to + * If this list is storing pointers instead of objects, @p array is expected to * be an array of pointers. * * @param list the list * @param array a pointer to the elements to add * @param n the number of elements to add * @return the number of added elements + * @see cxListEmplaceArray() */ cx_attr_nonnull -static inline size_t cxListAddArray( - CxList *list, - const void *array, - size_t n -) { - list->collection.sorted = false; - return list->cl->insert_array(list, list->collection.size, array, n); -} +CX_EXPORT size_t cxListAddArray(CxList *list, const void *array, size_t n); /** * Inserts an item at the specified index. * - * If @p index equals the list @c size, this is effectively cxListAdd(). + * If the @p index equals the list @c size, this is effectively cxListAdd(). * * @param list the list * @param index the index the element shall have @@ -423,14 +384,7 @@ * @see cxListEmplaceAt() */ cx_attr_nonnull -static inline int cxListInsert( - CxList *list, - size_t index, - const void *elem -) { - list->collection.sorted = false; - return list->cl->insert_element(list, index, elem) == NULL; -} +CX_EXPORT int cxListInsert(CxList *list, size_t index, const void *elem); /** * Allocates memory for an element at the specified index and returns a pointer to that memory. @@ -441,14 +395,11 @@ * @param index the index where to emplace the element * @return a pointer to the allocated memory; @c NULL when the operation fails, or the index is out-of-bounds * @see cxListEmplace() + * @see cxListEmplaceArrayAt() * @see cxListInsert() */ cx_attr_nonnull -static inline void *cxListEmplaceAt(CxList *list, size_t index) { - list->collection.sorted = false; - return list->cl->insert_element(list, index, NULL); -} - +CX_EXPORT void *cxListEmplaceAt(CxList *list, size_t index); /** * Allocates memory for an element at the end of the list and returns a pointer to that memory. @@ -461,10 +412,46 @@ * @see cxListAdd() */ cx_attr_nonnull -static inline void *cxListEmplace(CxList *list) { - list->collection.sorted = false; - return list->cl->insert_element(list, list->collection.size, NULL); -} +CX_EXPORT void *cxListEmplace(CxList *list); + +/** + * Allocates memory for multiple elements and returns an iterator. + * + * The iterator will only iterate over the successfully allocated elements. + * The @c elem_count attribute is set to that number, and the @c index attribute + * will range from zero to @c elem_count minus one. + * + * @remark When the list is storing pointers, the iterator will iterate over + * the @c void** elements. + * + * @param list the list + * @param index the index where to insert the new data + * @param n the number of elements for which to allocate the memory + * @return an iterator, iterating over the new memory + * @see cxListEmplaceAt() + * @see cxListInsertArray() + */ +cx_attr_nonnull +CX_EXPORT CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n); + +/** + * Allocates memory for multiple elements and returns an iterator. + * + * The iterator will only iterate over the successfully allocated elements. + * The @c elem_count attribute is set to that number, and the @c index attribute + * will range from zero to @c elem_count minus one. + * + * @remark When the list is storing pointers, the iterator will iterate over + * the @c void** elements. + * + * @param list the list + * @param n the number of elements for which to allocate the memory + * @return an iterator, iterating over the new memory + * @see cxListEmplace() + * @see cxListAddArray() + */ +cx_attr_nonnull +CX_EXPORT CxIterator cxListEmplaceArray(CxList *list, size_t n); /** * Inserts an item into a sorted list. @@ -477,18 +464,27 @@ * @retval non-zero memory allocation failure */ cx_attr_nonnull -static inline int cxListInsertSorted( - CxList *list, - const void *elem -) { - list->collection.sorted = true; // guaranteed by definition - const void *data = list->collection.store_pointer ? &elem : elem; - return list->cl->insert_sorted(list, data, 1) == 0; -} +CX_EXPORT int cxListInsertSorted(CxList *list, const void *elem); + +/** + * Inserts an item into a list if it does not exist. + * + * If the list is not sorted already, this function will check all elements + * and append the new element when it was not found. + * It is strongly recommended to use this function only on sorted lists, where + * the element, if it is not contained, is inserted at the correct position. + * + * @param list the list + * @param elem a pointer to the element to add + * @retval zero success (also when the element was already in the list) + * @retval non-zero memory allocation failure + */ +cx_attr_nonnull +CX_EXPORT int cxListInsertUnique(CxList *list, const void *elem); /** * Inserts multiple items to the list at the specified index. - * If @p index equals the list size, this is effectively cxListAddArray(). + * If the @p index equals the list size, this is effectively cxListAddArray(). * * This method is usually more efficient than invoking cxListInsert() * multiple times. @@ -496,7 +492,7 @@ * If there is not enough memory to add all elements, the returned value is * less than @p n. * - * If this list is storing pointers instead of objects @p array is expected to + * If this list is storing pointers instead of objects, @p array is expected to * be an array of pointers. * * @param list the list @@ -504,28 +500,21 @@ * @param array a pointer to the elements to add * @param n the number of elements to add * @return the number of added elements + * @see cxListEmplaceArrayAt() */ cx_attr_nonnull -static inline size_t cxListInsertArray( - CxList *list, - size_t index, - const void *array, - size_t n -) { - list->collection.sorted = false; - return list->cl->insert_array(list, index, array, n); -} +CX_EXPORT size_t cxListInsertArray(CxList *list, size_t index, const void *array, size_t n); /** * Inserts a sorted array into a sorted list. * - * This method is usually more efficient than inserting each element separately, + * This method is usually more efficient than inserting each element separately * because consecutive chunks of sorted data are inserted in one pass. * * If there is not enough memory to add all elements, the returned value is * less than @p n. * - * If this list is storing pointers instead of objects @p array is expected to + * If this list is storing pointers instead of objects, @p array is expected to * be an array of pointers. * * If the list is not sorted already, the behavior is undefined. @@ -536,14 +525,42 @@ * @return the number of added elements */ cx_attr_nonnull -static inline size_t cxListInsertSortedArray( - CxList *list, - const void *array, - size_t n -) { - list->collection.sorted = true; // guaranteed by definition - return list->cl->insert_sorted(list, array, n); -} +CX_EXPORT size_t cxListInsertSortedArray(CxList *list, const void *array, size_t n); + +/** + * Inserts an array into a list, skipping duplicates. + * + * The @p list does not need to be sorted (in contrast to cxListInsertSortedArray()). + * But it is strongly recommended to use this function only on sorted lists, + * because otherwise it will fall back to an inefficient algorithm which inserts + * all elements one by one. + * If the @p list is not sorted, the @p array also does not need to be sorted. + * But when the @p list is sorted, the @p array must also be sorted. + * + * This method is usually more efficient than inserting each element separately + * because consecutive chunks of sorted data are inserted in one pass. + * + * If there is not enough memory to add all elements, the returned value is + * less than @p n. + * + * @note The return value of this function denotes the number of elements + * from the @p sorted_data that are definitely contained in the list after + * completing the call. It is @em not the number of elements that were newly + * inserted. That means, when no error occurred, the return value should + * be @p n. + * + * If this list is storing pointers instead of objects @p array is expected to + * be an array of pointers. + * + * @param list the list + * @param array a pointer to the elements to add + * @param n the number of elements to add + * @return the number of added elements + * + * @return the number of elements from the @p sorted_data that are definitely present in the list after this call + */ +cx_attr_nonnull +CX_EXPORT size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n); /** * Inserts an element after the current location of the specified iterator. @@ -562,14 +579,7 @@ * @see cxListInsertBefore() */ cx_attr_nonnull -static inline int cxListInsertAfter( - CxIterator *iter, - const void *elem -) { - CxList* list = (CxList*)iter->src_handle.m; - list->collection.sorted = false; - return list->cl->insert_iter(iter, elem, 0); -} +CX_EXPORT int cxListInsertAfter(CxIterator *iter, const void *elem); /** * Inserts an element before the current location of the specified iterator. @@ -588,14 +598,7 @@ * @see cxListInsertAfter() */ cx_attr_nonnull -static inline int cxListInsertBefore( - CxIterator *iter, - const void *elem -) { - CxList* list = (CxList*)iter->src_handle.m; - list->collection.sorted = false; - return list->cl->insert_iter(iter, elem, 1); -} +CX_EXPORT int cxListInsertBefore(CxIterator *iter, const void *elem); /** * Removes the element at the specified index. @@ -609,12 +612,7 @@ * @retval non-zero index out of bounds */ cx_attr_nonnull -static inline int cxListRemove( - CxList *list, - size_t index -) { - return list->cl->remove(list, index, 1, NULL) == 0; -} +CX_EXPORT int cxListRemove(CxList *list, size_t index); /** * Removes and returns the element at the specified index. @@ -629,15 +627,8 @@ * @retval zero success * @retval non-zero index out of bounds */ -cx_attr_nonnull -cx_attr_access_w(3) -static inline int cxListRemoveAndGet( - CxList *list, - size_t index, - void *targetbuf -) { - return list->cl->remove(list, index, 1, targetbuf) == 0; -} +cx_attr_nonnull cx_attr_access_w(3) +CX_EXPORT int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf); /** * Removes and returns the first element of the list. @@ -649,18 +640,12 @@ * @param list the list * @param targetbuf a buffer where to copy the element * @retval zero success - * @retval non-zero list is empty + * @retval non-zero the list is empty * @see cxListPopFront() * @see cxListRemoveAndGetLast() */ -cx_attr_nonnull -cx_attr_access_w(2) -static inline int cxListRemoveAndGetFirst( - CxList *list, - void *targetbuf -) { - return list->cl->remove(list, 0, 1, targetbuf) == 0; -} +cx_attr_nonnull cx_attr_access_w(2) +CX_EXPORT int cxListRemoveAndGetFirst(CxList *list, void *targetbuf); /** * Removes and returns the first element of the list. @@ -674,7 +659,7 @@ * @param list (@c CxList*) the list * @param targetbuf (@c void*) a buffer where to copy the element * @retval zero success - * @retval non-zero list is empty + * @retval non-zero the list is empty * @see cxListRemoveAndGetFirst() * @see cxListPop() */ @@ -691,17 +676,10 @@ * @param list the list * @param targetbuf a buffer where to copy the element * @retval zero success - * @retval non-zero list is empty + * @retval non-zero the list is empty */ -cx_attr_nonnull -cx_attr_access_w(2) -static inline int cxListRemoveAndGetLast( - CxList *list, - void *targetbuf -) { - // note: index may wrap - member function will catch that - return list->cl->remove(list, list->collection.size - 1, 1, targetbuf) == 0; -} +cx_attr_nonnull cx_attr_access_w(2) +CX_EXPORT int cxListRemoveAndGetLast(CxList *list, void *targetbuf); /** * Removes and returns the last element of the list. @@ -715,7 +693,7 @@ * @param list (@c CxList*) the list * @param targetbuf (@c void*) a buffer where to copy the element * @retval zero success - * @retval non-zero list is empty + * @retval non-zero the list is empty * @see cxListRemoveAndGetLast() * @see cxListPopFront() */ @@ -737,13 +715,7 @@ * @return the actual number of removed elements */ cx_attr_nonnull -static inline size_t cxListRemoveArray( - CxList *list, - size_t index, - size_t num -) { - return list->cl->remove(list, index, num, NULL); -} +CX_EXPORT size_t cxListRemoveArray(CxList *list, size_t index, size_t num); /** * Removes and returns multiple elements starting at the specified index. @@ -758,16 +730,8 @@ * @param targetbuf a buffer where to copy the elements * @return the actual number of removed elements */ -cx_attr_nonnull -cx_attr_access_w(4) -static inline size_t cxListRemoveArrayAndGet( - CxList *list, - size_t index, - size_t num, - void *targetbuf -) { - return list->cl->remove(list, index, num, targetbuf); -} +cx_attr_nonnull cx_attr_access_w(4) +CX_EXPORT size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num, void *targetbuf); /** * Removes all elements from this list. @@ -778,10 +742,7 @@ * @param list the list */ cx_attr_nonnull -static inline void cxListClear(CxList *list) { - list->collection.sorted = true; // empty lists are always sorted - list->cl->clear(list); -} +CX_EXPORT void cxListClear(CxList *list); /** * Swaps two items in the list. @@ -797,14 +758,7 @@ * or the swap needed extra memory, but allocation failed */ cx_attr_nonnull -static inline int cxListSwap( - CxList *list, - size_t i, - size_t j -) { - list->collection.sorted = false; - return list->cl->swap(list, i, j); -} +CX_EXPORT int cxListSwap(CxList *list, size_t i, size_t j); /** * Returns a pointer to the element at the specified index. @@ -816,12 +770,7 @@ * @return a pointer to the element or @c NULL if the index is out of bounds */ cx_attr_nonnull -static inline void *cxListAt( - const CxList *list, - size_t index -) { - return list->cl->at(list, index); -} +CX_EXPORT void *cxListAt(const CxList *list, size_t index); /** * Returns a pointer to the first element. @@ -832,9 +781,7 @@ * @return a pointer to the first element or @c NULL if the list is empty */ cx_attr_nonnull -static inline void *cxListFirst(const CxList *list) { - return list->cl->at(list, 0); -} +CX_EXPORT void *cxListFirst(const CxList *list); /** * Returns a pointer to the last element. @@ -845,12 +792,13 @@ * @return a pointer to the last element or @c NULL if the list is empty */ cx_attr_nonnull -static inline void *cxListLast(const CxList *list) { - return list->cl->at(list, list->collection.size - 1); -} +CX_EXPORT void *cxListLast(const CxList *list); /** - * Sets the element at the specified index in the list + * Sets the element at the specified index in the list. + * + * This overwrites the element in-place without calling any destructor + * on the overwritten element. * * @param list the list to set the element in * @param index the index to set the element at @@ -859,12 +807,7 @@ * @retval non-zero when index is out of bounds */ cx_attr_nonnull -cx_attr_export -int cxListSet( - CxList *list, - size_t index, - const void *elem -); +CX_EXPORT int cxListSet(CxList *list, size_t index, const void *elem); /** * Returns an iterator pointing to the item at the specified index. @@ -878,13 +821,7 @@ * @return a new iterator */ cx_attr_nodiscard -static inline CxIterator cxListIteratorAt( - const CxList *list, - size_t index -) { - if (list == NULL) list = cxEmptyList; - return list->cl->iterator(list, index, false); -} +CX_EXPORT CxIterator cxListIteratorAt(const CxList *list, size_t index); /** * Returns a backwards iterator pointing to the item at the specified index. @@ -898,50 +835,7 @@ * @return a new iterator */ cx_attr_nodiscard -static inline CxIterator cxListBackwardsIteratorAt( - const CxList *list, - size_t index -) { - if (list == NULL) list = cxEmptyList; - return list->cl->iterator(list, index, true); -} - -/** - * Returns a mutating iterator pointing to the item at the specified index. - * - * The returned iterator is position-aware. - * - * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned. - * - * @param list the list - * @param index the index where the iterator shall point at - * @return a new iterator - */ -cx_attr_nodiscard -cx_attr_export -CxIterator cxListMutIteratorAt( - CxList *list, - size_t index -); - -/** - * Returns a mutating backwards iterator pointing to the item at the - * specified index. - * - * The returned iterator is position-aware. - * - * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned. - * - * @param list the list - * @param index the index where the iterator shall point at - * @return a new iterator - */ -cx_attr_nodiscard -cx_attr_export -CxIterator cxListMutBackwardsIteratorAt( - CxList *list, - size_t index -); +CX_EXPORT CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index); /** * Returns an iterator pointing to the first item of the list. @@ -954,27 +848,7 @@ * @return a new iterator */ cx_attr_nodiscard -static inline CxIterator cxListIterator(const CxList *list) { - if (list == NULL) list = cxEmptyList; - return list->cl->iterator(list, 0, false); -} - -/** - * Returns a mutating iterator pointing to the first item of the list. - * - * The returned iterator is position-aware. - * - * If the list is empty or @c NULL, a past-the-end iterator will be returned. - * - * @param list the list - * @return a new iterator - */ -cx_attr_nodiscard -static inline CxIterator cxListMutIterator(CxList *list) { - if (list == NULL) list = cxEmptyList; - return cxListMutIteratorAt(list, 0); -} - +CX_EXPORT CxIterator cxListIterator(const CxList *list); /** * Returns a backwards iterator pointing to the last item of the list. @@ -987,26 +861,7 @@ * @return a new iterator */ cx_attr_nodiscard -static inline CxIterator cxListBackwardsIterator(const CxList *list) { - if (list == NULL) list = cxEmptyList; - return list->cl->iterator(list, list->collection.size - 1, true); -} - -/** - * Returns a mutating backwards iterator pointing to the last item of the list. - * - * The returned iterator is position-aware. - * - * If the list is empty or @c NULL, a past-the-end iterator will be returned. - * - * @param list the list - * @return a new iterator - */ -cx_attr_nodiscard -static inline CxIterator cxListMutBackwardsIterator(CxList *list) { - if (list == NULL) list = cxEmptyList; - return cxListMutBackwardsIteratorAt(list, list->collection.size - 1); -} +CX_EXPORT CxIterator cxListBackwardsIterator(const CxList *list); /** * Returns the index of the first element that equals @p elem. @@ -1019,17 +874,11 @@ * @see cxListIndexValid() * @see cxListContains() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline size_t cxListFind( - const CxList *list, - const void *elem -) { - return list->cl->find_remove((CxList*)list, elem, false); -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT size_t cxListFind(const CxList *list, const void *elem); /** - * Checks, if the list contains the specified element. + * Checks if the list contains the specified element. * * The elements are compared with the list's comparator function. * @@ -1039,14 +888,8 @@ * @retval false if the element is not contained * @see cxListFind() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline bool cxListContains( - const CxList* list, - const void* elem -) { - return list->cl->find_remove((CxList*)list, elem, false) < list->collection.size; -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT bool cxListContains(const CxList* list, const void* elem); /** * Checks if the specified index is within bounds. @@ -1056,11 +899,8 @@ * @retval true if the index is within bounds * @retval false if the index is out of bounds */ -cx_attr_nonnull -cx_attr_nodiscard -static inline bool cxListIndexValid(const CxList *list, size_t index) { - return index < list->collection.size; -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT bool cxListIndexValid(const CxList *list, size_t index); /** * Removes and returns the index of the first element that equals @p elem. @@ -1074,12 +914,7 @@ * @see cxListIndexValid() */ cx_attr_nonnull -static inline size_t cxListFindRemove( - CxList *list, - const void *elem -) { - return list->cl->find_remove(list, elem, true); -} +CX_EXPORT size_t cxListFindRemove(CxList *list, const void *elem); /** * Sorts the list. @@ -1089,11 +924,7 @@ * @param list the list */ cx_attr_nonnull -static inline void cxListSort(CxList *list) { - if (list->collection.sorted) return; - list->cl->sort(list); - list->collection.sorted = true; -} +CX_EXPORT void cxListSort(CxList *list); /** * Reverses the order of the items. @@ -1101,11 +932,7 @@ * @param list the list */ cx_attr_nonnull -static inline void cxListReverse(CxList *list) { - // still sorted, but not according to the cmp_func - list->collection.sorted = false; - list->cl->reverse(list); -} +CX_EXPORT void cxListReverse(CxList *list); /** * Compares a list to another list of the same type. @@ -1116,28 +943,116 @@ * @param list the list * @param other the list to compare to * @retval zero both lists are equal element wise - * @retval negative the first list is smaller + * @retval negative the first list is smaller, * or the first non-equal element in the first list is smaller * @retval positive the first list is larger * or the first non-equal element in the first list is larger */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cxListCompare( - const CxList *list, - const CxList *other -); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cxListCompare(const CxList *list, const CxList *other); /** * Deallocates the memory of the specified list structure. * * Also calls the content destructor functions for each element, if specified. * - * @param list the list which shall be freed + * @param list the list that shall be freed + */ +CX_EXPORT void cxListFree(CxList *list); + + +/** + * Performs a deep clone of one list into another. + * + * If the destination list already contains elements, the cloned elements + * are appended to that list. + * + * @attention If the cloned elements need to be destroyed by a destructor + * function, you must make sure that the destination list also uses this + * destructor function. + * + * @param dst the destination list + * @param src the source list + * @param clone_func the clone function for the elements + * @param clone_allocator the allocator that is passed to the clone function + * @param data optional additional data that is passed to the clone function + * @retval zero when all elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxListUnion() + */ +cx_attr_nonnull_arg(1, 2, 3) +CX_EXPORT int cxListClone(CxList *dst, const CxList *src, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); + +/** + * Clones elements from a list only if they are not present in another list. + * + * If the @p minuend does not contain duplicates, this is equivalent to adding + * the set difference to the destination list. + * + * This function is optimized for the case when both the @p minuend and the + * @p subtrahend are sorted. + * + * @param dst the destination list + * @param minuend the list to subtract elements from + * @param subtrahend the elements that shall be subtracted + * @param clone_func the clone function for the elements + * @param clone_allocator the allocator that is passed to the clone function + * @param data optional additional data that is passed to the clone function + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred */ -cx_attr_export -void cxListFree(CxList *list); +cx_attr_nonnull_arg(1, 2, 3, 4) +CX_EXPORT int cxListDifference(CxList *dst, + const CxList *minuend, const CxList *subtrahend, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); + +/** + * Clones elements from a list only if they are also present in another list. + * + * This function is optimized for the case when both the @p src and the + * @p other list are sorted. + * + * If the destination list already contains elements, the intersection is appended + * to that list. + * + * @param dst the destination list + * @param src the list to clone the elements from + * @param other the list to check the elements for existence + * @param clone_func the clone function for the elements + * @param clone_allocator the allocator that is passed to the clone function + * @param data optional additional data that is passed to the clone function + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + */ +cx_attr_nonnull_arg(1, 2, 3, 4) +CX_EXPORT int cxListIntersection(CxList *dst, const CxList *src, const CxList *other, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); + +/** + * Performs a deep clone of one list into another, skipping duplicates. + * + * This function is optimized for the case when both the @p src and the + * @p other list are sorted. + * In that case, the union will also be sorted. + * + * If the destination list already contains elements, the union is appended + * to that list. In that case the destination is not necessarily sorted. + * + * @param dst the destination list + * @param src the primary source list + * @param other the other list, where elements are only cloned from + * when they are not in @p src + * @param clone_func the clone function for the elements + * @param clone_allocator the allocator that is passed to the clone function + * @param data optional additional data that is passed to the clone function + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxListClone() + */ +cx_attr_nonnull_arg(1, 2, 3, 4) +CX_EXPORT int cxListUnion(CxList *dst, const CxList *src, const CxList *other, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); #ifdef __cplusplus