1 /*
2 * SPDX-FileCopyrightText: 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