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