--- a/ucx/cx/list.h Tue Sep 09 16:01:30 2025 +0200 +++ b/ucx/cx/list.h Tue Sep 09 20:56:47 2025 +0200 @@ -80,8 +80,10 @@ /** * Member function for inserting a single element. + * 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. */ - int (*insert_element)( + void *(*insert_element)( struct cx_list_s *list, size_t index, const void *data @@ -181,7 +183,6 @@ * to another list of the same type. * If set to @c NULL, comparison won't be optimized. */ - cx_attr_nonnull int (*compare)( const struct cx_list_s *list, const struct cx_list_s *other @@ -203,6 +204,22 @@ }; /** + * Common type for all list implementations. + */ +typedef struct cx_list_s CxList; + +/** + * A shared instance of an empty list. + * + * Writing to that list is not allowed. + * + * You can use this is 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; + +/** * Default implementation of an array insert. * * This function uses the element insert function for each element of the array. @@ -336,11 +353,6 @@ ); /** - * Common type for all list implementations. - */ -typedef struct cx_list_s CxList; - -/** * Returns the number of elements currently stored in the list. * * @param list the list @@ -359,6 +371,7 @@ * @retval zero success * @retval non-zero memory allocation failure * @see cxListAddArray() + * @see cxListEmplace() */ cx_attr_nonnull static inline int cxListAdd( @@ -366,7 +379,7 @@ const void *elem ) { list->collection.sorted = false; - return list->cl->insert_element(list, list->collection.size, elem); + return list->cl->insert_element(list, list->collection.size, elem) == NULL; } /** @@ -407,6 +420,7 @@ * @retval non-zero memory allocation failure or the index is out of bounds * @see cxListInsertAfter() * @see cxListInsertBefore() + * @see cxListEmplaceAt() */ cx_attr_nonnull static inline int cxListInsert( @@ -415,7 +429,41 @@ const void *elem ) { list->collection.sorted = false; - return list->cl->insert_element(list, index, elem); + return list->cl->insert_element(list, index, elem) == NULL; +} + +/** + * Allocates memory for an element at the specified index and returns a pointer to that memory. + * + * @remark When the list is storing pointers, this will return a @c void**. + * + * @param list the list + * @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 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); +} + + +/** + * Allocates memory for an element at the end of the list and returns a pointer to that memory. + * + * @remark When the list is storing pointers, this will return a @c void**. + * + * @param list the list + * @return a pointer to the allocated memory; @c NULL when the operation fails, or the index is out-of-bounds + * @see cxListEmplaceAt() + * @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); } /** @@ -571,8 +619,9 @@ /** * Removes and returns the element at the specified index. * - * No destructor is called and instead the element is copied to the + * No destructor is called, and instead the element is copied to the * @p targetbuf which MUST be large enough to hold the removed element. + * If the list is storing pointers, only the pointer is copied to @p targetbuf. * * @param list the list * @param index the index of the element @@ -591,11 +640,93 @@ } /** + * Removes and returns the first element of the list. + * + * No destructor is called, and instead the element is copied to the + * @p targetbuf which MUST be large enough to hold the removed element. + * If the list is storing pointers, only the pointer is copied to @p targetbuf. + * + * @param list the list + * @param targetbuf a buffer where to copy the element + * @retval zero success + * @retval non-zero 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; +} + +/** + * Removes and returns the first element of the list. + * + * Alias for cxListRemoveAndGetFirst(). + * + * No destructor is called, and instead the element is copied to the + * @p targetbuf which MUST be large enough to hold the removed element. + * If the list is storing pointers, only the pointer is copied to @p targetbuf. + * + * @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 + * @see cxListRemoveAndGetFirst() + * @see cxListPop() + */ +#define cxListPopFront(list, targetbuf) cxListRemoveAndGetFirst((list), (targetbuf)) + + +/** + * Removes and returns the last element of the list. + * + * No destructor is called, and instead the element is copied to the + * @p targetbuf which MUST be large enough to hold the removed element. + * If the list is storing pointers, only the pointer is copied to @p targetbuf. + * + * @param list the list + * @param targetbuf a buffer where to copy the element + * @retval zero success + * @retval non-zero 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; +} + +/** + * Removes and returns the last element of the list. + * + * Alias for cxListRemoveAndGetLast(). + * + * No destructor is called, and instead the element is copied to the + * @p targetbuf which MUST be large enough to hold the removed element. + * If the list is storing pointers, only the pointer is copied to @p targetbuf. + * + * @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 + * @see cxListRemoveAndGetLast() + * @see cxListPopFront() + */ +#define cxListPop(list, targetbuf) cxListRemoveAndGetLast((list), (targetbuf)) + +/** * Removes multiple element starting at the specified index. * * If an element destructor function is specified, it is called for each * element. It is guaranteed that the destructor is called before removing - * the element, however, due to possible optimizations it is neither guaranteed + * the element. However, due to possible optimizations, it is neither guaranteed * that the destructors are invoked for all elements before starting to remove * them, nor that the element is removed immediately after the destructor call * before proceeding to the next element. @@ -615,10 +746,11 @@ } /** - * Removes and returns multiple element starting at the specified index. + * Removes and returns multiple elements starting at the specified index. * - * No destructor is called and instead the elements are copied to the + * No destructor is called, and instead the elements are copied to the * @p targetbuf which MUST be large enough to hold all removed elements. + * If the list is storing pointers, @p targetbuf is expected to be an array of pointers. * * @param list the list * @param index the index of the element @@ -654,15 +786,15 @@ /** * Swaps two items in the list. * - * Implementations should only allocate temporary memory for the swap, if + * Implementations should only allocate temporary memory for the swap if * it is necessary. * * @param list the list * @param i the index of the first element * @param j the index of the second element * @retval zero success - * @retval non-zero one of the indices is out of bounds - * or the swap needed extra memory but allocation failed + * @retval non-zero one of the indices is out of bounds, + * or the swap needed extra memory, but allocation failed */ cx_attr_nonnull static inline int cxListSwap( @@ -677,6 +809,8 @@ /** * Returns a pointer to the element at the specified index. * + * If the list is storing pointers, returns the pointer stored at the specified index. + * * @param list the list * @param index the index of the element * @return a pointer to the element or @c NULL if the index is out of bounds @@ -690,22 +824,65 @@ } /** + * Returns a pointer to the first element. + * + * If the list is storing pointers, returns the first pointer stored in the list. + * + * @param list the list + * @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); +} + +/** + * Returns a pointer to the last element. + * + * If the list is storing pointers, returns the last pointer stored in the list. + * + * @param list the list + * @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); +} + +/** + * Sets the element at the specified index in the list + * + * @param list the list to set the element in + * @param index the index to set the element at + * @param elem element to set + * @retval zero on success + * @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 +); + +/** * Returns an iterator pointing to the item at the specified index. * * The returned iterator is position-aware. * - * If the index is out of range, a past-the-end iterator will be returned. + * 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_nonnull 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); } @@ -714,18 +891,18 @@ * * The returned iterator is position-aware. * - * If the index is out of range, a past-the-end iterator will be returned. + * 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_nonnull 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); } @@ -734,13 +911,12 @@ * * The returned iterator is position-aware. * - * If the index is out of range, a past-the-end iterator will be returned. + * 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_nonnull cx_attr_nodiscard cx_attr_export CxIterator cxListMutIteratorAt( @@ -754,13 +930,12 @@ * * The returned iterator is position-aware. * - * If the index is out of range, a past-the-end iterator will be returned. + * 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_nonnull cx_attr_nodiscard cx_attr_export CxIterator cxListMutBackwardsIteratorAt( @@ -773,14 +948,14 @@ * * The returned iterator is position-aware. * - * If the list is empty, a past-the-end iterator will be returned. + * 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_nonnull cx_attr_nodiscard static inline CxIterator cxListIterator(const CxList *list) { + if (list == NULL) list = cxEmptyList; return list->cl->iterator(list, 0, false); } @@ -789,14 +964,14 @@ * * The returned iterator is position-aware. * - * If the list is empty, a past-the-end iterator will be returned. + * 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_nonnull cx_attr_nodiscard static inline CxIterator cxListMutIterator(CxList *list) { + if (list == NULL) list = cxEmptyList; return cxListMutIteratorAt(list, 0); } @@ -806,14 +981,14 @@ * * The returned iterator is position-aware. * - * If the list is empty, a past-the-end iterator will be returned. + * 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_nonnull 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); } @@ -822,14 +997,14 @@ * * The returned iterator is position-aware. * - * If the list is empty, a past-the-end iterator will be returned. + * 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_nonnull cx_attr_nodiscard static inline CxIterator cxListMutBackwardsIterator(CxList *list) { + if (list == NULL) list = cxEmptyList; return cxListMutBackwardsIteratorAt(list, list->collection.size - 1); } @@ -842,6 +1017,7 @@ * @param elem the element to find * @return the index of the element or the size of the list when the element is not found * @see cxListIndexValid() + * @see cxListContains() */ cx_attr_nonnull cx_attr_nodiscard @@ -853,6 +1029,26 @@ } /** + * Checks, if the list contains the specified element. + * + * The elements are compared with the list's comparator function. + * + * @param list the list + * @param elem the element to find + * @retval true if the element is contained + * @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; +} + +/** * Checks if the specified index is within bounds. * * @param list the list @@ -894,6 +1090,7 @@ */ cx_attr_nonnull static inline void cxListSort(CxList *list) { + if (list->collection.sorted) return; list->cl->sort(list); list->collection.sorted = true; } @@ -942,17 +1139,6 @@ cx_attr_export void cxListFree(CxList *list); -/** - * A shared instance of an empty list. - * - * Writing to that list is not allowed. - * - * You can use this is 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; - #ifdef __cplusplus } // extern "C"