LCOV - code coverage report
Current view: top level - zephyr/sys - dlist.h Hit Total Coverage
Test: new.info Lines: 32 33 97.0 %
Date: 2024-12-22 00:14:23

          Line data    Source code
       1           0 : /*
       2             :  * Copyright (c) 2013-2015 Wind River Systems, Inc.
       3             :  *
       4             :  * SPDX-License-Identifier: Apache-2.0
       5             :  */
       6             : 
       7             : /**
       8             :  * @file
       9             :  * @defgroup doubly-linked-list_apis Doubly-linked list
      10             :  * @ingroup datastructure_apis
      11             :  *
      12             :  * @brief Doubly-linked list implementation
      13             :  *
      14             :  * Doubly-linked list implementation using inline macros/functions.
      15             :  * This API is not thread safe, and thus if a list is used across threads,
      16             :  * calls to functions must be protected with synchronization primitives.
      17             :  *
      18             :  * The lists are expected to be initialized such that both the head and tail
      19             :  * pointers point to the list itself.  Initializing the lists in such a fashion
      20             :  * simplifies the adding and removing of nodes to/from the list.
      21             :  *
      22             :  * @{
      23             :  */
      24             : 
      25             : #ifndef ZEPHYR_INCLUDE_SYS_DLIST_H_
      26             : #define ZEPHYR_INCLUDE_SYS_DLIST_H_
      27             : 
      28             : #include <stddef.h>
      29             : #include <stdbool.h>
      30             : 
      31             : #ifdef __cplusplus
      32             : extern "C" {
      33             : #endif
      34             : 
      35             : 
      36             : struct _dnode {
      37             :         union {
      38             :                 struct _dnode *head; /* ptr to head of list (sys_dlist_t) */
      39             :                 struct _dnode *next; /* ptr to next node    (sys_dnode_t) */
      40             :         };
      41             :         union {
      42             :                 struct _dnode *tail; /* ptr to tail of list (sys_dlist_t) */
      43             :                 struct _dnode *prev; /* ptr to previous node (sys_dnode_t) */
      44             :         };
      45             : };
      46             : 
      47             : /**
      48             :  * @brief Doubly-linked list structure.
      49             :  */
      50           1 : typedef struct _dnode sys_dlist_t;
      51             : /**
      52             :  * @brief Doubly-linked list node structure.
      53             :  */
      54           1 : typedef struct _dnode sys_dnode_t;
      55             : 
      56             : 
      57             : /**
      58             :  * @brief Provide the primitive to iterate on a list
      59             :  * Note: the loop is unsafe and thus __dn should not be removed
      60             :  *
      61             :  * User _MUST_ add the loop statement curly braces enclosing its own code:
      62             :  *
      63             :  *     SYS_DLIST_FOR_EACH_NODE(l, n) {
      64             :  *         <user code>
      65             :  *     }
      66             :  *
      67             :  * This and other SYS_DLIST_*() macros are not thread safe.
      68             :  *
      69             :  * @param __dl A pointer on a sys_dlist_t to iterate on
      70             :  * @param __dn A sys_dnode_t pointer to peek each node of the list
      71             :  */
      72           1 : #define SYS_DLIST_FOR_EACH_NODE(__dl, __dn)                             \
      73             :         for (__dn = sys_dlist_peek_head(__dl); __dn != NULL;            \
      74             :              __dn = sys_dlist_peek_next(__dl, __dn))
      75             : 
      76             : /**
      77             :  * @brief Provide the primitive to iterate on a list, from a node in the list
      78             :  * Note: the loop is unsafe and thus __dn should not be removed
      79             :  *
      80             :  * User _MUST_ add the loop statement curly braces enclosing its own code:
      81             :  *
      82             :  *     SYS_DLIST_ITERATE_FROM_NODE(l, n) {
      83             :  *         <user code>
      84             :  *     }
      85             :  *
      86             :  * Like SYS_DLIST_FOR_EACH_NODE(), but __dn already contains a node in the list
      87             :  * where to start searching for the next entry from. If NULL, it starts from
      88             :  * the head.
      89             :  *
      90             :  * This and other SYS_DLIST_*() macros are not thread safe.
      91             :  *
      92             :  * @param __dl A pointer on a sys_dlist_t to iterate on
      93             :  * @param __dn A sys_dnode_t pointer to peek each node of the list;
      94             :  *             it contains the starting node, or NULL to start from the head
      95             :  */
      96           1 : #define SYS_DLIST_ITERATE_FROM_NODE(__dl, __dn) \
      97             :         for (__dn = __dn ? sys_dlist_peek_next_no_check(__dl, __dn) \
      98             :                          : sys_dlist_peek_head(__dl); \
      99             :              __dn != NULL; \
     100             :              __dn = sys_dlist_peek_next(__dl, __dn))
     101             : 
     102             : /**
     103             :  * @brief Provide the primitive to safely iterate on a list
     104             :  * Note: __dn can be removed, it will not break the loop.
     105             :  *
     106             :  * User _MUST_ add the loop statement curly braces enclosing its own code:
     107             :  *
     108             :  *     SYS_DLIST_FOR_EACH_NODE_SAFE(l, n, s) {
     109             :  *         <user code>
     110             :  *     }
     111             :  *
     112             :  * This and other SYS_DLIST_*() macros are not thread safe.
     113             :  *
     114             :  * @param __dl A pointer on a sys_dlist_t to iterate on
     115             :  * @param __dn A sys_dnode_t pointer to peek each node of the list
     116             :  * @param __dns A sys_dnode_t pointer for the loop to run safely
     117             :  */
     118           1 : #define SYS_DLIST_FOR_EACH_NODE_SAFE(__dl, __dn, __dns)                 \
     119             :         for ((__dn) = sys_dlist_peek_head(__dl),                        \
     120             :                      (__dns) = sys_dlist_peek_next((__dl), (__dn));     \
     121             :              (__dn) != NULL; (__dn) = (__dns),                          \
     122             :                      (__dns) = sys_dlist_peek_next(__dl, __dn))
     123             : 
     124             : /**
     125             :  * @brief Provide the primitive to resolve the container of a list node
     126             :  * Note: it is safe to use with NULL pointer nodes
     127             :  *
     128             :  * @param __dn A pointer on a sys_dnode_t to get its container
     129             :  * @param __cn Container struct type pointer
     130             :  * @param __n The field name of sys_dnode_t within the container struct
     131             :  */
     132           1 : #define SYS_DLIST_CONTAINER(__dn, __cn, __n) \
     133             :         (((__dn) != NULL) ? CONTAINER_OF(__dn, __typeof__(*(__cn)), __n) : NULL)
     134             : /**
     135             :  * @brief Provide the primitive to peek container of the list head
     136             :  *
     137             :  * @param __dl A pointer on a sys_dlist_t to peek
     138             :  * @param __cn Container struct type pointer
     139             :  * @param __n The field name of sys_dnode_t within the container struct
     140             :  */
     141           1 : #define SYS_DLIST_PEEK_HEAD_CONTAINER(__dl, __cn, __n) \
     142             :         SYS_DLIST_CONTAINER(sys_dlist_peek_head(__dl), __cn, __n)
     143             : 
     144             : /**
     145             :  * @brief Provide the primitive to peek the next container
     146             :  *
     147             :  * @param __dl A pointer on a sys_dlist_t to peek
     148             :  * @param __cn Container struct type pointer
     149             :  * @param __n The field name of sys_dnode_t within the container struct
     150             :  */
     151           1 : #define SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n) \
     152             :         (((__cn) != NULL) ? \
     153             :          SYS_DLIST_CONTAINER(sys_dlist_peek_next((__dl), &((__cn)->__n)),        \
     154             :                                       __cn, __n) : NULL)
     155             : 
     156             : /**
     157             :  * @brief Provide the primitive to iterate on a list under a container
     158             :  * Note: the loop is unsafe and thus __cn should not be detached
     159             :  *
     160             :  * User _MUST_ add the loop statement curly braces enclosing its own code:
     161             :  *
     162             :  *     SYS_DLIST_FOR_EACH_CONTAINER(l, c, n) {
     163             :  *         <user code>
     164             :  *     }
     165             :  *
     166             :  * @param __dl A pointer on a sys_dlist_t to iterate on
     167             :  * @param __cn A container struct type pointer to peek each entry of the list
     168             :  * @param __n The field name of sys_dnode_t within the container struct
     169             :  */
     170           1 : #define SYS_DLIST_FOR_EACH_CONTAINER(__dl, __cn, __n)                   \
     171             :         for ((__cn) = SYS_DLIST_PEEK_HEAD_CONTAINER(__dl, __cn, __n);     \
     172             :              (__cn) != NULL;                                              \
     173             :              (__cn) = SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n))
     174             : 
     175             : /**
     176             :  * @brief Provide the primitive to safely iterate on a list under a container
     177             :  * Note: __cn can be detached, it will not break the loop.
     178             :  *
     179             :  * User _MUST_ add the loop statement curly braces enclosing its own code:
     180             :  *
     181             :  *     SYS_DLIST_FOR_EACH_CONTAINER_SAFE(l, c, cn, n) {
     182             :  *         <user code>
     183             :  *     }
     184             :  *
     185             :  * @param __dl A pointer on a sys_dlist_t to iterate on
     186             :  * @param __cn A container struct type pointer to peek each entry of the list
     187             :  * @param __cns A container struct type pointer for the loop to run safely
     188             :  * @param __n The field name of sys_dnode_t within the container struct
     189             :  */
     190           1 : #define SYS_DLIST_FOR_EACH_CONTAINER_SAFE(__dl, __cn, __cns, __n)       \
     191             :         for ((__cn) = SYS_DLIST_PEEK_HEAD_CONTAINER(__dl, __cn, __n),   \
     192             :              (__cns) = SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n);    \
     193             :              (__cn) != NULL; (__cn) = (__cns),                          \
     194             :              (__cns) = SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n))
     195             : 
     196             : /**
     197             :  * @brief initialize list to its empty state
     198             :  *
     199             :  * @param list the doubly-linked list
     200             :  */
     201             : 
     202           1 : static inline void sys_dlist_init(sys_dlist_t *list)
     203             : {
     204             :         list->head = (sys_dnode_t *)list;
     205             :         list->tail = (sys_dnode_t *)list;
     206             : }
     207             : 
     208             : /**
     209             :  * @brief Static initializer for a doubly-linked list
     210             :  */
     211           1 : #define SYS_DLIST_STATIC_INIT(ptr_to_list) { {(ptr_to_list)}, {(ptr_to_list)} }
     212             : 
     213             : /**
     214             :  * @brief initialize node to its state when not in a list
     215             :  *
     216             :  * @param node the node
     217             :  */
     218             : 
     219           1 : static inline void sys_dnode_init(sys_dnode_t *node)
     220             : {
     221             :         node->next = NULL;
     222             :         node->prev = NULL;
     223             : }
     224             : 
     225             : /**
     226             :  * @brief check if a node is a member of any list
     227             :  *
     228             :  * @param node the node
     229             :  *
     230             :  * @return true if node is linked into a list, false if it is not
     231             :  */
     232             : 
     233           1 : static inline bool sys_dnode_is_linked(const sys_dnode_t *node)
     234             : {
     235             :         return node->next != NULL;
     236             : }
     237             : 
     238             : /**
     239             :  * @brief check if a node is the list's head
     240             :  *
     241             :  * @param list the doubly-linked list to operate on
     242             :  * @param node the node to check
     243             :  *
     244             :  * @return true if node is the head, false otherwise
     245             :  */
     246             : 
     247           1 : static inline bool sys_dlist_is_head(sys_dlist_t *list, sys_dnode_t *node)
     248             : {
     249             :         return list->head == node;
     250             : }
     251             : 
     252             : /**
     253             :  * @brief check if a node is the list's tail
     254             :  *
     255             :  * @param list the doubly-linked list to operate on
     256             :  * @param node the node to check
     257             :  *
     258             :  * @return true if node is the tail, false otherwise
     259             :  */
     260             : 
     261           1 : static inline bool sys_dlist_is_tail(sys_dlist_t *list, sys_dnode_t *node)
     262             : {
     263             :         return list->tail == node;
     264             : }
     265             : 
     266             : /**
     267             :  * @brief check if the list is empty
     268             :  *
     269             :  * @param list the doubly-linked list to operate on
     270             :  *
     271             :  * @return true if empty, false otherwise
     272             :  */
     273             : 
     274           1 : static inline bool sys_dlist_is_empty(sys_dlist_t *list)
     275             : {
     276             :         return list->head == list;
     277             : }
     278             : 
     279             : /**
     280             :  * @brief check if more than one node present
     281             :  *
     282             :  * This and other sys_dlist_*() functions are not thread safe.
     283             :  *
     284             :  * @param list the doubly-linked list to operate on
     285             :  *
     286             :  * @return true if multiple nodes, false otherwise
     287             :  */
     288             : 
     289           1 : static inline bool sys_dlist_has_multiple_nodes(sys_dlist_t *list)
     290             : {
     291             :         return list->head != list->tail;
     292             : }
     293             : 
     294             : /**
     295             :  * @brief get a reference to the head item in the list
     296             :  *
     297             :  * @param list the doubly-linked list to operate on
     298             :  *
     299             :  * @return a pointer to the head element, NULL if list is empty
     300             :  */
     301             : 
     302           1 : static inline sys_dnode_t *sys_dlist_peek_head(sys_dlist_t *list)
     303             : {
     304             :         return sys_dlist_is_empty(list) ? NULL : list->head;
     305             : }
     306             : 
     307             : /**
     308             :  * @brief get a reference to the head item in the list
     309             :  *
     310             :  * The list must be known to be non-empty.
     311             :  *
     312             :  * @param list the doubly-linked list to operate on
     313             :  *
     314             :  * @return a pointer to the head element
     315             :  */
     316             : 
     317           1 : static inline sys_dnode_t *sys_dlist_peek_head_not_empty(sys_dlist_t *list)
     318             : {
     319             :         return list->head;
     320             : }
     321             : 
     322             : /**
     323             :  * @brief get a reference to the next item in the list, node is not NULL
     324             :  *
     325             :  * Faster than sys_dlist_peek_next() if node is known not to be NULL.
     326             :  *
     327             :  * @param list the doubly-linked list to operate on
     328             :  * @param node the node from which to get the next element in the list
     329             :  *
     330             :  * @return a pointer to the next element from a node, NULL if node is the tail
     331             :  */
     332             : 
     333           1 : static inline sys_dnode_t *sys_dlist_peek_next_no_check(sys_dlist_t *list,
     334             :                                                         sys_dnode_t *node)
     335             : {
     336             :         return (node == list->tail) ? NULL : node->next;
     337             : }
     338             : 
     339             : /**
     340             :  * @brief get a reference to the next item in the list
     341             :  *
     342             :  * @param list the doubly-linked list to operate on
     343             :  * @param node the node from which to get the next element in the list
     344             :  *
     345             :  * @return a pointer to the next element from a node, NULL if node is the tail
     346             :  * or NULL (when node comes from reading the head of an empty list).
     347             :  */
     348             : 
     349           1 : static inline sys_dnode_t *sys_dlist_peek_next(sys_dlist_t *list,
     350             :                                                sys_dnode_t *node)
     351             : {
     352             :         return (node != NULL) ? sys_dlist_peek_next_no_check(list, node) : NULL;
     353             : }
     354             : 
     355             : /**
     356             :  * @brief get a reference to the previous item in the list, node is not NULL
     357             :  *
     358             :  * Faster than sys_dlist_peek_prev() if node is known not to be NULL.
     359             :  *
     360             :  * @param list the doubly-linked list to operate on
     361             :  * @param node the node from which to get the previous element in the list
     362             :  *
     363             :  * @return a pointer to the previous element from a node, NULL if node is the
     364             :  *         tail
     365             :  */
     366             : 
     367           1 : static inline sys_dnode_t *sys_dlist_peek_prev_no_check(sys_dlist_t *list,
     368             :                                                         sys_dnode_t *node)
     369             : {
     370             :         return (node == list->head) ? NULL : node->prev;
     371             : }
     372             : 
     373             : /**
     374             :  * @brief get a reference to the previous item in the list
     375             :  *
     376             :  * @param list the doubly-linked list to operate on
     377             :  * @param node the node from which to get the previous element in the list
     378             :  *
     379             :  * @return a pointer to the previous element from a node, NULL if node is the
     380             :  *         tail or NULL (when node comes from reading the head of an empty
     381             :  *         list).
     382             :  */
     383             : 
     384           1 : static inline sys_dnode_t *sys_dlist_peek_prev(sys_dlist_t *list,
     385             :                                                sys_dnode_t *node)
     386             : {
     387             :         return (node != NULL) ? sys_dlist_peek_prev_no_check(list, node) : NULL;
     388             : }
     389             : 
     390             : /**
     391             :  * @brief get a reference to the tail item in the list
     392             :  *
     393             :  * @param list the doubly-linked list to operate on
     394             :  *
     395             :  * @return a pointer to the tail element, NULL if list is empty
     396             :  */
     397             : 
     398           1 : static inline sys_dnode_t *sys_dlist_peek_tail(sys_dlist_t *list)
     399             : {
     400             :         return sys_dlist_is_empty(list) ? NULL : list->tail;
     401             : }
     402             : 
     403             : /**
     404             :  * @brief add node to tail of list
     405             :  *
     406             :  * This and other sys_dlist_*() functions are not thread safe.
     407             :  *
     408             :  * @param list the doubly-linked list to operate on
     409             :  * @param node the element to append
     410             :  */
     411             : 
     412           1 : static inline void sys_dlist_append(sys_dlist_t *list, sys_dnode_t *node)
     413             : {
     414             :         sys_dnode_t *const tail = list->tail;
     415             : 
     416             :         node->next = list;
     417             :         node->prev = tail;
     418             : 
     419             :         tail->next = node;
     420             :         list->tail = node;
     421             : }
     422             : 
     423             : /**
     424             :  * @brief add node to head of list
     425             :  *
     426             :  * This and other sys_dlist_*() functions are not thread safe.
     427             :  *
     428             :  * @param list the doubly-linked list to operate on
     429             :  * @param node the element to append
     430             :  */
     431             : 
     432           1 : static inline void sys_dlist_prepend(sys_dlist_t *list, sys_dnode_t *node)
     433             : {
     434             :         sys_dnode_t *const head = list->head;
     435             : 
     436             :         node->next = head;
     437             :         node->prev = list;
     438             : 
     439             :         head->prev = node;
     440             :         list->head = node;
     441             : }
     442             : 
     443             : /**
     444             :  * @brief Insert a node into a list
     445             :  *
     446             :  * Insert a node before a specified node in a dlist.
     447             :  *
     448             :  * @param successor the position before which "node" will be inserted
     449             :  * @param node the element to insert
     450             :  */
     451           1 : static inline void sys_dlist_insert(sys_dnode_t *successor, sys_dnode_t *node)
     452             : {
     453             :         sys_dnode_t *const prev = successor->prev;
     454             : 
     455             :         node->prev = prev;
     456             :         node->next = successor;
     457             :         prev->next = node;
     458             :         successor->prev = node;
     459             : }
     460             : 
     461             : /**
     462             :  * @brief insert node at position
     463             :  *
     464             :  * Insert a node in a location depending on a external condition. The cond()
     465             :  * function checks if the node is to be inserted _before_ the current node
     466             :  * against which it is checked.
     467             :  * This and other sys_dlist_*() functions are not thread safe.
     468             :  *
     469             :  * @param list the doubly-linked list to operate on
     470             :  * @param node the element to insert
     471             :  * @param cond a function that determines if the current node is the correct
     472             :  *             insert point
     473             :  * @param data parameter to cond()
     474             :  */
     475             : 
     476           1 : static inline void sys_dlist_insert_at(sys_dlist_t *list, sys_dnode_t *node,
     477             :         int (*cond)(sys_dnode_t *node, void *data), void *data)
     478             : {
     479             :         if (sys_dlist_is_empty(list)) {
     480             :                 sys_dlist_append(list, node);
     481             :         } else {
     482             :                 sys_dnode_t *pos = sys_dlist_peek_head(list);
     483             : 
     484             :                 while ((pos != NULL) && (cond(pos, data) == 0)) {
     485             :                         pos = sys_dlist_peek_next(list, pos);
     486             :                 }
     487             :                 if (pos != NULL) {
     488             :                         sys_dlist_insert(pos, node);
     489             :                 } else {
     490             :                         sys_dlist_append(list, node);
     491             :                 }
     492             :         }
     493             : }
     494             : 
     495             : /**
     496             :  * @brief remove a specific node from a list
     497             :  *
     498             :  * The list is implicit from the node. The node must be part of a list.
     499             :  * This and other sys_dlist_*() functions are not thread safe.
     500             :  *
     501             :  * @param node the node to remove
     502             :  */
     503             : 
     504           1 : static inline void sys_dlist_remove(sys_dnode_t *node)
     505             : {
     506             :         sys_dnode_t *const prev = node->prev;
     507             :         sys_dnode_t *const next = node->next;
     508             : 
     509             :         prev->next = next;
     510             :         next->prev = prev;
     511             :         sys_dnode_init(node);
     512             : }
     513             : 
     514             : /**
     515             :  * @brief get the first node in a list
     516             :  *
     517             :  * This and other sys_dlist_*() functions are not thread safe.
     518             :  *
     519             :  * @param list the doubly-linked list to operate on
     520             :  *
     521             :  * @return the first node in the list, NULL if list is empty
     522             :  */
     523             : 
     524           1 : static inline sys_dnode_t *sys_dlist_get(sys_dlist_t *list)
     525             : {
     526             :         sys_dnode_t *node = NULL;
     527             : 
     528             :         if (!sys_dlist_is_empty(list)) {
     529             :                 node = list->head;
     530             :                 sys_dlist_remove(node);
     531             :         }
     532             : 
     533             :         return node;
     534             : }
     535             : 
     536             : /**
     537             :  * @brief Compute the size of the given list in O(n) time
     538             :  *
     539             :  * @param list A pointer on the list
     540             :  *
     541             :  * @return an integer equal to the size of the list, or 0 if empty
     542             :  */
     543           1 : static inline size_t sys_dlist_len(sys_dlist_t *list)
     544             : {
     545             :         size_t len = 0;
     546             :         sys_dnode_t *node = NULL;
     547             : 
     548             :         SYS_DLIST_FOR_EACH_NODE(list, node) {
     549             :                 len++;
     550             :         }
     551             :         return len;
     552             : }
     553             : 
     554             : /** @} */
     555             : 
     556             : #ifdef __cplusplus
     557             : }
     558             : #endif
     559             : 
     560             : #endif /* ZEPHYR_INCLUDE_SYS_DLIST_H_ */

Generated by: LCOV version 1.14