1 /*
2  * Copyright (c) 2016 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  *
10  * @brief Single-linked list implementation
11  *
12  * Single-linked list implementation using inline macros/functions.
13  * This API is not thread safe, and thus if a list is used across threads,
14  * calls to functions must be protected with synchronization primitives.
15  */
16 
17 #ifndef _BLE_MESH_SLIST_H_
18 #define _BLE_MESH_SLIST_H_
19 
20 #include <stddef.h>
21 #include <stdbool.h>
22 #include "mesh_util.h"
23 
24 #ifdef __cplusplus
25 extern "C" {
26 #endif
27 
28 struct _snode {
29     struct _snode *next;
30 };
31 
32 typedef struct _snode sys_snode_t;
33 
34 struct _slist {
35     sys_snode_t *head;
36     sys_snode_t *tail;
37 };
38 
39 typedef struct _slist sys_slist_t;
40 
41 /**
42  * @brief Provide the primitive to iterate on a list
43  * Note: the loop is unsafe and thus __sn should not be removed
44  *
45  * User _MUST_ add the loop statement curly braces enclosing its own code:
46  *
47  *     SYS_SLIST_FOR_EACH_NODE(l, n) {
48  *         <user code>
49  *     }
50  *
51  * This and other SYS_SLIST_*() macros are not thread safe.
52  *
53  * @param __sl A pointer on a sys_slist_t to iterate on
54  * @param __sn A sys_snode_t pointer to peek each node of the list
55  */
56 #define SYS_SLIST_FOR_EACH_NODE(__sl, __sn)         \
57     for (__sn = sys_slist_peek_head(__sl); __sn;    \
58          __sn = sys_slist_peek_next(__sn))
59 
60 /**
61  * @brief Provide the primitive to iterate on a list, from a node in the list
62  * Note: the loop is unsafe and thus __sn should not be removed
63  *
64  * User _MUST_ add the loop statement curly braces enclosing its own code:
65  *
66  *     SYS_SLIST_ITERATE_FROM_NODE(l, n) {
67  *         <user code>
68  *     }
69  *
70  * Like SYS_SLIST_FOR_EACH_NODE(), but __dn already contains a node in the list
71  * where to start searching for the next entry from. If NULL, it starts from
72  * the head.
73  *
74  * This and other SYS_SLIST_*() macros are not thread safe.
75  *
76  * @param __sl A pointer on a sys_slist_t to iterate on
77  * @param __sn A sys_snode_t pointer to peek each node of the list
78  *             it contains the starting node, or NULL to start from the head
79  */
80 #define SYS_SLIST_ITERATE_FROM_NODE(__sl, __sn)             \
81     for (__sn = __sn ? sys_slist_peek_next_no_check(__sn)   \
82              : sys_slist_peek_head(__sl);                   \
83          __sn;                                              \
84          __sn = sys_slist_peek_next(__sn))
85 
86 /**
87  * @brief Provide the primitive to safely iterate on a list
88  * Note: __sn can be removed, it will not break the loop.
89  *
90  * User _MUST_ add the loop statement curly braces enclosing its own code:
91  *
92  *     SYS_SLIST_FOR_EACH_NODE_SAFE(l, n, s) {
93  *         <user code>
94  *     }
95  *
96  * This and other SYS_SLIST_*() macros are not thread safe.
97  *
98  * @param __sl A pointer on a sys_slist_t to iterate on
99  * @param __sn A sys_snode_t pointer to peek each node of the list
100  * @param __sns A sys_snode_t pointer for the loop to run safely
101  */
102 #define SYS_SLIST_FOR_EACH_NODE_SAFE(__sl, __sn, __sns) \
103     for (__sn = sys_slist_peek_head(__sl),              \
104              __sns = sys_slist_peek_next(__sn);         \
105          __sn; __sn = __sns,                            \
106              __sns = sys_slist_peek_next(__sn))
107 
108 /*
109  * @brief Provide the primitive to resolve the container of a list node
110  * Note: it is safe to use with NULL pointer nodes
111  *
112  * @param __ln A pointer on a sys_node_t to get its container
113  * @param __cn Container struct type pointer
114  * @param __n The field name of sys_node_t within the container struct
115  */
116 #define SYS_SLIST_CONTAINER(__ln, __cn, __n)    \
117         ((__ln) ? CONTAINER_OF((__ln), __typeof__(*(__cn)), __n) : NULL)
118 /*
119  * @brief Provide the primitive to peek container of the list head
120  *
121  * @param __sl A pointer on a sys_slist_t to peek
122  * @param __cn Container struct type pointer
123  * @param __n The field name of sys_node_t within the container struct
124  */
125 #define SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n)  \
126         SYS_SLIST_CONTAINER(sys_slist_peek_head(__sl), __cn, __n)
127 
128 /*
129  * @brief Provide the primitive to peek container of the list tail
130  *
131  * @param __sl A pointer on a sys_slist_t to peek
132  * @param __cn Container struct type pointer
133  * @param __n The field name of sys_node_t within the container struct
134  */
135 #define SYS_SLIST_PEEK_TAIL_CONTAINER(__sl, __cn, __n)  \
136         SYS_SLIST_CONTAINER(sys_slist_peek_tail(__sl), __cn, __n)
137 
138 /*
139  * @brief Provide the primitive to peek the next container
140  *
141  * @param __cn Container struct type pointer
142  * @param __n The field name of sys_node_t within the container struct
143  */
144 
145 #define SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n)                            \
146         ((__cn) ? SYS_SLIST_CONTAINER(sys_slist_peek_next(&((__cn)->__n)),  \
147                 __cn, __n) : NULL)
148 
149 /**
150  * @brief Provide the primitive to iterate on a list under a container
151  * Note: the loop is unsafe and thus __cn should not be detached
152  *
153  * User _MUST_ add the loop statement curly braces enclosing its own code:
154  *
155  *     SYS_SLIST_FOR_EACH_CONTAINER(l, c, n) {
156  *         <user code>
157  *     }
158  *
159  * @param __sl A pointer on a sys_slist_t to iterate on
160  * @param __cn A pointer to peek each entry of the list
161  * @param __n The field name of sys_node_t within the container struct
162  */
163 #define SYS_SLIST_FOR_EACH_CONTAINER(__sl, __cn, __n)                   \
164     for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n); __cn;   \
165          __cn = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n))
166 
167 /**
168  * @brief Provide the primitive to safely iterate on a list under a container
169  * Note: __cn can be detached, it will not break the loop.
170  *
171  * User _MUST_ add the loop statement curly braces enclosing its own code:
172  *
173  *     SYS_SLIST_FOR_EACH_NODE_SAFE(l, c, cn, n) {
174  *         <user code>
175  *     }
176  *
177  * @param __sl A pointer on a sys_slist_t to iterate on
178  * @param __cn A pointer to peek each entry of the list
179  * @param __cns A pointer for the loop to run safely
180  * @param __n The field name of sys_node_t within the container struct
181  */
182 #define SYS_SLIST_FOR_EACH_CONTAINER_SAFE(__sl, __cn, __cns, __n)   \
183     for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n),     \
184          __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n); __cn;    \
185          __cn = __cns, __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n))
186 
187 /**
188  * @brief Initialize a list
189  *
190  * @param list A pointer on the list to initialize
191  */
sys_slist_init(sys_slist_t * list)192 static inline void sys_slist_init(sys_slist_t *list)
193 {
194     list->head = NULL;
195     list->tail = NULL;
196 }
197 
198 #define SYS_SLIST_STATIC_INIT(ptr_to_list) {NULL, NULL}
199 
200 /**
201  * @brief Test if the given list is empty
202  *
203  * @param list A pointer on the list to test
204  *
205  * @return a boolean, true if it's empty, false otherwise
206  */
sys_slist_is_empty(sys_slist_t * list)207 static inline bool sys_slist_is_empty(sys_slist_t *list)
208 {
209     return (!list->head);
210 }
211 
212 /**
213  * @brief Peek the first node from the list
214  *
215  * @param list A point on the list to peek the first node from
216  *
217  * @return A pointer on the first node of the list (or NULL if none)
218  */
sys_slist_peek_head(sys_slist_t * list)219 static inline sys_snode_t *sys_slist_peek_head(sys_slist_t *list)
220 {
221     return list->head;
222 }
223 
224 /**
225  * @brief Peek the last node from the list
226  *
227  * @param list A point on the list to peek the last node from
228  *
229  * @return A pointer on the last node of the list (or NULL if none)
230  */
sys_slist_peek_tail(sys_slist_t * list)231 static inline sys_snode_t *sys_slist_peek_tail(sys_slist_t *list)
232 {
233     return list->tail;
234 }
235 
236 /**
237  * @brief Peek the next node from current node, node is not NULL
238  *
239  * Faster then sys_slist_peek_next() if node is known not to be NULL.
240  *
241  * @param node A pointer on the node where to peek the next node
242  *
243  * @return a pointer on the next node (or NULL if none)
244  */
sys_slist_peek_next_no_check(sys_snode_t * node)245 static inline sys_snode_t *sys_slist_peek_next_no_check(sys_snode_t *node)
246 {
247     return node->next;
248 }
249 
250 /**
251  * @brief Peek the next node from current node
252  *
253  * @param node A pointer on the node where to peek the next node
254  *
255  * @return a pointer on the next node (or NULL if none)
256  */
sys_slist_peek_next(sys_snode_t * node)257 static inline sys_snode_t *sys_slist_peek_next(sys_snode_t *node)
258 {
259     return node ? sys_slist_peek_next_no_check(node) : NULL;
260 }
261 
262 /**
263  * @brief Prepend a node to the given list
264  *
265  * This and other sys_slist_*() functions are not thread safe.
266  *
267  * @param list A pointer on the list to affect
268  * @param node A pointer on the node to prepend
269  */
sys_slist_prepend(sys_slist_t * list,sys_snode_t * node)270 static inline void sys_slist_prepend(sys_slist_t *list,
271                                      sys_snode_t *node)
272 {
273     node->next = list->head;
274     list->head = node;
275 
276     if (!list->tail) {
277         list->tail = list->head;
278     }
279 }
280 
281 /**
282  * @brief Append a node to the given list
283  *
284  * This and other sys_slist_*() functions are not thread safe.
285  *
286  * @param list A pointer on the list to affect
287  * @param node A pointer on the node to append
288  */
sys_slist_append(sys_slist_t * list,sys_snode_t * node)289 static inline void sys_slist_append(sys_slist_t *list,
290                                     sys_snode_t *node)
291 {
292     node->next = NULL;
293 
294     if (!list->tail) {
295         list->tail = node;
296         list->head = node;
297     } else {
298         list->tail->next = node;
299         list->tail = node;
300     }
301 }
302 
303 /**
304  * @brief Append a list to the given list
305  *
306  * Append a singly-linked, NULL-terminated list consisting of nodes containing
307  * the pointer to the next node as the first element of a node, to @a list.
308  * This and other sys_slist_*() functions are not thread safe.
309  *
310  * @param list A pointer on the list to affect
311  * @param head A pointer to the first element of the list to append
312  * @param tail A pointer to the last element of the list to append
313  */
sys_slist_append_list(sys_slist_t * list,void * head,void * tail)314 static inline void sys_slist_append_list(sys_slist_t *list,
315                                          void *head, void *tail)
316 {
317     if (!list->tail) {
318         list->head = (sys_snode_t *)head;
319         list->tail = (sys_snode_t *)tail;
320     } else {
321         list->tail->next = (sys_snode_t *)head;
322         list->tail = (sys_snode_t *)tail;
323     }
324 }
325 
326 /**
327  * @brief merge two slists, appending the second one to the first
328  *
329  * When the operation is completed, the appending list is empty.
330  * This and other sys_slist_*() functions are not thread safe.
331  *
332  * @param list A pointer on the list to affect
333  * @param list_to_append A pointer to the list to append.
334  */
sys_slist_merge_slist(sys_slist_t * list,sys_slist_t * list_to_append)335 static inline void sys_slist_merge_slist(sys_slist_t *list,
336                                          sys_slist_t *list_to_append)
337 {
338     sys_slist_append_list(list, list_to_append->head,
339                           list_to_append->tail);
340     sys_slist_init(list_to_append);
341 }
342 
343 /**
344  * @brief Insert a node to the given list
345  *
346  * This and other sys_slist_*() functions are not thread safe.
347  *
348  * @param list A pointer on the list to affect
349  * @param prev A pointer on the previous node
350  * @param node A pointer on the node to insert
351  */
sys_slist_insert(sys_slist_t * list,sys_snode_t * prev,sys_snode_t * node)352 static inline void sys_slist_insert(sys_slist_t *list,
353                                     sys_snode_t *prev,
354                                     sys_snode_t *node)
355 {
356     if (!prev) {
357         sys_slist_prepend(list, node);
358     } else if (!prev->next) {
359         sys_slist_append(list, node);
360     } else {
361         node->next = prev->next;
362         prev->next = node;
363     }
364 }
365 
366 /**
367  * @brief Fetch and remove the first node of the given list
368  *
369  * List must be known to be non-empty.
370  * This and other sys_slist_*() functions are not thread safe.
371  *
372  * @param list A pointer on the list to affect
373  *
374  * @return A pointer to the first node of the list
375  */
sys_slist_get_not_empty(sys_slist_t * list)376 static inline sys_snode_t *sys_slist_get_not_empty(sys_slist_t *list)
377 {
378     sys_snode_t *node = list->head;
379 
380     list->head = node->next;
381     if (list->tail == node) {
382         list->tail = list->head;
383     }
384 
385     return node;
386 }
387 
388 /**
389  * @brief Fetch and remove the first node of the given list
390  *
391  * This and other sys_slist_*() functions are not thread safe.
392  *
393  * @param list A pointer on the list to affect
394  *
395  * @return A pointer to the first node of the list (or NULL if empty)
396  */
sys_slist_get(sys_slist_t * list)397 static inline sys_snode_t *sys_slist_get(sys_slist_t *list)
398 {
399     return sys_slist_is_empty(list) ? NULL : sys_slist_get_not_empty(list);
400 }
401 
402 /**
403  * @brief Remove a node
404  *
405  * This and other sys_slist_*() functions are not thread safe.
406  *
407  * @param list A pointer on the list to affect
408  * @param prev_node A pointer on the previous node
409  *        (can be NULL, which means the node is the list's head)
410  * @param node A pointer on the node to remove
411  */
sys_slist_remove(sys_slist_t * list,sys_snode_t * prev_node,sys_snode_t * node)412 static inline void sys_slist_remove(sys_slist_t *list,
413                                     sys_snode_t *prev_node,
414                                     sys_snode_t *node)
415 {
416     if (!prev_node) {
417         list->head = node->next;
418 
419         /* Was node also the tail? */
420         if (list->tail == node) {
421             list->tail = list->head;
422         }
423     } else {
424         prev_node->next = node->next;
425 
426         /* Was node the tail? */
427         if (list->tail == node) {
428             list->tail = prev_node;
429         }
430     }
431 
432     node->next = NULL;
433 }
434 
435 /**
436  * @brief Find and remove a node from a list
437  *
438  * This and other sys_slist_*() functions are not thread safe.
439  *
440  * @param list A pointer on the list to affect
441  * @param node A pointer on the node to remove from the list
442  *
443  * @return true if node was removed
444  */
sys_slist_find_and_remove(sys_slist_t * list,sys_snode_t * node)445 static inline bool sys_slist_find_and_remove(sys_slist_t *list,
446                                              sys_snode_t *node)
447 {
448     sys_snode_t *prev = NULL;
449     sys_snode_t *test;
450 
451     SYS_SLIST_FOR_EACH_NODE(list, test) {
452         if (test == node) {
453             sys_slist_remove(list, prev, node);
454             return true;
455         }
456 
457         prev = test;
458     }
459 
460     return false;
461 }
462 
463 #ifdef __cplusplus
464 }
465 #endif
466 
467 #endif /* _BLE_MESH_SLIST_H_ */
468