/src/vlc/include/vlc_list.h
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * vlc_list.h |
3 | | ****************************************************************************** |
4 | | * Copyright © 2018 Rémi Denis-Courmont |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or modify it |
7 | | * under the terms of the GNU Lesser General Public License as published by |
8 | | * the Free Software Foundation; either version 2.1 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public License |
17 | | * along with this program; if not, write to the Free Software Foundation, |
18 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
19 | | *****************************************************************************/ |
20 | | |
21 | | #ifndef VLC_LIST_H |
22 | | # define VLC_LIST_H 1 |
23 | | |
24 | | # include <stdalign.h> |
25 | | # include <stdbool.h> |
26 | | |
27 | | /** |
28 | | * \defgroup list Linked lists |
29 | | * \ingroup cext |
30 | | * @{ |
31 | | * \file |
32 | | * This provides convenience helpers for linked lists. |
33 | | */ |
34 | | |
35 | | /** |
36 | | * Doubly-linked list node. |
37 | | * |
38 | | * This data structure provides a doubly-linked list node. |
39 | | * It must be embedded in each member of a list as a node proper. |
40 | | * It also serves as a list head in the object containing the list. |
41 | | */ |
42 | | struct vlc_list |
43 | | { |
44 | | struct vlc_list *prev; |
45 | | struct vlc_list *next; |
46 | | }; |
47 | | |
48 | | /** |
49 | | * Static initializer for a list head. |
50 | | */ |
51 | | #define VLC_LIST_INITIALIZER(h) { h, h } |
52 | | |
53 | | /** |
54 | | * Initializes an empty list head. |
55 | | */ |
56 | | static inline void vlc_list_init(struct vlc_list *restrict head) |
57 | 0 | { |
58 | 0 | head->prev = head; |
59 | 0 | head->next = head; |
60 | 0 | } |
61 | | |
62 | | /** |
63 | | * Inserts an element in a list. |
64 | | * |
65 | | * \param node [out] Node pointer of the element to insert. |
66 | | * \param prev Node pointer of the previous element. |
67 | | * \param next Node pointer of the next element. |
68 | | */ |
69 | | static inline void vlc_list_add_between(struct vlc_list *restrict node, |
70 | | struct vlc_list *prev, |
71 | | struct vlc_list *next) |
72 | 0 | { |
73 | 0 | node->prev = prev; |
74 | 0 | node->next = next; |
75 | 0 | prev->next = node; |
76 | 0 | next->prev = node; |
77 | 0 | } |
78 | | |
79 | | /** |
80 | | * Inserts an element after another. |
81 | | * |
82 | | * \param node [out] Node pointer of the element to insert |
83 | | * \param prev Node pointer of the previous element. |
84 | | */ |
85 | | static inline void vlc_list_add_after(struct vlc_list *restrict node, |
86 | | struct vlc_list *prev) |
87 | 0 | { |
88 | 0 | vlc_list_add_between(node, prev, prev->next); |
89 | 0 | } |
90 | | |
91 | | /** |
92 | | * Inserts an element before another. |
93 | | * |
94 | | * \param node [out] Node pointer of the element to insert. |
95 | | * \param next Node pointer of the next element. |
96 | | */ |
97 | | static inline void vlc_list_add_before(struct vlc_list *restrict node, |
98 | | struct vlc_list *next) |
99 | 0 | { |
100 | 0 | vlc_list_add_between(node, next->prev, next); |
101 | 0 | } |
102 | | |
103 | | /** |
104 | | * Appends an element into a list. |
105 | | * |
106 | | * \param node [out] Node pointer of the element to append to the list. |
107 | | * \param head Head pointer of the list to append the element to. |
108 | | */ |
109 | | static inline void vlc_list_append(struct vlc_list *restrict node, |
110 | | struct vlc_list *head) |
111 | 0 | { |
112 | 0 | vlc_list_add_before(node, head); |
113 | 0 | } |
114 | | |
115 | | /** |
116 | | * Prepends an element into a list. |
117 | | * |
118 | | * \param node [out] Node pointer of the element to prepend to the list. |
119 | | * \param head Head pointer of the list to prepend the element to. |
120 | | */ |
121 | | static inline void vlc_list_prepend(struct vlc_list *restrict node, |
122 | | struct vlc_list *head) |
123 | 0 | { |
124 | 0 | vlc_list_add_after(node, head); |
125 | 0 | } |
126 | | |
127 | | /** |
128 | | * Removes an element from a list. |
129 | | * |
130 | | * \param node Node of the element to remove from a list. |
131 | | * \warning The element must be inside a list. |
132 | | * Otherwise the behaviour is undefined. |
133 | | */ |
134 | | static inline void vlc_list_remove(struct vlc_list *restrict node) |
135 | 0 | { |
136 | 0 | struct vlc_list *prev = node->prev; |
137 | 0 | struct vlc_list *next = node->next; |
138 | 0 |
|
139 | 0 | prev->next = next; |
140 | 0 | next->prev = prev; |
141 | 0 | } |
142 | | |
143 | | /** |
144 | | * Replaces an element with another one. |
145 | | * |
146 | | * \param original [in] Node pointer of the element to remove from the list. |
147 | | * \param substitute [out] Node pointer of the replacement. |
148 | | */ |
149 | | static inline void vlc_list_replace(const struct vlc_list *original, |
150 | | struct vlc_list *restrict substitute) |
151 | 0 | { |
152 | 0 | vlc_list_add_between(substitute, original->prev, original->next); |
153 | 0 | } |
154 | | |
155 | | /** |
156 | | * Checks if a list is empty. |
157 | | * |
158 | | * \param head [in] Head of the list to be checked. |
159 | | * |
160 | | * \retval false The list is not empty. |
161 | | * \retval true The list is empty. |
162 | | * |
163 | | * \note Obviously the list must have been initialized. |
164 | | * Otherwise, the behaviour is undefined. |
165 | | */ |
166 | | static inline bool vlc_list_is_empty(const struct vlc_list *head) |
167 | 0 | { |
168 | 0 | return head->next == head; |
169 | 0 | } |
170 | | |
171 | | /** |
172 | | * Checks if an element is first in a list. |
173 | | * |
174 | | * \param node [in] List node of the element. |
175 | | * \param head [in] Head of the list to be checked. |
176 | | * |
177 | | * \retval false The element is not first (or is in another list). |
178 | | * \retval true The element is first. |
179 | | */ |
180 | | static inline bool vlc_list_is_first(const struct vlc_list *node, |
181 | | const struct vlc_list *head) |
182 | 0 | { |
183 | 0 | return node->prev == head; |
184 | 0 | } |
185 | | |
186 | | /** |
187 | | * Checks if an element is last in a list. |
188 | | * |
189 | | * \param node [in] List node of the element. |
190 | | * \param head [in] Head of the list to be checked. |
191 | | * |
192 | | * \retval false The element is not last (or is in another list). |
193 | | * \retval true The element is last. |
194 | | */ |
195 | | static inline bool vlc_list_is_last(const struct vlc_list *node, |
196 | | const struct vlc_list *head) |
197 | 0 | { |
198 | 0 | return node->next == head; |
199 | 0 | } |
200 | | |
201 | | /** |
202 | | * List iterator. |
203 | | */ |
204 | | struct vlc_list_it |
205 | | { |
206 | | const struct vlc_list *head; |
207 | | struct vlc_list *current; |
208 | | struct vlc_list *next; |
209 | | }; |
210 | | |
211 | | static inline |
212 | | struct vlc_list_it vlc_list_it_start(const struct vlc_list *head) |
213 | 0 | { |
214 | 0 | struct vlc_list *first = head->next; |
215 | 0 |
|
216 | 0 | struct vlc_list_it it = { head, first, first->next }; |
217 | 0 | return it; |
218 | 0 | } |
219 | | |
220 | | static inline |
221 | | struct vlc_list_it vlc_list_it_reverse_start(const struct vlc_list *head) |
222 | 0 | { |
223 | 0 | struct vlc_list *first = head->prev; |
224 | 0 |
|
225 | 0 | struct vlc_list_it it = { head, first, first->prev }; |
226 | 0 | return it; |
227 | 0 | } |
228 | | |
229 | | static inline bool vlc_list_it_continue(const struct vlc_list_it *restrict it) |
230 | 0 | { |
231 | 0 | return it->current != it->head; |
232 | 0 | } |
233 | | |
234 | | static inline void vlc_list_it_next(struct vlc_list_it *restrict it) |
235 | 0 | { |
236 | 0 | struct vlc_list *next = it->next; |
237 | 0 |
|
238 | 0 | it->current = next; |
239 | 0 | it->next = next->next; |
240 | 0 | } |
241 | | |
242 | | static inline void vlc_list_it_prev(struct vlc_list_it *restrict it) |
243 | 0 | { |
244 | 0 | struct vlc_list *next = it->next; |
245 | 0 |
|
246 | 0 | it->current = next; |
247 | 0 | it->next = next->prev; |
248 | 0 | } |
249 | | |
250 | | /** |
251 | | * List iteration macro. |
252 | | * |
253 | | * This macro iterates over all elements (excluding the head) of a list, |
254 | | * in order from the first to the last. |
255 | | * |
256 | | * For each iteration, it sets the cursor variable to the current element. |
257 | | * |
258 | | * \param pos Cursor pointer variable identifier. |
259 | | * \param head [in] Head pointer of the list to iterate. |
260 | | * \param member Identifier of the member of the data type |
261 | | * serving as list node. |
262 | | * \note It it safe to delete the current item while iterating. |
263 | | * It is however <b>not</b> safe to delete another item. |
264 | | */ |
265 | | #define vlc_list_foreach(pos, head, member) \ |
266 | | for (struct vlc_list_it vlc_list_it__##pos = vlc_list_it_start(head); \ |
267 | | vlc_list_it_continue(&(vlc_list_it__##pos)) \ |
268 | | && ((pos) = container_of((vlc_list_it__##pos).current, \ |
269 | | typeof (*(pos)), member), true); \ |
270 | | vlc_list_it_next(&(vlc_list_it__##pos))) |
271 | | |
272 | | /** |
273 | | * List iteration macro. |
274 | | * |
275 | | * This macro iterates over all elements (excluding the head) of a list, |
276 | | * in reversed order from the first to the last. |
277 | | * |
278 | | * For each iteration, it sets the cursor variable to the current element. |
279 | | * |
280 | | * \param pos Cursor pointer variable identifier. |
281 | | * \param head [in] Head pointer of the list to iterate. |
282 | | * \param member Identifier of the member of the data type |
283 | | * serving as list node. |
284 | | * \note It it safe to delete the current item while iterating. |
285 | | * It is however <b>not</b> safe to delete another item. |
286 | | */ |
287 | | #define vlc_list_reverse_foreach(pos, head, member) \ |
288 | | for (struct vlc_list_it vlc_list_it_##pos = vlc_list_it_reverse_start(head); \ |
289 | | vlc_list_it_continue(&(vlc_list_it_##pos)) \ |
290 | | && ((pos) = container_of((vlc_list_it_##pos).current, \ |
291 | | typeof (*(pos)), member), true); \ |
292 | | vlc_list_it_prev(&(vlc_list_it_##pos))) |
293 | | |
294 | | /** |
295 | | * Converts a list node pointer to an element pointer. |
296 | | * |
297 | | * \param ptr list node pointer |
298 | | * \param type list data element type name |
299 | | * \param member list node member within the data element compound type |
300 | | */ |
301 | | #define vlc_list_entry(ptr, type, member) container_of(ptr, type, member) |
302 | | |
303 | | static inline void *vlc_list_first_or_null(const struct vlc_list *head, |
304 | | size_t offset) |
305 | 0 | { |
306 | 0 | if (vlc_list_is_empty(head)) |
307 | 0 | return NULL; |
308 | 0 | return ((char *)(head->next)) - offset; |
309 | 0 | } |
310 | | |
311 | | static inline void *vlc_list_last_or_null(const struct vlc_list *head, |
312 | | size_t offset) |
313 | 0 | { |
314 | 0 | if (vlc_list_is_empty(head)) |
315 | 0 | return NULL; |
316 | 0 | return ((char *)(head->prev)) - offset; |
317 | 0 | } |
318 | | |
319 | | static inline void *vlc_list_prev_or_null(const struct vlc_list *head, |
320 | | const struct vlc_list *node, |
321 | | size_t offset) |
322 | 0 | { |
323 | 0 | if (vlc_list_is_first(node, head)) |
324 | 0 | return NULL; |
325 | 0 | return ((char *)(node->prev)) - offset; |
326 | 0 | } |
327 | | |
328 | | static inline void *vlc_list_next_or_null(const struct vlc_list *head, |
329 | | const struct vlc_list *node, |
330 | | size_t offset) |
331 | 0 | { |
332 | 0 | if (vlc_list_is_last(node, head)) |
333 | 0 | return NULL; |
334 | 0 | return ((char *)(node->next)) - offset; |
335 | 0 | } |
336 | | |
337 | | /** |
338 | | * Gets the first element. |
339 | | * |
340 | | * \param head [in] Head of list whose last element to get. |
341 | | * |
342 | | * \return the first entry in a list or NULL if empty. |
343 | | */ |
344 | | #define vlc_list_first_entry_or_null(head, type, member) \ |
345 | | ((type *)vlc_list_first_or_null(head, offsetof (type, member))) |
346 | | |
347 | | /** |
348 | | * Gets the last element. |
349 | | * |
350 | | * \param head [in] Head of list whose last element to get. |
351 | | * |
352 | | * \return the last entry in a list or NULL if empty. |
353 | | */ |
354 | | #define vlc_list_last_entry_or_null(head, type, member) \ |
355 | | ((type *)vlc_list_last_or_null(head, offsetof (type, member))) |
356 | | |
357 | | #define vlc_list_prev_entry_or_null(head, entry, type, member) \ |
358 | | ((type *)vlc_list_prev_or_null(head, &(entry)->member, \ |
359 | | offsetof (type, member))) |
360 | | #define vlc_list_next_entry_or_null(head, entry, type, member) \ |
361 | | ((type *)vlc_list_next_or_null(head, &(entry)->member, \ |
362 | | offsetof (type, member))) |
363 | | |
364 | | /** \todo Merging lists, splitting lists. */ |
365 | | |
366 | | /** @} */ |
367 | | |
368 | | #endif /* VLC_LIST_H */ |