/** * @file list.h * * @brief A doubly-linked list * * List Implementation * =================== * * - This @ref List operates as a linked list @ref ListNodes. It operates as a regular * doubly-linked list but doesn't allocate memory and rather gets @ref ListNodes from a pool. */ #ifndef LIST_H #define LIST_H #include /** * @def MAX_LIST_NODES * @brief Number of reserved list nodes. * * Number of list nodes available from the pool of @ref ListNode . This should * be set to to the maximum number of list nodes needed at once. */ #define MAX_LIST_NODES 128 /** * @brief Default list declaration for empty lists */ // clang-format off #define LIST_DEFAULT { .head = NULL, .tail = NULL, .len = 0 } // clang-format on typedef struct ListNode ListNode; /** * @brief A single entry in a @ref List */ struct ListNode { /** * @brief The previous @ref ListNode in the associated @ref List, NULL if at the `head` of the * list */ ListNode* prev; /** * @brief The next @ref ListNode in the associated @ref List, NULL if at the `tail` of the list */ ListNode* next; /** * @brief Pointer to generic data stored in this node */ void* data; }; /** * @brief A doubly-linked list */ typedef struct List { /** * @brief The first entry in the list */ ListNode* head; /** * @brief The last entry in the list */ ListNode* tail; /** * @brief Number of elements in list */ int len; } List; /** * @brief @ref ListItr direction */ enum ListItrDirection { LIST_ITR_FORWARD, LIST_ITR_REVERSE, }; /** * @brief An iterator into a list */ typedef struct { /** * @brief A pointer to the @ref List this is iterating through */ List* list; /** * @brief The next node in the list */ ListNode* next_node; /** * @brief The current node in the list iterator * * The node of the most recently returned data from @ref list_itr_next() . */ ListNode* current_node; /** * @brief The direction of the iterator */ enum ListItrDirection direction; } ListItr; /** * Initialize a list. * * Set the values of a list to default. * * If using this function to reset a list, the list must be freed with @ref list_clear to ensure the * list's nodes are deleted properly. * * @return A @ref List with head and tail reset. */ List list_init(void); /** * Clear a list. * * Go through the list and free each node and set the `head` and `tail` to `NULL`. * Note, it doesn't "free" the data at the node. * * @note To reset an existing list to default values, first call `list_clear` then @ref list_init * * @param list pointer to a @ref List to clear */ void list_clear(List* list); /** * Check if a list is empty * * @param list pointer to a @ref List * * @return `true` if the `list` is empty, `false` otherwise. */ bool list_is_empty(const List* list); /** * Prepend an entry to the `head` of a @ref list * * @param list pointer to a @ref List * @param data pointer to data to put into the @ref List */ void list_push_front(List* list, void* data); /** * Append an entry to the `tail` of a @ref list * * @param list pointer to a @ref List * @param data pointer to data to put into the @ref List */ void list_push_back(List* list, void* data); /** * Insert data into a @ref List a specific index * * If the index specified is larger than the length of the list * it will @ref list_push_back() the data instead; * * Performs the following operation: * * ┌─────┐ * │ node│ * └─────┘ * ┌─────┐ ┌─────┐ ┌─────┐ * │idx-1│◄─►│ idx │◄─►│idx+1│ * └─────┘ └─────┘ └─────┘ * * 1. Set new `node` `prev` to the node at idx - 1 * 2. Set new `node` `next` to the node at idx * 3. Set node at idx - 1 `next` to new `node` * 4. Set node at idx `prev` to the new `node` * * Result: * * ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ * │idx-1│◄─►│ node│◄─►│ idx │◄─►│idx+1│ * └─────┘ └─────┘ └─────┘ └─────┘ * * Finally, the list is now updated with new `node` now at the labeled idx: * * ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ * │idx-1│◄─►│ idx │◄─►│idx+1│◄─►│idx+2│ * └─────┘ └─────┘ └─────┘ └─────┘ * * @param list pointer to a @ref List * @param data pointer to data to put into the @ref List * @param idx desired index to insert */ void list_insert(List* list, void* data, unsigned int idx); /** * Swap the data pointers at the specified indices of a @ref List * * If either indices are larger than the length of the list, return false. * * @param list pointer to a @ref List * @param idx_a desired index to swap with idx_b * @param idx_b desired index to swap with idx_a * * @return true if successful, false otherwise */ bool list_swap(List* list, unsigned int idx_a, unsigned int idx_b); /** * Get a List's node at the specified index * * @param list pointer to a @ref List * @param idx index of the desired @ref ListNode in the list * * @return a pointer to the data at the index of the list, or NULL if out-of-bounds */ void* list_get_at_idx(List* list, unsigned int idx); /** * Remove a List's node at the specified index * * @param list pointer to a @ref List * @param idx index of the desired @ref ListNode in the list * * @return `true` if successfully removed, `false` if out-of-bounds */ bool list_remove_at_idx(List* list, unsigned int idx); /** * Remove a List's node with the matching pointer * * @param list pointer to a @ref List * @param data pointer to data in node in list * * @return `true` if successfully removed, `false` otherwise * * @note When working with @ref ListItr, use @ref list_itr_remove_current_node() */ bool list_remove_data(List* list, void* data); /** * Get the number of elements in a @ref List * * @param list pointer to a @ref List * * @return The number of elements in the list */ int list_get_len(const List* list); /** * Declare a @ref ListItr * * @param list pointer to a @ref List * * @return A new @ref ListItr */ ListItr list_itr_create(List* list); /** * Declare a reverse @ref ListItr * * @param list pointer to a @ref List * * @return A new reverse @ref ListItr */ ListItr rev_list_itr_create(List* list); /** * Get the next data entry in a @ref ListItr * * @param itr pointer to the @ref ListItr * * @return A pointer to the data pointer at the next @ref ListNode if valid, otherwise return NULL. */ void* list_itr_next(ListItr* itr); /** * Remove the current @ref ListNode from the iterator. * * The "current node" corresponds to the list node associated with the * most recently returned valu from @ref list_itr_next() * * @param itr pointer to the @ref ListItr * * @note When working with @ref ListItr, use this and not @ref list_remove_at() as it will * "break" the iterator. */ void list_itr_remove_current_node(ListItr* itr); #endif