The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
dlist.h
Go to the documentation of this file.
1#pragma once
2/*
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
16 */
17
18/** Doubly linked list implementation
19 *
20 * @file src/lib/util/dlist.h
21 *
22 * @copyright 2016 Alan DeKok (aland@freeradius.org)
23 */
24RCSIDH(dlist_h, "$Id: 2615d2ef3f77fe17b0ef976e1892c859e962afe8 $")
25
26#ifdef __cplusplus
27extern "C" {
28#endif
29
30#include <freeradius-devel/build.h>
31#include <freeradius-devel/missing.h>
32#include <freeradius-devel/util/debug.h>
33#include <freeradius-devel/util/misc.h>
34#include <freeradius-devel/util/talloc.h>
35
36typedef struct fr_dlist_s fr_dlist_t;
37
38/** Entry in a doubly linked list
39 *
40 */
45
46/** Head of a doubly linked list
47 *
48 * Holds additional information about the list items,
49 * like at which offset the next/prev pointers can be found.
50 */
51typedef struct {
52 fr_dlist_t entry; //!< Struct holding the head and tail of the list.
53 ///< First for efficient traversal.
54
55 unsigned int offset; //!< Positive offset from start of structure to #fr_dlist_t.
56 ///< Yes, this should really be size_t, but we shave 8 bytes
57 ///< off each head structure by making it unsigned int.
58
59 unsigned int num_elements; //!< Number of elements contained within the dlist.
60
61 char const *type; //!< of items contained within the list. Used for talloc
62 ///< validation.
64
65static_assert(sizeof(unsigned int) >= 4, "Unsigned integer too small on this platform");
66
67/** Static initialiser for entries
68 *
69 @code {.c}
70 fr_dlist_entry_t my_entry = FR_DLIST_INITIALISER(my_entry)
71 @endcode
72 *
73 * @param[in] _entry what's being initialised.
74 */
75#define FR_DLIST_ENTRY_INITIALISER(_entry) { .next = &_entry, .prev = &_entry }
76
77/** Static initialiser for list heads
78 *
79 @code {.c}
80 fr_dlist_head_t my_head = FR_DLIST_INITIALISER(my_head)
81 @endcode
82 *
83 * @param[in] _head what's being initialised.
84 */
85#define FR_DLIST_HEAD_INITIALISER(_head) { .entry = FR_DLIST_ENTRY_INITIALISER((_head).entry) }
86
87/** Iterate over the contents of a list
88 *
89 * The macro is "safe", in that the current iterator variable can be deleted.
90 *
91 * The iterators can be nested, so long as the _iter variable names are different.
92 *
93 * @param[in] _list_head to iterate over.
94 * @param[in] _type of item the list contains.
95 * @param[in] _iter Name of iteration variable.
96 * Will be declared in the scope of the loop.
97 */
98#define fr_dlist_foreach(_list_head, _type, _iter) \
99 for (_type *JOIN(_next,_iter), *_iter = fr_dlist_head(_list_head); JOIN(_next,_iter) = fr_dlist_next(_list_head, _iter), _iter != NULL; _iter = JOIN(_next,_iter))
100
101/** Find the dlist pointers within a list item
102 *
103 */
104static inline fr_dlist_t *fr_dlist_item_to_entry(size_t offset, void const *item)
105{
106 return (fr_dlist_t *)(((uintptr_t) item) + offset);
107}
108
109/** Get the item from a fr_dlist_t
110 *
111 */
112static inline void *fr_dlist_entry_to_item(size_t offset, fr_dlist_t const *entry)
113{
114 return (void *)(((uintptr_t) entry) - offset);
115}
116
117/** Initialise a linked list without metadata
118 *
119 */
120static inline void fr_dlist_entry_init(fr_dlist_t *entry)
121{
122 entry->prev = entry->next = entry;
123}
124
125/** Remove an item from the dlist when we don't have access to the head
126 *
127 */
128static inline CC_HINT(nonnull) void fr_dlist_entry_unlink(fr_dlist_t *entry)
129{
130 entry->prev->next = entry->next;
131 entry->next->prev = entry->prev;
132 entry->prev = entry->next = entry;
133}
134
135/** Check if a list entry is part of a list
136 *
137 * This works because the fr_dlist_head_t has an entry in the list.
138 * So if next and prev both point to the entry for the object being
139 * passed in, then it can't be part of a list with a fr_dlist_head_t.
140 *
141 * @return
142 * - True if in a list.
143 * - False otherwise.
144 */
145static inline CC_HINT(nonnull) bool fr_dlist_entry_in_list(fr_dlist_t const *entry)
146{
147 if (((entry->prev == entry) && (entry->next == entry)) ||
148 ((entry->prev == NULL) && (entry->next == NULL))) return false;
149
150 return true;
151}
152
153/** Link in a single entry after the current entry
154 *
155 * @param[in] entry to link in entry after.
156 * @param[in] to_link entry to link in after.
157 */
158static inline CC_HINT(nonnull) void fr_dlist_entry_link_after(fr_dlist_t *entry, fr_dlist_t *to_link)
159{
160 to_link->prev = entry;
161 to_link->next = entry->next;
162 entry->next->prev = to_link;
163 entry->next = to_link;
164}
165
166/** Link in a single entry before the current entry
167 *
168 * @param[in] entry to link in entry before.
169 * @param[in] to_link entry to link in before.
170 */
171static inline CC_HINT(nonnull) void fr_dlist_entry_link_before(fr_dlist_t *entry, fr_dlist_t *to_link)
172{
173 to_link->next = entry;
174 to_link->prev = entry->prev;
175 entry->prev->next = to_link;
176 entry->prev = to_link;
177}
178
179/** Link in a sublist before the current entry
180 *
181 * @note This is not suitable for moving elements from a fr_dlist_head_t as
182 * we need to snip out the head element. This is only suitable if the
183 * two entries are not part of lists with head elements.
184 *
185 * @param[in] dst to link src in before.
186 * @param[in] src to link in before dst.
187 */
188static inline CC_HINT(nonnull) void fr_dlist_entry_move(fr_dlist_t *dst, fr_dlist_t *src)
189{
190 fr_dlist_t *src_end = src->prev;
191
192 dst->prev->next = src;
193 src->prev->next = dst;
194 src->prev = dst->prev;
195 dst->prev = src_end;
196}
197
198/** Replace one entry with another
199 *
200 * @param[in] entry to replace.
201 * @param[in] replacement to link in.
202 */
203static inline CC_HINT(nonnull) void fr_dlist_entry_replace(fr_dlist_t *entry, fr_dlist_t *replacement)
204{
205 /* Link replacement item into list */
206 entry->prev->next = replacement;
207 replacement->prev = entry->prev;
208 entry->next->prev = replacement;
209 replacement->next = entry->next;
210
211 /* Reset links on replaced item */
212 fr_dlist_entry_init(entry);
213}
214
215/** Initialise the head structure of a doubly linked list
216 *
217 * @note This variant does not perform talloc validation.
218 *
219 @code{.c}
220 typedef struct {
221 fr_dlist_t dlist;
222 char const *field_a;
223 int *field_b;
224 ...
225 } my_struct_t;
226
227 int my_func(my_struct_t *a, my_struct_t *b)
228 {
229 fr_dlist_head_t head;
230
231 fr_dlist_init(&head, my_struct_t, dlist);
232 fr_dlist_insert_head(&head, a);
233 fr_dlist_insert_head(&head, b);
234 }
235 @endcode
236 *
237 * @param[in] _head structure to initialise.
238 * @param[in] _type of item being stored in the list, e.g. fr_value_box_t,
239 * fr_dict_attr_t etc...
240 * @param[in] _field Containing the #fr_dlist_t within item being stored.
241 */
242#define fr_dlist_init(_head, _type, _field) \
243 _Generic((((_type *)0)->_field), fr_dlist_t: _fr_dlist_init(_head, offsetof(_type, _field), NULL))
244
245/** Initialise the head structure of a doubly linked list
246 *
247 * @note This variant *DOES* perform talloc validation. All items inserted
248 * into the list must be allocated with talloc.
249 *
250 * @copybrief fr_dlist_init
251 *
252 * @param[in] _head structure to initialise.
253 * @param[in] _type of item being stored in the list, e.g. fr_value_box_t,
254 * fr_dict_attr_t etc...
255 * @param[in] _field Containing the #fr_dlist_t within item being stored.
256 */
257#define fr_dlist_talloc_init(_head, _type, _field) \
258 _Generic((((_type *)0)->_field), fr_dlist_t: _fr_dlist_init(_head, offsetof(_type, _field), #_type))
259
260/** Initialise common fields in a dlist
261 *
262 */
263static inline void _fr_dlist_init(fr_dlist_head_t *list_head, size_t offset, char const *type)
264{
265 fr_assert(offset < (1 << 20)); /* list_head->offset is `unsigned int`, and 1G structs are stupid. */
266
267 fr_dlist_entry_init(&list_head->entry);
268 list_head->offset = offset;
269 list_head->type = type;
270 list_head->num_elements = 0;
271}
272
273/** Efficiently remove all elements in a dlist
274 *
275 * @param[in] list_head to clear.
276 */
277static inline void fr_dlist_clear(fr_dlist_head_t *list_head)
278{
279 fr_dlist_entry_init(&list_head->entry);
280 list_head->num_elements = 0;
281}
282
283/** Verify we're not going to overflow the element count
284 *
285 */
286#define CHECK_ELEMENT_COUNT(_head, _add) \
287 if (unlikely((_head)->num_elements > (UINT_MAX - (_add)))) do { \
288 fr_strerror_const("Maximum elements in list"); \
289 return -1; \
290 } while (0)
291
292/** Check if a list entry is part of a list
293 *
294 * This works because the fr_dlist_head_t has an entry in the list.
295 * So if next and prev both point to the entry for the object being
296 * passed in, then it can't be part of a list with a fr_dlist_head_t.
297 *
298 * @return
299 * - True if in a list.
300 * - False otherwise.
301 */
302static inline CC_HINT(nonnull) bool fr_dlist_in_list(fr_dlist_head_t *list_head, void *ptr)
303{
304 fr_dlist_t *entry = fr_dlist_item_to_entry(list_head->offset, ptr);
305
306 return fr_dlist_entry_in_list(entry);
307}
308
309/** Insert an item into the head of a list
310 *
311 * @note If #fr_dlist_talloc_init was used to initialise #fr_dlist_head_t
312 * ptr must be a talloced chunk of the type passed to #fr_dlist_talloc_init.
313 *
314 * @param[in] list_head to insert ptr into.
315 * @param[in] ptr to insert.
316 * @return
317 * - 0 on success.
318 * - -1 on failure.
319 */
320static inline CC_HINT(nonnull) int fr_dlist_insert_head(fr_dlist_head_t *list_head, void *ptr)
321{
322 fr_dlist_t *entry;
324
325 CHECK_ELEMENT_COUNT(list_head, 1);
326
327#ifndef TALLOC_GET_TYPE_ABORT_NOOP
328 if (list_head->type) ptr = _talloc_get_type_abort(ptr, list_head->type, __location__);
329#endif
330
331 entry = fr_dlist_item_to_entry(list_head->offset, ptr);
332 head = &(list_head->entry);
333
334 if (!fr_cond_assert(head->next != NULL)) return -1;
335 if (!fr_cond_assert(head->prev != NULL)) return -1;
336
337 entry->prev = head;
338 entry->next = head->next;
339 head->next->prev = entry;
340 head->next = entry;
341
342 list_head->num_elements++;
343
344 return 0;
345}
346
347/** Insert an item into the tail of a list
348 *
349 * @note If #fr_dlist_talloc_init was used to initialise #fr_dlist_head_t
350 * ptr must be a talloced chunk of the type passed to #fr_dlist_talloc_init.
351 *
352 * @param[in] list_head to insert ptr into.
353 * @param[in] ptr to insert.
354 * @return
355 * - 0 on success.
356 * - -1 on failure.
357 *
358 * @hidecallergraph
359 */
360static inline CC_HINT(nonnull) int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
361{
362 fr_dlist_t *entry;
364
365 CHECK_ELEMENT_COUNT(list_head, 1);
366
367#ifndef TALLOC_GET_TYPE_ABORT_NOOP
368 if (list_head->type) ptr = _talloc_get_type_abort(ptr, list_head->type, __location__);
369#endif
370
371 entry = fr_dlist_item_to_entry(list_head->offset, ptr);
372 head = &(list_head->entry);
373
374 if (!fr_cond_assert(head->next != NULL)) return -1;
375 if (!fr_cond_assert(head->prev != NULL)) return -1;
376
377 entry->next = head;
378 entry->prev = head->prev;
379 head->prev->next = entry;
380 head->prev = entry;
381
382 list_head->num_elements++;
383
384 return 0;
385}
386
387/** Insert an item after an item already in the list
388 *
389 * @note If #fr_dlist_talloc_init was used to initialise #fr_dlist_head_t
390 * ptr must be a talloced chunk of the type passed to #fr_dlist_talloc_init.
391 *
392 * @param[in] list_head to insert ptr into.
393 * @param[in] pos to insert ptr after.
394 * @param[in] ptr to insert.
395 */
396static inline CC_HINT(nonnull(1,3)) int fr_dlist_insert_after(fr_dlist_head_t *list_head, void *pos, void *ptr)
397{
398 fr_dlist_t *entry, *pos_entry;
399
400 CHECK_ELEMENT_COUNT(list_head, 1);
401
402#ifndef TALLOC_GET_TYPE_ABORT_NOOP
403 if (list_head->type) ptr = _talloc_get_type_abort(ptr, list_head->type, __location__);
404#endif
405
406 entry = fr_dlist_item_to_entry(list_head->offset, ptr);
407 if (!pos) {
408 pos_entry = &(list_head->entry);
409 } else {
410 pos_entry = fr_dlist_item_to_entry(list_head->offset, pos);
411 }
412
413 if (!fr_cond_assert(pos_entry->next != NULL)) return -1;
414 if (!fr_cond_assert(pos_entry->prev != NULL)) return -1;
415
416 fr_dlist_entry_link_after(pos_entry, entry);
417
418 list_head->num_elements++;
419
420 return 0;
421}
422
423/** Insert an item before an item already in the list
424 *
425 * @note If #fr_dlist_talloc_init was used to initialise #fr_dlist_head_t
426 * ptr must be a talloced chunk of the type passed to #fr_dlist_talloc_init.
427 *
428 * @param[in] list_head to insert ptr into.
429 * @param[in] pos to insert ptr before.
430 * @param[in] ptr to insert.
431 */
432static inline CC_HINT(nonnull(1,3)) int fr_dlist_insert_before(fr_dlist_head_t *list_head, void *pos, void *ptr)
433{
434 fr_dlist_t *entry, *pos_entry;
435
436 CHECK_ELEMENT_COUNT(list_head, 1);
437
438#ifndef TALLOC_GET_TYPE_ABORT_NOOP
439 if (list_head->type) ptr = _talloc_get_type_abort(ptr, list_head->type, __location__);
440#endif
441
442 entry = fr_dlist_item_to_entry(list_head->offset, ptr);
443 if (!pos) {
444 pos_entry = &(list_head->entry);
445 } else {
446 pos_entry = fr_dlist_item_to_entry(list_head->offset, pos);
447 }
448
449 if (!fr_cond_assert(pos_entry->next != NULL)) return -1;
450 if (!fr_cond_assert(pos_entry->prev != NULL)) return -1;
451
452 fr_dlist_entry_link_before(pos_entry, entry);
453
454 list_head->num_elements++;
455
456 return 0;
457}
458
459/** Return the HEAD item of a list or NULL if the list is empty
460 *
461 * @param[in] list_head to return the HEAD item from.
462 * @return
463 * - The HEAD item.
464 * - NULL if no items exist in the list.
465 *
466 * @hidecallergraph
467 */
468static inline CC_HINT(nonnull) void *fr_dlist_head(fr_dlist_head_t const *list_head)
469{
470 fr_dlist_t const *head = &(list_head->entry);
471
472 if (head->next == head) return NULL;
473
474 return fr_dlist_entry_to_item(list_head->offset, head->next);
475}
476
477/** Check whether a list has any items.
478 *
479 * @return
480 * - True if it does not.
481 * - False if it does.
482 */
483static inline CC_HINT(nonnull) bool fr_dlist_empty(fr_dlist_head_t const *list_head)
484{
485 fr_dlist_t const *head = &(list_head->entry);
486 return (head->prev == head);
487}
488
489/** Check if the list head is initialised
490 *
491 * Memory must be zeroed out or initialised.
492 *
493 * @return
494 * - True if dlist initialised.
495 * - False if dlist not initialised
496 */
497static inline CC_HINT(nonnull) bool fr_dlist_initialised(fr_dlist_head_t const *list_head)
498{
499 fr_dlist_t const *head = &(list_head->entry);
500
501 if (!head->prev && !head->next) return false;
502
503 return true;
504}
505
506/** Return the TAIL item of a list or NULL if the list is empty
507 *
508 * @param[in] list_head to return the TAIL item from.
509 * @return
510 * - The TAIL item.
511 * - NULL if no items exist in the list.
512 */
513static inline CC_HINT(nonnull) void *fr_dlist_tail(fr_dlist_head_t const *list_head)
514{
515 fr_dlist_t const *head = &(list_head->entry);
516
517 if (head->prev == head) return NULL;
518
519 return fr_dlist_entry_to_item(list_head->offset, head->prev);
520
521}
522
523/** Get the next item in a list
524 *
525 * @note If #fr_dlist_talloc_init was used to initialise #fr_dlist_head_t
526 * ptr must be a talloced chunk of the type passed to #fr_dlist_talloc_init.
527 *
528 * @param[in] list_head containing ptr.
529 * @param[in] ptr to retrieve the next item from.
530 * If ptr is NULL, the HEAD of the list will be returned.
531 * @return
532 * - The next item in the list if ptr is not NULL.
533 * - The head of the list if ptr is NULL.
534 * - NULL if ptr is the tail of the list (no more items).
535 * @hidecallergraph
536 */
537static inline CC_HINT(nonnull(1)) void *fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
538{
539 fr_dlist_t const *entry;
540 fr_dlist_t const *head;
541
542 if (!ptr) return fr_dlist_head(list_head);
543
544#ifndef TALLOC_GET_TYPE_ABORT_NOOP
545 if (list_head->type) ptr = _talloc_get_type_abort(ptr, list_head->type, __location__);
546#endif
547 entry = fr_dlist_item_to_entry(list_head->offset, ptr);
548 head = &(list_head->entry);
549
550 if (entry->next == head) return NULL;
551 if (!entry->next) return NULL;
552 entry = entry->next;
553
554 return fr_dlist_entry_to_item(list_head->offset, entry);
555}
556
557/** Get the previous item in a list
558 *
559 * @note If #fr_dlist_talloc_init was used to initialise #fr_dlist_head_t
560 * ptr must be a talloced chunk of the type passed to #fr_dlist_talloc_init.
561 *
562 * @param[in] list_head containing ptr.
563 * @param[in] ptr to retrieve the previous item to.
564 * If ptr is NULL, the TAIL of the list will be returned.
565 * @return
566 * - The previous item in the list if ptr is not NULL.
567 * - The tail of the list if ptr is NULL.
568 * - NULL if ptr is the head of the list (no more items).
569 */
570static inline CC_HINT(nonnull(1)) void *fr_dlist_prev(fr_dlist_head_t const *list_head, void const *ptr)
571{
572 fr_dlist_t const *entry;
573 fr_dlist_t const *head;
574
575 if (!ptr) return fr_dlist_tail(list_head);
576
577#ifndef TALLOC_GET_TYPE_ABORT_NOOP
578 if (list_head->type) ptr = _talloc_get_type_abort(ptr, list_head->type, __location__);
579#endif
580
581 entry = fr_dlist_item_to_entry(list_head->offset, ptr);
582 head = &(list_head->entry);
583
584 if (entry->prev == head) return NULL;
585 entry = entry->prev;
586
587 return fr_dlist_entry_to_item(list_head->offset, entry);
588}
589
590/** Remove an item from the list
591 *
592 * @note If #fr_dlist_talloc_init was used to initialise #fr_dlist_head_t
593 * ptr must be a talloced chunk of the type passed to #fr_dlist_talloc_init.
594 *
595 * When removing items in an iteration loop, the iterator variable must be
596 * assigned the item returned by this function.
597 *
598 * If the iterator variable is not updated, the item removed will be the last item
599 * iterated over, as its next/prev pointers are set to point to itself.
600 @code{.c}
601 my_item_t *item = NULL;
602
603 while ((item = fr_dlist_next(&head, item))) {
604 if (item->should_be_removed) {
605 ...do things with item
606 item = fr_dlist_remove(&head, item);
607 continue;
608 }
609 }
610 @endcode
611 *
612 * @param[in] list_head to remove ptr from.
613 * @param[in] ptr to remove.
614 * @return
615 * - The previous item in the list (makes iteration easier).
616 * - NULL if we just removed the head of the list.
617 *
618 * @hidecallergraph
619 */
620static inline CC_HINT(nonnull(1)) void *fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
621{
622 fr_dlist_t *entry;
624 fr_dlist_t *prev;
625
626 if (!ptr || fr_dlist_empty(list_head)) return NULL;
627
628#ifndef TALLOC_GET_TYPE_ABORT_NOOP
629 if (list_head->type) (void)_talloc_get_type_abort(ptr, list_head->type, __location__);
630#endif
631
632 entry = fr_dlist_item_to_entry(list_head->offset, ptr);
633 if (!fr_dlist_entry_in_list(entry)) return NULL;
634
635 head = &(list_head->entry);
636 entry->prev->next = entry->next;
637 entry->next->prev = prev = entry->prev;
638 entry->prev = entry->next = entry;
639
640 list_head->num_elements--;
641
642 if (prev == head) return NULL; /* Works with fr_dlist_next so that the next item is the list HEAD */
643
644 return fr_dlist_entry_to_item(list_head->offset, prev);
645}
646
647/** Remove the head item in a list
648 *
649 * @param[in] list_head to remove head item from.
650 * @return
651 * - The item removed.
652 * - NULL if not items in dlist.
653 */
654static inline CC_HINT(nonnull(1)) void *fr_dlist_pop_head(fr_dlist_head_t *list_head)
655{
656 void *item = fr_dlist_head(list_head);
657
658 (void)fr_dlist_remove(list_head, item);
659
660 return item; /* fr_dlist_remove returns the previous item */
661}
662
663/** Remove the tail item in a list
664 *
665 * @param[in] list_head to remove tail item from.
666 * @return
667 * - The item removed.
668 * - NULL if not items in dlist.
669 */
670static inline CC_HINT(nonnull(1)) void *fr_dlist_pop_tail(fr_dlist_head_t *list_head)
671{
672 void *item = fr_dlist_tail(list_head);
673
674 (void)fr_dlist_remove(list_head, item);
675
676 return item; /* fr_dlist_remove returns the previous item */
677}
678
679/** Replace an item in a dlist
680 *
681 * @param list_head in which the original item is.
682 * @param item to replace.
683 * @param ptr replacement item.
684 * @return
685 * - The item replaced
686 * - NULL if nothing replaced
687 */
688static inline void *fr_dlist_replace(fr_dlist_head_t *list_head, void *item, void *ptr)
689{
690 fr_dlist_t *item_entry;
691 fr_dlist_t *ptr_entry;
692
693 if (!item || !ptr) return NULL;
694
695#ifndef TALLOC_GET_TYPE_ABORT_NOOP
696 if (list_head->type) (void)_talloc_get_type_abort(ptr, list_head->type, __location__);
697#endif
698
699 item_entry = fr_dlist_item_to_entry(list_head->offset, item);
700 if (!fr_dlist_entry_in_list(item_entry)) return NULL;
701
702 ptr_entry = fr_dlist_item_to_entry(list_head->offset, ptr);
703
704 fr_dlist_entry_replace(item_entry, ptr_entry);
705
706 return item;
707}
708
709/** Check all items in the list are valid
710 *
711 * Checks item talloc headers and types to ensure they're consistent
712 * with what we expect.
713 *
714 * Does nothing if the list was not initialised with #fr_dlist_talloc_init.
715 */
716#ifndef TALLOC_GET_TYPE_ABORT_NOOP
717static inline CC_HINT(nonnull) void _fr_dlist_verify(char const *file, int line, fr_dlist_head_t const *list_head)
718{
719 void *item;
720
721 if (!list_head->type) return;
722
723 fr_assert_msg(fr_dlist_initialised(list_head), "CONSISTENCY CHECK FAILED %s[%i]: dlist not initialised", file, line);
724
725 for (item = fr_dlist_head(list_head);
726 item;
727 item = fr_dlist_next(list_head, item)) {
728 item = _talloc_get_type_abort(item, list_head->type, __location__);
729 }
730}
731#else
732static inline void _fr_dlist_verify(UNUSED char const *file, UNUSED int line, fr_dlist_head_t const *list_head)
733{
734 if (!fr_cond_assert(list_head != NULL)) return;
735}
736#endif
737#define fr_dlist_verify(_head) _fr_dlist_verify(__FILE__, __LINE__, _head)
738
739/** Merge two lists, inserting the source at the tail of the destination
740 *
741 * @return
742 * - 0 on success.
743 * - -1 on failure.
744 */
745static inline CC_HINT(nonnull) int fr_dlist_move(fr_dlist_head_t *list_dst, fr_dlist_head_t *list_src)
746{
747 fr_dlist_t *dst = &(list_dst->entry);
748 fr_dlist_t *src = &(list_src->entry);
749
750 CHECK_ELEMENT_COUNT(list_dst, list_src->num_elements);
751
752#ifdef WITH_VERIFY_PTR
753 /*
754 * Must be both talloced or both not
755 */
756 if (!fr_cond_assert((list_dst->type && list_src->type) || (!list_dst->type && !list_src->type))) return -1;
757
758 /*
759 * Must be of the same type
760 */
761 if (!fr_cond_assert(!list_dst->type || (strcmp(list_dst->type, list_src->type) == 0))) return -1;
762#endif
763
764 if (!fr_cond_assert(dst->next != NULL)) return -1;
765 if (!fr_cond_assert(dst->prev != NULL)) return -1;
766
767 if (fr_dlist_empty(list_src)) return 0;
768
769 /*
770 * This is different to fr_dlist_entry_move
771 * because we need need to snip out the
772 * list head entry from the src.
773 */
774 src->prev->next = dst;
775 src->next->prev = dst->prev;
776
777 dst->prev->next = src->next;
778 dst->prev = src->prev;
779
780 list_dst->num_elements += list_src->num_elements;
781
783 list_src->num_elements = 0;
784
785 return 0;
786}
787
788/** Merge two lists, inserting the source at the head of the destination
789 *
790 * @return
791 * - 0 on success.
792 * - -1 on failure.
793 */
794static inline CC_HINT(nonnull) int fr_dlist_move_head(fr_dlist_head_t *list_dst, fr_dlist_head_t *list_src)
795{
796 fr_dlist_t *dst = &(list_dst->entry);
797 fr_dlist_t *src = &(list_src->entry);
798
799 CHECK_ELEMENT_COUNT(list_dst, list_src->num_elements);
800
801#ifdef WITH_VERIFY_PTR
802 /*
803 * Must be both talloced or both not
804 */
805 if (!fr_cond_assert((list_dst->type && list_src->type) || (!list_dst->type && !list_src->type))) return -1;
806
807 /*
808 * Must be of the same type
809 */
810 if (!fr_cond_assert(!list_dst->type || (strcmp(list_dst->type, list_src->type) == 0))) return -1;
811#endif
812
813 if (!fr_cond_assert(dst->next != NULL)) return -1;
814 if (!fr_cond_assert(dst->prev != NULL)) return -1;
815
816 if (fr_dlist_empty(list_src)) return 0;
817
818 src->next->prev = dst;
819 src->prev->next = dst->next;
820
821 dst->next->prev = src->prev;
822 dst->next = src->next;
823
824 list_dst->num_elements += list_src->num_elements;
825
827 list_src->num_elements = 0;
828
829 return 0;
830}
831
832/** Free the first item in the list
833 *
834 * @param[in] list_head to free head item in.
835 */
836static inline void fr_dlist_talloc_free_head(fr_dlist_head_t *list_head)
837{
838 talloc_free(fr_dlist_pop_head(list_head));
839}
840
841/** Free the last item in the list
842 *
843 * @param[in] list_head to free tail item in.
844 */
845static inline void fr_dlist_talloc_free_tail(fr_dlist_head_t *list_head)
846{
847 talloc_free(fr_dlist_pop_head(list_head));
848}
849
850/** Free the item specified
851 *
852 * @param[in] list_head to free item in.
853 * @param[in] ptr to remove and free.
854 * @return
855 * - NULL if no more items in the list.
856 * - Previous item in the list
857 */
858static inline void *fr_dlist_talloc_free_item(fr_dlist_head_t *list_head, void *ptr)
859{
860 void *prev;
861 prev = fr_dlist_remove(list_head, ptr);
862 talloc_free(ptr);
863 return prev;
864}
865
866/** Free items in a doubly linked list (with talloc)
867 *
868 * @param[in] head of list to free.
869 * @param[in] ptr remove and free from this to the tail.
870 */
871static inline void fr_dlist_talloc_free_to_tail(fr_dlist_head_t *head, void *ptr)
872{
873 void *e = ptr, *p;
874
875 if (!ptr) return; /* NULL means don't do anything */
876
877 while (e) {
878 p = fr_dlist_next(head, e);
879 (void) fr_dlist_remove(head, e);
880 talloc_free(e);
881 e = p;
882 }
883}
884
885
886/** Free all items in a doubly linked list (with talloc)
887 *
888 * @param[in] head of list to free.
889 */
891{
892 void *e = NULL, *p;
893
894 while ((e = fr_dlist_next(head, e))) {
895 p = fr_dlist_remove(head, e);
896 talloc_free(e);
897 e = p;
898 }
899}
900
901/** Free all items in a doubly linked list from the tail backwards
902 *
903 * @param[in] head of list to free.
904 */
906{
907 void *e = NULL, *p;
908
909 e = fr_dlist_tail(head);
910 do {
911 p = fr_dlist_remove(head, e);
912 talloc_free(e);
913 e = p;
914 } while (e);
915}
916
917/** Return the number of elements in the dlist
918 *
919 * @param[in] head of list to count elements for.
920 */
921static inline unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
922{
923 return head->num_elements;
924}
925
926/** Split phase of a merge sort of a dlist
927 *
928 * @note Only to be used within a merge sort
929 *
930 * @param[in] head of the original list being sorted
931 * @param[in] source first item in the section of the list to split
932 * @param[out] front first item of the first half of the split list
933 * @param[out] back first item of the second half of the split list
934 */
935static inline void fr_dlist_sort_split(fr_dlist_head_t *head, void **source, void **front, void **back)
936{
937 void *fast = NULL;
938 void *slow;
939 fr_dlist_t *entry = NULL;
940
941 *front = *source;
942
943 if (*source) entry = fr_dlist_item_to_entry(head->offset, *source);
944 /*
945 * Stopping condition - no more elements left to split
946 */
947 if (!*source || !entry->next) {
948 *back = NULL;
949 return;
950 }
951
952 /*
953 * Fast advances twice as fast as slow, so when it gets to the end,
954 * slow will point to the middle of the linked list.
955 */
956 slow = *source;
957 fast = fr_dlist_next(head, slow);
958 while (fast) {
959 fast = fr_dlist_next(head, fast);
960 if (fast) {
961 slow = fr_dlist_next(head, slow);
962 fast = fr_dlist_next(head, fast);
963 }
964 }
965
966 *back = fr_dlist_next(head, slow);
967
968 if (slow) {
969 entry = fr_dlist_item_to_entry(head->offset, slow);
970 entry->next = NULL;
971 }
972}
973
974/** Merge phase of a merge sort of a dlist
975 *
976 * @note Only to be used within a merge sort
977 *
978 * @param[in] head of the original list being sorted
979 * @param[in] a first element of first list being merged
980 * @param[in] b first element of second list being merged
981 * @param[in] cmp comparison function for the sort
982 * @returns pointer to first item in merged list
983 */
984static inline void *fr_dlist_sort_merge(fr_dlist_head_t *head, void **a, void **b, fr_cmp_t cmp)
985{
986 void *result = NULL;
987 void *next;
988 fr_dlist_t *result_entry;
989 fr_dlist_t *next_entry;
990
991 if (!*a) return *b;
992 if (!*b) return *a;
993
994 /*
995 * Compare entries in the lists
996 */
997 if (cmp(*a, *b) <= 0) {
998 result = *a;
999 next = fr_dlist_next(head, *a);
1000 next = fr_dlist_sort_merge(head, &next, b, cmp);
1001 } else {
1002 result = *b;
1003 next = fr_dlist_next(head, *b);
1004 next = fr_dlist_sort_merge(head, a, &next, cmp);
1005 }
1006
1007 result_entry = fr_dlist_item_to_entry(head->offset, result);
1008 next_entry = fr_dlist_item_to_entry(head->offset, next);
1009 result_entry->next = next_entry;
1010
1011 return result;
1012}
1013
1014/** Recursive sort routine for dlist
1015 *
1016 * @param[in] head of the list being sorted
1017 * @param[in,out] ptr to the first item in the current section of the list being sorted.
1018 * @param[in] cmp comparison function to sort with
1019 */
1020static inline void fr_dlist_recursive_sort(fr_dlist_head_t *head, void **ptr, fr_cmp_t cmp)
1021{
1022 void *a;
1023 void *b;
1024 fr_dlist_t *entry = NULL;
1025
1026 if (*ptr) entry = fr_dlist_item_to_entry(head->offset, *ptr);
1027
1028 if (!*ptr || (!entry->next)) return;
1029 fr_dlist_sort_split(head, ptr, &a, &b); /* Split into sublists */
1030 fr_dlist_recursive_sort(head, &a, cmp); /* Traverse left */
1031 fr_dlist_recursive_sort(head, &b, cmp); /* Traverse right */
1032
1033 /*
1034 * merge the two sorted lists together
1035 */
1036 *ptr = fr_dlist_sort_merge(head, &a, &b, cmp);
1037}
1038
1039/** Sort a dlist using merge sort
1040 *
1041 * @note This routine temporarily breaks the doubly linked nature of the list
1042 *
1043 * @param[in,out] list to sort
1044 * @param[in] cmp comparison function to sort with
1045 */
1046static inline void fr_dlist_sort(fr_dlist_head_t *list, fr_cmp_t cmp)
1047{
1048 void *head;
1049 fr_dlist_t *entry;
1050
1051 if (fr_dlist_num_elements(list) <= 1) return;
1052
1053 head = fr_dlist_head(list);
1054 /* NULL terminate existing list */
1055 list->entry.prev->next = NULL;
1056
1057 /*
1058 * Call the recursive sort routine
1059 */
1060 fr_dlist_recursive_sort(list, &head, cmp);
1061
1062 /*
1063 * Reset "prev" pointers broken during sort
1064 */
1065 entry = fr_dlist_item_to_entry(list->offset, head);
1066 list->entry.next = entry;
1067 entry->prev = &list->entry;
1068
1069 while (head) {
1070 entry = fr_dlist_item_to_entry(list->offset, head);
1071 if (entry->next) {
1072 /*
1073 * There is a "next" entry, point it back to the current one
1074 */
1075 entry->next->prev = entry;
1076 } else {
1077 /*
1078 * No next entry, this is the tail
1079 */
1080 list->entry.prev = entry;
1081 entry->next = &list->entry;
1082 }
1083 head = fr_dlist_next(list, head);
1084 }
1085}
1086
1087static inline void fr_dlist_noop(void)
1088{
1089 return;
1090}
1091
1092/** Expands to the type name used for the entry wrapper structure
1093 *
1094 * @param[in] _name Prefix we add to type-specific structures.
1095 * @return fr_dlist_<name>_entry_t
1096 */
1097#define FR_DLIST_ENTRY(_name) _name ## _entry_t
1098
1099/** Expands to the type name used for the head wrapper structure
1100 *
1101 * @param[in] _name Prefix we add to type-specific structures.
1102 * @return fr_dlist_<name>_head_t
1103 */
1104#define FR_DLIST_HEAD(_name) _name ## _head_t
1105
1106/** Define type specific wrapper structs for dlists
1107 *
1108 * @note This macro should be used inside the header for the area of code
1109 * which will use type specific functions.
1110 */
1111#define FR_DLIST_TYPES(_name) \
1112 typedef struct { fr_dlist_t entry; } FR_DLIST_ENTRY(_name); \
1113 typedef struct { fr_dlist_head_t head; } FR_DLIST_HEAD(_name); \
1114
1115/** Define friendly names for type specific dlist head and entry structures
1116 *
1117 * @param[in] _name Prefix we add to type-specific structures.
1118 * @param[in] _head Name to use for head structure.
1119 * @param[in] _entry Name to use for entry structure.
1120*/
1121#define FR_DLIST_TYPEDEFS(_name, _head, _entry) \
1122 typedef FR_DLIST_HEAD(_name) _head; \
1123 typedef FR_DLIST_ENTRY(_name) _entry;
1124
1125/** Define type specific wrapper functions for dlists
1126 *
1127 * @note This macro should be used inside the source file that will use
1128 * the type specific functions.
1129 *
1130 * @param[in] _name Prefix we add to type-specific dlist functions.
1131 * @param[in] _element_type Type of structure that'll be inserted into the dlist.
1132 * @param[in] _element_entry Field in the _element_type that holds the dlist entry information.
1133 */
1134#define FR_DLIST_FUNCS(_name, _element_type, _element_entry) \
1135DIAG_OFF(unused-function) \
1136 _Static_assert(IS_FIELD_COMPATIBLE(_element_type, _element_entry, FR_DLIST_ENTRY(_name)) == 1, "Bad dlist entry field type");\
1137 static inline fr_dlist_head_t *_name ## _dlist_head(FR_DLIST_HEAD(_name) const *list) \
1138 { return UNCONST(fr_dlist_head_t *, &list->head); } \
1139\
1140 static inline fr_dlist_t *_name ## _dlist_entry(FR_DLIST_ENTRY(_name) const *entry) \
1141 { return UNCONST(fr_dlist_t *, &entry->entry ); } \
1142\
1143 static inline void _name ## _entry_init(_element_type *entry) \
1144 { \
1145 _Generic((&entry->_element_entry), \
1146 FR_DLIST_ENTRY(_name) *: fr_dlist_entry_init(UNCONST(fr_dlist_t *, &entry->_element_entry.entry)), \
1147 FR_DLIST_ENTRY(_name) const *: fr_dlist_noop()\
1148 ); \
1149 } \
1150\
1151 static inline void _name ## _init(FR_DLIST_HEAD(_name) *list) \
1152 { _fr_dlist_init(&list->head, offsetof(_element_type, _element_entry), NULL); } \
1153\
1154 static inline void _name ## _talloc_init(FR_DLIST_HEAD(_name) *list) \
1155 { _fr_dlist_init(&list->head, offsetof(_element_type, _element_entry), #_element_type); } \
1156\
1157 static inline void _name ## _clear(FR_DLIST_HEAD(_name) *list) \
1158 { fr_dlist_clear(&list->head); } \
1159\
1160 static inline bool _name ## _in_list(FR_DLIST_HEAD(_name) *list, _element_type *ptr) \
1161 { return fr_dlist_in_list(&list->head, ptr); } \
1162\
1163 static inline int _name ## _insert_head(FR_DLIST_HEAD(_name) *list, _element_type *ptr) \
1164 { return fr_dlist_insert_head(&list->head, ptr); } \
1165\
1166 static inline int _name ## _insert_tail(FR_DLIST_HEAD(_name) *list, _element_type *ptr) \
1167 { return fr_dlist_insert_tail(&list->head, ptr); } \
1168\
1169 static inline int _name ## _insert_after(FR_DLIST_HEAD(_name) *list, _element_type *pos, _element_type *ptr) \
1170 { return fr_dlist_insert_after(&list->head, pos, ptr); } \
1171\
1172 static inline int _name ## _insert_before(FR_DLIST_HEAD(_name) *list, _element_type *pos, _element_type *ptr) \
1173 { return fr_dlist_insert_before(&list->head, pos, ptr); } \
1174\
1175 static inline _element_type *_name ## _head(FR_DLIST_HEAD(_name) const *list) \
1176 { return fr_dlist_head(&list->head); } \
1177\
1178 static inline bool _name ## _empty(FR_DLIST_HEAD(_name) const *list) \
1179 { return fr_dlist_empty(&list->head); } \
1180\
1181 static inline bool _name ## _initialised(FR_DLIST_HEAD(_name) const *list) \
1182 { return fr_dlist_initialised(&list->head); } \
1183\
1184 static inline _element_type *_name ## _tail(FR_DLIST_HEAD(_name) const *list) \
1185 { return fr_dlist_tail(&list->head); } \
1186\
1187 static inline _element_type *_name ## _next(FR_DLIST_HEAD(_name) const *list, _element_type const *ptr) \
1188 { return fr_dlist_next(&list->head, ptr); } \
1189\
1190 static inline _element_type *_name ## _prev(FR_DLIST_HEAD(_name) const *list, _element_type const *ptr) \
1191 { return fr_dlist_prev(&list->head, ptr); } \
1192\
1193 static inline _element_type *_name ## _remove(FR_DLIST_HEAD(_name) *list, _element_type *ptr) \
1194 { return fr_dlist_remove(&list->head, ptr); } \
1195\
1196 static inline _element_type *_name ## _pop_head(FR_DLIST_HEAD(_name) *list) \
1197 { return fr_dlist_pop_head(&list->head); } \
1198\
1199 static inline _element_type *_name ## _pop_tail(FR_DLIST_HEAD(_name) *list) \
1200 { return fr_dlist_pop_tail(&list->head); } \
1201\
1202 static inline _element_type *_name ## _replace(FR_DLIST_HEAD(_name) *list, _element_type *item, _element_type *ptr) \
1203 { return fr_dlist_replace(&list->head, item, ptr); } \
1204\
1205 static inline void _##_name ## _verify(char const *file, int line, FR_DLIST_HEAD(_name) const *list_head) \
1206 { _fr_dlist_verify(file, line, UNCONST(fr_dlist_head_t *, &list_head->head)); } \
1207\
1208 static inline int _name ## _move(FR_DLIST_HEAD(_name) *dst, FR_DLIST_HEAD(_name) *src) \
1209 { return fr_dlist_move(&dst->head, &src->head); } \
1210\
1211 static inline int _name ## _move_head(FR_DLIST_HEAD(_name) *dst, FR_DLIST_HEAD(_name) *src) \
1212 { return fr_dlist_move_head(&dst->head, &src->head); } \
1213\
1214 static inline void _name ## _talloc_free_head(FR_DLIST_HEAD(_name) *list) \
1215 { fr_dlist_talloc_free_head(&list->head); } \
1216\
1217 static inline void _name ## _talloc_free_tail(FR_DLIST_HEAD(_name) *list) \
1218 { fr_dlist_talloc_free_tail(&list->head); } \
1219\
1220 static inline _element_type * _name ## _talloc_free_item(FR_DLIST_HEAD(_name) *list, _element_type *ptr) \
1221 {return fr_dlist_talloc_free_item(&list->head, ptr); } \
1222\
1223 static inline void _name ## _talloc_free(FR_DLIST_HEAD(_name) *list) \
1224 { fr_dlist_talloc_free(&list->head); } \
1225\
1226 static inline void _name ## _talloc_free_to_tail(FR_DLIST_HEAD(_name) *list, _element_type *ptr) \
1227 { fr_dlist_talloc_free_to_tail(&list->head, ptr); } \
1228\
1229 static inline void _name ## _talloc_reverse_free(FR_DLIST_HEAD(_name) *list) \
1230 { fr_dlist_talloc_reverse_free(&list->head); } \
1231\
1232 static inline unsigned int _name ## _num_elements(FR_DLIST_HEAD(_name) const *list) \
1233 { return fr_dlist_num_elements(&list->head); } \
1234\
1235 static inline void _name ## _sort(FR_DLIST_HEAD(_name) *list, fr_cmp_t cmp) \
1236 { fr_dlist_sort(&list->head, cmp); } \
1237DIAG_ON(unused-function)
1238
1239/*
1240 * In addition to the above call to FR_DLIST_FUNCS two additional macros should be defined
1241 *
1242 * Unfortunately as there's no way to dynamically define macro names these need to be done manually.
1243 *
1244 * #define <name>_foreach(_list_head, _iter) fr_dlist_foreach(<name>_dlist_head(_list_head), <element_type>, _iter)
1245 * #define <name>_verify(_list_head) _<name>_verify(__FILE__, __LINE__, _list_head)
1246 */
1247
1248#ifdef __cplusplus
1249}
1250#endif
int const char * file
Definition acutest.h:704
int const char int line
Definition acutest.h:704
#define RCSIDH(h, id)
Definition build.h:488
#define UNUSED
Definition build.h:317
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:131
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:202
static void fr_dlist_sort(fr_dlist_head_t *list, fr_cmp_t cmp)
Sort a dlist using merge sort.
Definition dlist.h:1046
static void fr_dlist_entry_link_before(fr_dlist_t *entry, fr_dlist_t *to_link)
Link in a single entry before the current entry.
Definition dlist.h:171
static fr_dlist_t * fr_dlist_item_to_entry(size_t offset, void const *item)
Find the dlist pointers within a list item.
Definition dlist.h:104
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition dlist.h:468
static int fr_dlist_move_head(fr_dlist_head_t *list_dst, fr_dlist_head_t *list_src)
Merge two lists, inserting the source at the head of the destination.
Definition dlist.h:794
unsigned int offset
Positive offset from start of structure to fr_dlist_t.
Definition dlist.h:55
static void _fr_dlist_verify(char const *file, int line, fr_dlist_head_t const *list_head)
Check all items in the list are valid.
Definition dlist.h:717
static void fr_dlist_talloc_free_head(fr_dlist_head_t *list_head)
Free the first item in the list.
Definition dlist.h:836
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
static void fr_dlist_sort_split(fr_dlist_head_t *head, void **source, void **front, void **back)
Split phase of a merge sort of a dlist.
Definition dlist.h:935
static void fr_dlist_recursive_sort(fr_dlist_head_t *head, void **ptr, fr_cmp_t cmp)
Recursive sort routine for dlist.
Definition dlist.h:1020
static int fr_dlist_insert_before(fr_dlist_head_t *list_head, void *pos, void *ptr)
Insert an item before an item already in the list.
Definition dlist.h:432
fr_dlist_t * next
Definition dlist.h:43
static bool fr_dlist_entry_in_list(fr_dlist_t const *entry)
Check if a list entry is part of a list.
Definition dlist.h:145
static void * fr_dlist_sort_merge(fr_dlist_head_t *head, void **a, void **b, fr_cmp_t cmp)
Merge phase of a merge sort of a dlist.
Definition dlist.h:984
static void fr_dlist_talloc_free_to_tail(fr_dlist_head_t *head, void *ptr)
Free items in a doubly linked list (with talloc)
Definition dlist.h:871
static void fr_dlist_talloc_free(fr_dlist_head_t *head)
Free all items in a doubly linked list (with talloc)
Definition dlist.h:890
static void fr_dlist_entry_unlink(fr_dlist_t *entry)
Remove an item from the dlist when we don't have access to the head.
Definition dlist.h:128
char const * type
of items contained within the list.
Definition dlist.h:61
static void * fr_dlist_prev(fr_dlist_head_t const *list_head, void const *ptr)
Get the previous item in a list.
Definition dlist.h:570
static bool fr_dlist_in_list(fr_dlist_head_t *list_head, void *ptr)
Check if a list entry is part of a list.
Definition dlist.h:302
static void * fr_dlist_entry_to_item(size_t offset, fr_dlist_t const *entry)
Get the item from a fr_dlist_t.
Definition dlist.h:112
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:921
static bool fr_dlist_empty(fr_dlist_head_t const *list_head)
Check whether a list has any items.
Definition dlist.h:483
static void fr_dlist_talloc_free_tail(fr_dlist_head_t *list_head)
Free the last item in the list.
Definition dlist.h:845
static void * fr_dlist_pop_tail(fr_dlist_head_t *list_head)
Remove the tail item in a list.
Definition dlist.h:670
static void * fr_dlist_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
Definition dlist.h:654
static void * fr_dlist_tail(fr_dlist_head_t const *list_head)
Return the TAIL item of a list or NULL if the list is empty.
Definition dlist.h:513
static void fr_dlist_entry_replace(fr_dlist_t *entry, fr_dlist_t *replacement)
Replace one entry with another.
Definition dlist.h:203
static void * fr_dlist_talloc_free_item(fr_dlist_head_t *list_head, void *ptr)
Free the item specified.
Definition dlist.h:858
static void * fr_dlist_replace(fr_dlist_head_t *list_head, void *item, void *ptr)
Replace an item in a dlist.
Definition dlist.h:688
static void _fr_dlist_init(fr_dlist_head_t *list_head, size_t offset, char const *type)
Initialise common fields in a dlist.
Definition dlist.h:263
unsigned int num_elements
Number of elements contained within the dlist.
Definition dlist.h:59
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
static int fr_dlist_move(fr_dlist_head_t *list_dst, fr_dlist_head_t *list_src)
Merge two lists, inserting the source at the tail of the destination.
Definition dlist.h:745
static void fr_dlist_noop(void)
Definition dlist.h:1087
static void fr_dlist_entry_move(fr_dlist_t *dst, fr_dlist_t *src)
Link in a sublist before the current entry.
Definition dlist.h:188
fr_dlist_t entry
Struct holding the head and tail of the list.
Definition dlist.h:52
fr_dlist_t * prev
Definition dlist.h:42
static void fr_dlist_entry_link_after(fr_dlist_t *entry, fr_dlist_t *to_link)
Link in a single entry after the current entry.
Definition dlist.h:158
static void fr_dlist_talloc_reverse_free(fr_dlist_head_t *head)
Free all items in a doubly linked list from the tail backwards.
Definition dlist.h:905
static int fr_dlist_insert_head(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the head of a list.
Definition dlist.h:320
static void fr_dlist_entry_init(fr_dlist_t *entry)
Initialise a linked list without metadata.
Definition dlist.h:120
#define CHECK_ELEMENT_COUNT(_head, _add)
Verify we're not going to overflow the element count.
Definition dlist.h:286
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:537
static int fr_dlist_insert_after(fr_dlist_head_t *list_head, void *pos, void *ptr)
Insert an item after an item already in the list.
Definition dlist.h:396
static bool fr_dlist_initialised(fr_dlist_head_t const *list_head)
Check if the list head is initialised.
Definition dlist.h:497
static void fr_dlist_clear(fr_dlist_head_t *list_head)
Efficiently remove all elements in a dlist.
Definition dlist.h:277
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
talloc_free(hp)
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
Definition lst.c:122
int8_t(* fr_cmp_t)(void const *a, void const *b)
Definition misc.h:38
#define fr_assert(_expr)
Definition rad_assert.h:38
fr_aka_sim_id_type_t type
static fr_slen_t head
Definition xlat.h:420
int nonnull(2, 5))