You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
350 lines
8.5 KiB
350 lines
8.5 KiB
/* |
|
* Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de> |
|
* 2013 Freie Universität Berlin |
|
* |
|
* This file is subject to the terms and conditions of the GNU Lesser |
|
* General Public License v2.1. See the file LICENSE in the top level |
|
* directory for more details. |
|
*/ |
|
|
|
/** |
|
* @addtogroup core_util |
|
* @{ |
|
* |
|
* @file |
|
* @brief Circular linked list |
|
* |
|
* This file contains a circularly and singly linked list implementation. |
|
* |
|
* Its operations are: |
|
* |
|
* operation | runtime | description |
|
* ---------------------|---------|--------------- |
|
* clist_lpush() | O(1) | insert as head (leftmost node) |
|
* clist_lpop() | O(1) | remove and return head (leftmost node) |
|
* clist_rpush() | O(1) | append as tail (rightmost node) |
|
* clist_rpop() | O(n) | remove and return tail (rightmost node) |
|
* clist_find() | O(n) | find and return node |
|
* clist_find_before() | O(n) | find node return node pointing to node |
|
* clist_remove() | O(n) | remove and return node |
|
* |
|
* clist can be used as a traditional list, a queue (FIFO) and a stack (LIFO) using |
|
* fast O(1) operations. |
|
* |
|
* When used as traditional list, in order to traverse, make sure every element |
|
* is only visited once. |
|
* |
|
* Example: |
|
* |
|
* void clist_traverse(clist_node_t *list) { |
|
* clist_node_t *node = list->next; |
|
* if (! node) { |
|
* puts("list empty"); |
|
* return; |
|
* } |
|
* |
|
* do { |
|
* node = node->next; |
|
* // do something with node |
|
* } while (node != list->next); |
|
* } |
|
* |
|
* Or use the clist_foreach() helper function, e.g.,: |
|
* |
|
* static int _print_node(clist_node_t *node) |
|
* { |
|
* printf("0x%08x ", (unsigned)node); |
|
* return 0; |
|
* } |
|
* |
|
* [...] |
|
* clist_foreach(&list, _print_node); |
|
* |
|
* To use clist as a queue, use clist_rpush() for adding elements and clist_lpop() |
|
* for removal. Using clist_lpush() and clist_rpop() is inefficient due to |
|
* clist_rpop()'s O(n) runtime. |
|
* |
|
* To use clist as stack, use clist_lpush()/clist_lpop(). |
|
* |
|
* Implementation details: |
|
* |
|
* Each list is represented as a "clist_node_t". Its only member, the "next" |
|
* pointer, points to the last entry in the list, whose "next" pointer points to |
|
* the first entry. |
|
* Actual list objects should have a @c clist_node_t as member and then use |
|
* the container_of() macro in list operations. |
|
* See @ref thread_add_to_list() as example. |
|
* |
|
* @author Kaspar Schleiser <kaspar@schleiser.de> |
|
*/ |
|
|
|
#ifndef CLIST_H |
|
#define CLIST_H |
|
|
|
#include <stddef.h> |
|
#include "list.h" |
|
|
|
#ifdef __cplusplus |
|
extern "C" { |
|
#endif |
|
|
|
/** |
|
* @brief List node structure |
|
* |
|
* Used as is as reference to a list. |
|
* |
|
*/ |
|
typedef list_node_t clist_node_t; |
|
|
|
/** |
|
* @brief Appends *new_node* at the end of *list |
|
* |
|
* @note Complexity: O(1) |
|
* |
|
* @param[in,out] list Pointer to clist |
|
* @param[in,out] new_node Node which gets inserted. |
|
* Must not be NULL. |
|
*/ |
|
static inline void clist_rpush(clist_node_t *list, clist_node_t *new_node) |
|
{ |
|
if (list->next) { |
|
new_node->next = list->next->next; |
|
list->next->next = new_node; |
|
} |
|
else { |
|
new_node->next = new_node; |
|
} |
|
list->next = new_node; |
|
} |
|
|
|
|
|
/** |
|
* @brief Inserts *new_node* at the beginning of *list |
|
* |
|
* @note Complexity: O(1) |
|
* |
|
* @param[in,out] list Pointer to clist |
|
* @param[in,out] new_node Node which gets inserted. |
|
* Must not be NULL. |
|
*/ |
|
static inline void clist_lpush(clist_node_t *list, clist_node_t *new_node) |
|
{ |
|
if (list->next) { |
|
new_node->next = list->next->next; |
|
list->next->next = new_node; |
|
} |
|
else { |
|
new_node->next = new_node; |
|
list->next = new_node; |
|
} |
|
} |
|
|
|
/** |
|
* @brief Removes and returns first element from list |
|
* |
|
* @note Complexity: O(1) |
|
* |
|
* @param[in,out] list Pointer to the *list* to remove first element |
|
* from. |
|
*/ |
|
static inline clist_node_t *clist_lpop(clist_node_t *list) |
|
{ |
|
if (list->next) { |
|
clist_node_t *first = list->next->next; |
|
if (list->next == first) { |
|
list->next = NULL; |
|
} |
|
else { |
|
list->next->next = first->next; |
|
} |
|
return first; |
|
} |
|
else { |
|
return NULL; |
|
} |
|
} |
|
|
|
/** |
|
* @brief Advances the circle list. |
|
* |
|
* The result of this function is will be a list with |
|
* nodes shifted by one. So second list entry will be |
|
* first, first is last. |
|
* |
|
* [ A, B, C ] becomes [ B, C, A ] |
|
* |
|
* @note Complexity: O(1) |
|
* |
|
* @param[in,out] list The list to work upon. |
|
*/ |
|
static inline void clist_lpoprpush(clist_node_t *list) |
|
{ |
|
if (list->next) { |
|
list->next = list->next->next; |
|
} |
|
} |
|
|
|
/** |
|
* @brief Returns first element in list |
|
* |
|
* @note: Complexity: O(1) |
|
* |
|
* @param[in] list The list to work upon. |
|
* @returns first (leftmost) list element, or NULL if list is empty |
|
*/ |
|
static inline clist_node_t *clist_lpeek(const clist_node_t *list) |
|
{ |
|
if (list->next) { |
|
return list->next->next; |
|
} |
|
return NULL; |
|
} |
|
|
|
/** |
|
* @brief Returns last element in list |
|
* |
|
* @note: Complexity: O(1) |
|
* |
|
* @param[in] list The list to work upon. |
|
* @returns last (rightmost) list element, or NULL if list is empty |
|
*/ |
|
static inline clist_node_t *clist_rpeek(const clist_node_t *list) |
|
{ |
|
return list->next; |
|
} |
|
|
|
/** |
|
* @brief Removes and returns last element from list |
|
* |
|
* @note Complexity: O(n) with n being the number of elements in the list. |
|
* |
|
* @param[in,out] list Pointer to the *list* to remove last element |
|
* from. |
|
*/ |
|
static inline clist_node_t *clist_rpop(clist_node_t *list) |
|
{ |
|
if (list->next) { |
|
list_node_t *last = list->next; |
|
while (list->next->next != last) { |
|
clist_lpoprpush(list); |
|
} |
|
return clist_lpop(list); |
|
} |
|
else { |
|
return NULL; |
|
} |
|
} |
|
|
|
/** |
|
* @brief Finds node and returns its predecessor |
|
* |
|
* @note Complexity: O(n) |
|
* |
|
* @param[in] list pointer to clist |
|
* @param[in,out] node Node to look for |
|
* Must not be NULL. |
|
* |
|
* @returns predecessor of node if found |
|
* @returns NULL if node is not a list member |
|
*/ |
|
static inline clist_node_t *clist_find_before(const clist_node_t *list, const clist_node_t *node) |
|
{ |
|
clist_node_t *pos = list->next; |
|
if (!pos) { |
|
return NULL; |
|
} |
|
do { |
|
pos = pos->next; |
|
if (pos->next == node) { |
|
return pos; |
|
} |
|
} while (pos != list->next); |
|
|
|
return NULL; |
|
} |
|
|
|
/** |
|
* @brief Finds and returns node |
|
* |
|
* @note Complexity: O(n) |
|
* |
|
* @param[in] list pointer to clist |
|
* @param[in,out] node Node to look for |
|
* Must not be NULL. |
|
* |
|
* @returns node if found |
|
* @returns NULL if node is not a list member |
|
*/ |
|
static inline clist_node_t *clist_find(const clist_node_t *list, const clist_node_t *node) |
|
{ |
|
clist_node_t *tmp = clist_find_before(list, node); |
|
if (tmp) { |
|
return tmp->next; |
|
} |
|
else { |
|
return NULL; |
|
} |
|
} |
|
|
|
/** |
|
* @brief Finds and removes node |
|
* |
|
* @note Complexity: O(n) |
|
* |
|
* @param[in] list pointer to clist |
|
* @param[in,out] node Node to remove for |
|
* Must not be NULL. |
|
* |
|
* @returns node if found and removed |
|
* @returns NULL if node is not a list member |
|
*/ |
|
static inline clist_node_t *clist_remove(clist_node_t *list, clist_node_t *node) |
|
{ |
|
if (list->next) { |
|
if (list->next->next == node) { |
|
return clist_lpop(list); |
|
} |
|
else { |
|
clist_node_t *tmp = clist_find_before(list, node); |
|
if (tmp) { |
|
tmp->next = tmp->next->next; |
|
if (node == list->next) { |
|
list->next = tmp; |
|
} |
|
return node; |
|
} |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
/** |
|
* @brief Traverse clist, call function for each member |
|
* |
|
* If @p func returns non-zero, traversal will be aborted like when calling |
|
* break within a for loop. |
|
* |
|
* @param[in] list List to traverse. |
|
* @param[in] func Function to call for each member. |
|
*/ |
|
static inline void clist_foreach(clist_node_t *list, int(*func)(clist_node_t *)) |
|
{ |
|
clist_node_t *node = list->next; |
|
if (! node) { |
|
return; |
|
} |
|
do { |
|
node = node->next; |
|
if (func(node)) { |
|
return; |
|
} |
|
} while (node != list->next); |
|
} |
|
|
|
#ifdef __cplusplus |
|
} |
|
#endif |
|
|
|
#endif /* CLIST_H */ |
|
/** @} */
|
|
|