src/cx/list.h

Fri, 23 May 2025 12:44:24 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 23 May 2025 12:44:24 +0200
changeset 1327
ed75dc1db503
parent 1316
c41538edfcef
permissions
-rw-r--r--

make test-compile depend on both static and shared

the shared lib is not needed for the tests,
but when run with coverage, gcov will be confused
when outdated line information is available from
a previous shared build

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2021 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 list.h
 * @brief Interface for list implementations.
 * @author Mike Becker
 * @author Olaf Wintermann
 * @copyright 2-Clause BSD License
 */

#ifndef UCX_LIST_H
#define UCX_LIST_H

#include "common.h"
#include "collection.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
 * List class type.
 */
typedef struct cx_list_class_s cx_list_class;

/**
 * Structure for holding the base data of a list.
 */
struct cx_list_s {
    /**
     * Common members for collections.
     */
    CX_COLLECTION_BASE;
    /**
     * The list class definition.
     */
    const cx_list_class *cl;
    /**
     * The actual implementation in case the list class is delegating.
     */
    const cx_list_class *climpl;
};

/**
 * The class definition for arbitrary lists.
 */
struct cx_list_class_s {
    /**
     * Destructor function.
     *
     * Implementations SHALL invoke the content destructor functions if provided
     * and SHALL deallocate the entire list memory.
     */
    void (*deallocate)(struct cx_list_s *list);

    /**
     * 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 data of the inserted element.
     */
    void *(*insert_element)(
            struct cx_list_s *list,
            size_t index,
            const void *data
    );

    /**
     * Member function for inserting multiple 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
    );

    /**
     * Member function for inserting sorted elements into a sorted list.
     *
     * @see cx_list_default_insert_sorted()
     */
    size_t (*insert_sorted)(
            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
    );

    /**
     * Member function for removing elements.
     *
     * Implementations SHALL check if @p targetbuf is set and copy the elements
     * to the buffer without invoking any destructor.
     * When @p targetbuf is not set, the destructors SHALL be invoked.
     *
     * 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
    );

    /**
     * Member function for removing all elements.
     */
    void (*clear)(struct cx_list_s *list);

    /**
     * Member function for swapping two elements.
     *
     * @see cx_list_default_swap()
     */
    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
    );

    /**
     * Member function for finding and optionally removing an element.
     */
    size_t (*find_remove)(
            struct cx_list_s *list,
            const void *elem,
            bool remove
    );

    /**
     * Member function for sorting the list.
     *
     * @see cx_list_default_sort()
     */
    void (*sort)(struct cx_list_s *list);

    /**
     * Optional member function for comparing this list
     * 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
    );

    /**
     * Member function for reversing the order of the items.
     */
    void (*reverse)(struct cx_list_s *list);

    /**
     * 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
    );
};

/**
 * 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.
 *
 * 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 index the index where to insert the data
 * @param data a pointer to the array of data to insert
 * @param n the number of elements to insert
 * @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
);

/**
 * Default implementation of a sorted insert.
 *
 * This function uses the array insert function to insert consecutive groups
 * of sorted data.
 *
 * The source data @em must already be sorted wrt. the list's compare function.
 *
 * 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 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
);

/**
 * Default unoptimized sort implementation.
 *
 * This function will copy all data to an array, sort the array with standard
 * qsort, and then copy the data back to the list memory.
 *
 * Use this in your own list class if you do not want to implement an optimized
 * version for your list.
 *
 * @param list the list that shall be sorted
 */
cx_attr_nonnull
cx_attr_export
void cx_list_default_sort(struct cx_list_s *list);

/**
 * Default unoptimized swap implementation.
 *
 * Use this in your own list class if you do not want to implement an optimized
 * version for your list.
 *
 * @param list the list in which to swap
 * @param i index of one element
 * @param j index of the other element
 * @retval zero success
 * @retval non-zero when indices are out of bounds or memory
 * 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);

/**
 * 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.
 *
 * 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
 * the feature of storing pointers.
 *
 * @par Example
 *
 * @code
 * CxList *myCustomListCreate(
 *         const CxAllocator *allocator,
 *         cx_compare_func comparator,
 *         size_t elem_size
 * ) {
 *     if (allocator == NULL) {
 *         allocator = cxDefaultAllocator;
 *     }
 *
 *     MyCustomList *list = cxCalloc(allocator, 1, sizeof(MyCustomList));
 *     if (list == NULL) return NULL;
 *
 *     // initialize
 *     cx_list_init((CxList*)list, &my_custom_list_class,
 *             allocator, comparator, elem_size);
 *
 *     // ... some more custom stuff ...
 *
 *     return (CxList *) list;
 * }
 * @endcode
 *
 * @param list the list to initialize
 * @param cl the list class
 * @param allocator the allocator for the elements
 * @param comparator a compare function for the elements
 * @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
);

/**
 * Returns the number of elements currently stored in the list.
 *
 * @param list the list
 * @return the number of currently stored elements
 */
cx_attr_nonnull
static inline size_t cxListSize(const CxList *list) {
    return list->collection.size;
}

