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

/*
* 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 */
/** @} */