The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
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: 4eb99aa56a13ed25cabc4888c08852a57ac2ff73 $")
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 * @return result of talloc_free
836 */
837static inline int fr_dlist_talloc_free_head(fr_dlist_head_t *list_head)
838{
839 return talloc_free(fr_dlist_pop_head(list_head));
840}
841
842/** Free the last item in the list
843 *
844 * @param[in] list_head to free tail item in.
845 * @return result of talloc_free
846 */
847static inline int fr_dlist_talloc_free_tail(fr_dlist_head_t *list_head)
848{
849 return talloc_free(fr_dlist_pop_tail(list_head));
850}
851
852/** Free the item specified
853 *
854 * @param[in] list_head to free item in.
855 * @param[in] ptr to remove and free.
856 * @return
857 * - NULL if no more items in the list.
858 * - Previous item in the list
859 */
860static inline void *fr_dlist_talloc_free_item(fr_dlist_head_t *list_head, void *ptr)
861{
862 void *prev;
863 prev = fr_dlist_remove(list_head, ptr);
864 talloc_free(ptr);
865 return prev;
866}
867
868/** Free items in a doubly linked list (with talloc)
869 *
870 * @param[in] head of list to free.
871 * @param[in] ptr remove and free from this to the tail.
872 */
873static inline void fr_dlist_talloc_free_to_tail(fr_dlist_head_t *head, void *ptr)
874{
875 void *e = ptr, *p;
876
877 if (!ptr) return; /* NULL means don't do anything */
878
879 while (e) {
880 p = fr_dlist_next(head, e);
881 (void) fr_dlist_remove(head, e);
882 talloc_free(e);
883 e = p;
884 }
885}
886
887
888/** Free all items in a doubly linked list (with talloc)
889 *
890 * @param[in] head of list to free.
891 */
893{
894 void *e;
895
896 while ((e = fr_dlist_pop_head(head)) != NULL) {
897 talloc_free(e);
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[out] merged first item in the merged list
979 * @param[in] head of the original list being sorted
980 * @param[in] a first element of first list being merged
981 * @param[in] b first element of second list being merged
982 * @param[in] cmp comparison function for the sort
983 * @return
984 * - 0 on success.
985 * - -1 on comparator error.
986 */
987static inline int fr_dlist_sort_merge(void **merged, fr_dlist_head_t *head, void **a, void **b, fr_cmp_t cmp)
988{
989 void *result = NULL;
990 void *next;
991 fr_dlist_t *result_entry;
992 fr_dlist_t *next_entry;
993 fr_cmp_ret_t c;
994 int ret = 0;
995
996 if (!*a) {
997 *merged = *b;
998 return 0;
999 }
1000 if (!*b) {
1001 *merged = *a;
1002 return 0;
1003 }
1004
1005 /*
1006 * Compare entries in the lists.
1007 *
1008 * On comparator error keep merging so every node is relinked:
1009 * the list stays intact, only the order is undefined. Sibling
1010 * recursion levels may still call the comparator after an error;
1011 * the error is accumulated through the return values.
1012 */
1013 c = cmp(*a, *b);
1014 if (unlikely(c == CMP_ERR)) {
1015 ret = -1;
1016 c = CMP_EQ;
1017 }
1018 if (c != CMP_GT) {
1019 result = *a;
1020 next = fr_dlist_next(head, *a);
1021 if (unlikely(fr_dlist_sort_merge(&next, head, &next, b, cmp) < 0)) ret = -1;
1022 } else {
1023 result = *b;
1024 next = fr_dlist_next(head, *b);
1025 if (unlikely(fr_dlist_sort_merge(&next, head, a, &next, cmp) < 0)) ret = -1;
1026 }
1027
1028 result_entry = fr_dlist_item_to_entry(head->offset, result);
1029 next_entry = next ? fr_dlist_item_to_entry(head->offset, next) : NULL;
1030 result_entry->next = next_entry;
1031
1032 *merged = result;
1033 return ret;
1034}
1035
1036/** Recursive sort routine for dlist
1037 *
1038 * @param[in] head of the list being sorted
1039 * @param[in,out] ptr to the first item in the current section of the list being sorted.
1040 * @param[in] cmp comparison function to sort with
1041 * @return
1042 * - 0 on success.
1043 * - -1 on comparator error.
1044 */
1045static inline int fr_dlist_recursive_sort(fr_dlist_head_t *head, void **ptr, fr_cmp_t cmp)
1046{
1047 void *a;
1048 void *b;
1049 fr_dlist_t *entry = NULL;
1050 int ret = 0;
1051
1052 if (*ptr) entry = fr_dlist_item_to_entry(head->offset, *ptr);
1053
1054 if (!*ptr || (!entry->next)) return 0;
1055 fr_dlist_sort_split(head, ptr, &a, &b); /* Split into sublists */
1056 if (unlikely(fr_dlist_recursive_sort(head, &a, cmp) < 0)) ret = -1; /* Traverse left */
1057 if (unlikely(fr_dlist_recursive_sort(head, &b, cmp) < 0)) ret = -1; /* Traverse right */
1058
1059 /*
1060 * merge the two sorted lists together
1061 */
1062 if (unlikely(fr_dlist_sort_merge(ptr, head, &a, &b, cmp) < 0)) ret = -1;
1063
1064 return ret;
1065}
1066
1067/** Sort a dlist using merge sort
1068 *
1069 * @note This routine temporarily breaks the doubly linked nature of the list
1070 *
1071 * @param[in,out] list to sort
1072 * @param[in] cmp comparison function to sort with
1073 * @return
1074 * - 0 on success.
1075 * - -1 on comparator error, retrieve the error with fr_strerror. The list is
1076 * intact but its order is undefined.
1077 */
1078static inline int fr_dlist_sort(fr_dlist_head_t *list, fr_cmp_t cmp)
1079{
1080 void *head;
1081 fr_dlist_t *entry;
1082 int ret;
1083
1084 if (fr_dlist_num_elements(list) <= 1) return 0;
1085
1086 head = fr_dlist_head(list);
1087 /* NULL terminate existing list */
1088 list->entry.prev->next = NULL;
1089
1090 /*
1091 * Call the recursive sort routine
1092 */
1093 ret = fr_dlist_recursive_sort(list, &head, cmp);
1094
1095 /*
1096 * Reset "prev" pointers broken during sort
1097 */
1098 entry = fr_dlist_item_to_entry(list->offset, head);
1099 list->entry.next = entry;
1100 entry->prev = &list->entry;
1101
1102 while (head) {
1103 entry = fr_dlist_item_to_entry(list->offset, head);
1104 if (entry->next) {
1105 /*
1106 * There is a "next" entry, point it back to the current one
1107 */
1108 entry->next->prev = entry;
1109 } else {
1110 /*
1111 * No next entry, this is the tail
1112 */
1113 list->entry.prev = entry;
1114 entry->next = &list->entry;
1115 }
1116 head = fr_dlist_next(list, head);
1117 }
1118
1119 return ret;
1120}
1121
1122static inline void fr_dlist_noop(void)
1123{
1124 return;
1125}
1126
1127/** Expands to the type name used for the entry wrapper structure
1128 *
1129 * @param[in] _name Prefix we add to type-specific structures.
1130 * @return fr_dlist_<name>_entry_t
1131 */
1132#define FR_DLIST_ENTRY(_name) _name ## _entry_t
1133
1134/** Expands to the type name used for the head wrapper structure
1135 *
1136 * @param[in] _name Prefix we add to type-specific structures.
1137 * @return fr_dlist_<name>_head_t
1138 */
1139#define FR_DLIST_HEAD(_name) _name ## _head_t
1140
1141/** Define type specific wrapper structs for dlists
1142 *
1143 * @note This macro should be used inside the header for the area of code
1144 * which will use type specific functions.
1145 */
1146#define FR_DLIST_TYPES(_name) \
1147 typedef struct { fr_dlist_t entry; } FR_DLIST_ENTRY(_name); \
1148 typedef struct { fr_dlist_head_t head; } FR_DLIST_HEAD(_name); \
1149
1150/** Define friendly names for type specific dlist head and entry structures
1151 *
1152 * @param[in] _name Prefix we add to type-specific structures.
1153 * @param[in] _head Name to use for head structure.
1154 * @param[in] _entry Name to use for entry structure.
1155*/
1156#define FR_DLIST_TYPEDEFS(_name, _head, _entry) \
1157 typedef FR_DLIST_HEAD(_name) _head; \
1158 typedef FR_DLIST_ENTRY(_name) _entry;
1159
1160/** Define type specific wrapper functions for dlists
1161 *
1162 * @note This macro should be used inside the source file that will use
1163 * the type specific functions.
1164 *
1165 * @param[in] _name Prefix we add to type-specific dlist functions.
1166 * @param[in] _element_type Type of structure that'll be inserted into the dlist.
1167 * @param[in] _element_entry Field in the _element_type that holds the dlist entry information.
1168 */
1169#define FR_DLIST_FUNCS(_name, _element_type, _element_entry) \
1170DIAG_OFF(unused-function) \
1171 _Static_assert(IS_FIELD_COMPATIBLE(_element_type, _element_entry, FR_DLIST_ENTRY(_name)) == 1, "Bad dlist entry field type");\
1172 static inline fr_dlist_head_t *_name ## _dlist_head(FR_DLIST_HEAD(_name) const *list) \
1173 { return UNCONST(fr_dlist_head_t *, &list->head); } \
1174\
1175 static inline fr_dlist_t *_name ## _dlist_entry(FR_DLIST_ENTRY(_name) const *entry) \
1176 { return UNCONST(fr_dlist_t *, &entry->entry ); } \
1177\
1178 static inline void _name ## _entry_init(_element_type *entry) \
1179 { \
1180 _Generic((&entry->_element_entry), \
1181 FR_DLIST_ENTRY(_name) *: fr_dlist_entry_init(UNCONST(fr_dlist_t *, &entry->_element_entry.entry)), \
1182 FR_DLIST_ENTRY(_name) const *: fr_dlist_noop()\
1183 ); \
1184 } \
1185\
1186 static inline void _name ## _init(FR_DLIST_HEAD(_name) *list) \
1187 { _fr_dlist_init(&list->head, offsetof(_element_type, _element_entry), NULL); } \
1188\
1189 static inline void _name ## _talloc_init(FR_DLIST_HEAD(_name) *list) \
1190 { _fr_dlist_init(&list->head, offsetof(_element_type, _element_entry), #_element_type); } \
1191\
1192 static inline void _name ## _clear(FR_DLIST_HEAD(_name) *list) \
1193 { fr_dlist_clear(&list->head); } \
1194\
1195 static inline bool _name ## _in_list(FR_DLIST_HEAD(_name) *list, _element_type *ptr) \
1196 { return fr_dlist_in_list(&list->head, ptr); } \
1197\
1198 static inline int _name ## _insert_head(FR_DLIST_HEAD(_name) *list, _element_type *ptr) \
1199 { return fr_dlist_insert_head(&list->head, ptr); } \
1200\
1201 static inline int _name ## _insert_tail(FR_DLIST_HEAD(_name) *list, _element_type *ptr) \
1202 { return fr_dlist_insert_tail(&list->head, ptr); } \
1203\
1204 static inline int _name ## _insert_after(FR_DLIST_HEAD(_name) *list, _element_type *pos, _element_type *ptr) \
1205 { return fr_dlist_insert_after(&list->head, pos, ptr); } \
1206\
1207 static inline int _name ## _insert_before(FR_DLIST_HEAD(_name) *list, _element_type *pos, _element_type *ptr) \
1208 { return fr_dlist_insert_before(&list->head, pos, ptr); } \
1209\
1210 static inline _element_type *_name ## _head(FR_DLIST_HEAD(_name) const *list) \
1211 { return fr_dlist_head(&list->head); } \
1212\
1213 static inline bool _name ## _empty(FR_DLIST_HEAD(_name) const *list) \
1214 { return fr_dlist_empty(&list->head); } \
1215\
1216 static inline bool _name ## _initialised(FR_DLIST_HEAD(_name) const *list) \
1217 { return fr_dlist_initialised(&list->head); } \
1218\
1219 static inline _element_type *_name ## _tail(FR_DLIST_HEAD(_name) const *list) \
1220 { return fr_dlist_tail(&list->head); } \
1221\
1222 static inline _element_type *_name ## _next(FR_DLIST_HEAD(_name) const *list, _element_type const *ptr) \
1223 { return fr_dlist_next(&list->head, ptr); } \
1224\
1225 static inline _element_type *_name ## _prev(FR_DLIST_HEAD(_name) const *list, _element_type const *ptr) \
1226 { return fr_dlist_prev(&list->head, ptr); } \
1227\
1228 static inline _element_type *_name ## _remove(FR_DLIST_HEAD(_name) *list, _element_type *ptr) \
1229 { return fr_dlist_remove(&list->head, ptr); } \
1230\
1231 static inline _element_type *_name ## _pop_head(FR_DLIST_HEAD(_name) *list) \
1232 { return fr_dlist_pop_head(&list->head); } \
1233\
1234 static inline _element_type *_name ## _pop_tail(FR_DLIST_HEAD(_name) *list) \
1235 { return fr_dlist_pop_tail(&list->head); } \
1236\
1237 static inline _element_type *_name ## _replace(FR_DLIST_HEAD(_name) *list, _element_type *item, _element_type *ptr) \
1238 { return fr_dlist_replace(&list->head, item, ptr); } \
1239\
1240 static inline void _##_name ## _verify(char const *file, int line, FR_DLIST_HEAD(_name) const *list_head) \
1241 { _fr_dlist_verify(file, line, UNCONST(fr_dlist_head_t *, &list_head->head)); } \
1242\
1243 static inline int _name ## _move(FR_DLIST_HEAD(_name) *dst, FR_DLIST_HEAD(_name) *src) \
1244 { return fr_dlist_move(&dst->head, &src->head); } \
1245\
1246 static inline int _name ## _move_head(FR_DLIST_HEAD(_name) *dst, FR_DLIST_HEAD(_name) *src) \
1247 { return fr_dlist_move_head(&dst->head, &src->head); } \
1248\
1249 static inline void _name ## _talloc_free_head(FR_DLIST_HEAD(_name) *list) \
1250 { fr_dlist_talloc_free_head(&list->head); } \
1251\
1252 static inline void _name ## _talloc_free_tail(FR_DLIST_HEAD(_name) *list) \
1253 { fr_dlist_talloc_free_tail(&list->head); } \
1254\
1255 static inline _element_type * _name ## _talloc_free_item(FR_DLIST_HEAD(_name) *list, _element_type *ptr) \
1256 {return fr_dlist_talloc_free_item(&list->head, ptr); } \
1257\
1258 static inline void _name ## _talloc_free(FR_DLIST_HEAD(_name) *list) \
1259 { fr_dlist_talloc_free(&list->head); } \
1260\
1261 static inline void _name ## _talloc_free_to_tail(FR_DLIST_HEAD(_name) *list, _element_type *ptr) \
1262 { fr_dlist_talloc_free_to_tail(&list->head, ptr); } \
1263\
1264 static inline void _name ## _talloc_reverse_free(FR_DLIST_HEAD(_name) *list) \
1265 { fr_dlist_talloc_reverse_free(&list->head); } \
1266\
1267 static inline unsigned int _name ## _num_elements(FR_DLIST_HEAD(_name) const *list) \
1268 { return fr_dlist_num_elements(&list->head); } \
1269\
1270 static inline int _name ## _sort(FR_DLIST_HEAD(_name) *list, fr_cmp_t cmp) \
1271 { return fr_dlist_sort(&list->head, cmp); } \
1272DIAG_ON(unused-function)
1273
1274/*
1275 * In addition to the above call to FR_DLIST_FUNCS two additional macros should be defined
1276 *
1277 * Unfortunately as there's no way to dynamically define macro names these need to be done manually.
1278 *
1279 * #define <name>_foreach(_list_head, _iter) fr_dlist_foreach(<name>_dlist_head(_list_head), <element_type>, _iter)
1280 * #define <name>_verify(_list_head) _<name>_verify(__FILE__, __LINE__, _list_head)
1281 */
1282
1283#ifdef __cplusplus
1284}
1285#endif
int const char * file
Definition acutest.h:702
#define RCSIDH(h, id)
Definition build.h:561
#define unlikely(_x)
Definition build.h:455
#define UNUSED
Definition build.h:384
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:172
#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:243
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 int fr_dlist_sort_merge(void **merged, 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:987
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 int fr_dlist_sort(fr_dlist_head_t *list, fr_cmp_t cmp)
Sort a dlist using merge sort.
Definition dlist.h:1078
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_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 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 int fr_dlist_talloc_free_head(fr_dlist_head_t *list_head)
Free the first item in the list.
Definition dlist.h:837
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:873
static void fr_dlist_talloc_free(fr_dlist_head_t *head)
Free all items in a doubly linked list (with talloc)
Definition dlist.h:892
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_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:860
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:1122
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 int fr_dlist_recursive_sort(fr_dlist_head_t *head, void **ptr, fr_cmp_t cmp)
Recursive sort routine for dlist.
Definition dlist.h:1045
static int fr_dlist_talloc_free_tail(fr_dlist_head_t *list_head)
Free the last item in the list.
Definition dlist.h:847
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
Definition dwarf.c:563
talloc_free(hp)
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
Definition lst.c:121
fr_cmp_ret_t(* fr_cmp_t)(void const *a, void const *b)
Definition misc.h:57
fr_cmp_ret_t
Result of an ordering comparison.
Definition misc.h:50
@ CMP_GT
a > b
Definition misc.h:54
@ CMP_ERR
comparison failed
Definition misc.h:51
@ CMP_EQ
a == b
Definition misc.h:53
#define fr_assert(_expr)
Definition rad_assert.h:37
fr_aka_sim_id_type_t type
static fr_slen_t head
Definition xlat.h:421
int nonnull(2, 5))