/**
 * Adds an item to the end of the list.
 *
 * @param list the list
 * @param elem a pointer to the element to add
 * @retval zero success
 * @retval non-zero memory allocation failure
 * @see cxListAddArray()
 * @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;
}

/**
 * Adds multiple items to the end of the list.
 *
 * This method is more efficient than invoking cxListAdd() multiple times.
 *
 * 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
 * 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
 */
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);
}

/**
 * Inserts an item at the specified index.
 *
 * If @p index equals the list @c size, this is effectively cxListAdd().
 *
 * @param list the list
 * @param index the index the element shall have
 * @param elem a pointer to the element to add
 * @retval zero success
 * @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(
        CxList *list,
        size_t index,
        const void *elem
) {
    list->collection.sorted = false;
    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);
}

/**
 * Inserts an item into a sorted list.
 *
 * If the list is not sorted already, the behavior is undefined.
 *
 * @param list the list
 * @param elem a pointer to the element to add
 * @retval zero success
 * @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;
}

/**
 * Inserts multiple items to the list at the specified index.
 * If @p index equals the list size, this is effectively cxListAddArray().
 *
 * This method is usually more efficient than invoking cxListInsert()
 * multiple times.
 *
 * 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
 * be an array of pointers.
 *
 * @param list the list
 * @param index the index where to add the elements
 * @param array a pointer to the elements to add
 * @param n the number of elements to add
 * @return the number of added elements
 */
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);
}

/**
 * Inserts a sorted array into a sorted list.
 *
 * 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
 * be an array of pointers.
 *
 * If the list is not sorted already, the behavior is undefined.
 *
 * @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
 */
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);
}

/**
 * Inserts an element after the current location of the specified iterator.
 *
 * The used iterator remains operational, but all other active iterators should
 * be considered invalidated.
 *
 * If @p iter is not a list iterator, the behavior is undefined.
 * If @p iter is a past-the-end iterator, the new element gets appended to the list.
 *
 * @param iter an iterator
 * @param elem the element to insert
 * @retval zero success
 * @retval non-zero memory allocation failure
 * @see cxListInsert()
 * @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);
}

/**
 * Inserts an element before the current location of the specified iterator.
 *
 * The used iterator remains operational, but all other active iterators should
 * be considered invalidated.
 *
 * If @p iter is not a list iterator, the behavior is undefined.
 * If @p iter is a past-the-end iterator, the new element gets appended to the list.
 *
 * @param iter an iterator
 * @param elem the element to insert
 * @retval zero success
 * @retval non-zero memory allocation failure
 * @see cxListInsert()
 * @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);
}

/**
 * Removes the element at the specified index.
 *
 * If an element destructor function is specified, it is called before
 * removing the element.
 *
 * @param list the list
 * @param index the index of the element
 * @retval zero success
 * @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;
}

/**
 * Removes and returns the element at the specified index.
 *
 * 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
 * @param targetbuf a buffer where to copy the element
 * @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;
}

/**
 * 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
 * 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.
 *
 * @param list the list
 * @param index the index of the element
 * @param num the number of elements to remove
 * @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);
}

/**
 * Removes and returns multiple elements starting at the specified index.
 *
 * 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
 * @param num the number of elements to remove
 * @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);
}

/**
 * Removes all elements from this list.
 *
 * If element destructor functions are specified, they are called for each
 * element before removing them.
 *
 * @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);
}

/**
 * Swaps two items in the list.
 *
 * 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
 */
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);
}

/**
 * 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
 */
cx_attr_nonnull
static inline void *cxListAt(
        const CxList *list,
        size_t index
) {
    return list->cl->at(list, index);
}

/**
 * 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.
 *
 * @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
) {
    return list->cl->iterator(list, index, false);
}

/**
 * Returns a backwards 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.
 *
 * @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
) {
    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, 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(
        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, 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(
        CxList *list,
        size_t index
);

/**
 * Returns an 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 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);
}


/**
 * Returns a 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 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);
}

/**
 * Returns the index of the first element that equals @p elem.
 *
 * Determining equality is performed by the list's comparator function.
 *
 * @param list the list
 * @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
static inline size_t cxListFind(
        const CxList *list,
        const void *elem
) {
    return list->cl->find_remove((CxList*)list, elem, false);
}

/**
 * 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
 * @param index the index
 * @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;
}

/**
 * Removes and returns the index of the first element that equals @p elem.
 *
 * Determining equality is performed by the list's comparator function.
 *
 * @param list the list
 * @param elem the element to find and remove
 * @return the index of the now removed element or the list size
 * when the element is not found or could not be removed
 * @see cxListIndexValid()
 */
cx_attr_nonnull
static inline size_t cxListFindRemove(
        CxList *list,
        const void *elem
) {
    return list->cl->find_remove(list, elem, true);
}

/**
 * Sorts the list.
 *
 * @remark The underlying sort algorithm is implementation defined.
 *
 * @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;
}

/**
 * Reverses the order of the items.
 *
 * @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);
}

/**
 * Compares a list to another list of the same type.
 *
 * First, the list sizes are compared.
 * If they match, the lists are compared element-wise.
 *
 * @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
 * 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
);

/**
 * 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
 */
cx_attr_export
void cxListFree(CxList *list);


#ifdef __cplusplus
} // extern "C"
#endif

#endif // UCX_LIST_H

mercurial