The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
tlist.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/** Tree list implementation
19 *
20 * @file src/lib/util/tlist.h
21 *
22 * @copyright 2022 Network RADIUS SAS (legal@networkradius.com)
23 */
24RCSIDH(tlist_h, "$Id: 5d6d3918a13b36a43049c5a0d32f9a3c2c3cbfea $")
25
26#ifdef __cplusplus
27extern "C" {
28#endif
29
30#include <freeradius-devel/util/dlist.h>
31
33typedef struct fr_tlist_s fr_tlist_t;
34
36 fr_tlist_t *parent; //!< the parent entry which holds this list. May be NULL.
37
39};
40
41#define tlist_type(_list) ((_list)->dlist_head.type)
42
43struct fr_tlist_s {
44 fr_tlist_head_t *list_head; //!< the list which holds this entry
45 fr_tlist_head_t *children; //!< any child list
46 fr_dlist_t dlist_entry; //!< the doubly linked list of entries.
47};
48
49
50/** Find the tlist pointers within a list item
51 *
52 */
53static inline fr_tlist_t *fr_tlist_item_to_entry(fr_tlist_head_t const *list_head, void const *item)
54{
55 return (fr_tlist_t *)(((uintptr_t) item) + list_head->dlist_head.offset - offsetof(fr_tlist_t, dlist_entry));
56}
57
58/** Get the item from a fr_tlist_t
59 *
60 */
61static inline void *fr_tlist_entry_to_item(fr_tlist_head_t const *list_head, fr_tlist_t const *entry)
62{
63 return (void *)(((uintptr_t) entry) - list_head->dlist_head.offset + offsetof(fr_tlist_t, dlist_entry));
64}
65
66/** Get a fr_tlist_head_t from a fr_dlist_head_t
67 *
68 */
70{
71 return (fr_tlist_head_t *)(((uintptr_t) dlist_head) - offsetof(fr_tlist_head_t, dlist_head));
72}
73
74/** Initialise a linked list without metadata
75 *
76 */
77static inline void fr_tlist_entry_init(fr_tlist_t *entry)
78{
80 entry->list_head = NULL;
81}
82
83static inline CC_HINT(nonnull) void fr_tlist_entry_unlink(fr_tlist_t *entry)
84{
86 entry->list_head = NULL;
87}
88
89/** Check if a list entry is part of a list
90 *
91 * This works because the fr_tlist_head_t has an entry in the list.
92 * So if next and prev both point to the entry for the object being
93 * passed in, then it can't be part of a list with a fr_tlist_head_t.
94 *
95 * @return
96 * - True if in a list.
97 * - False otherwise.
98 */
99static inline CC_HINT(nonnull) bool fr_tlist_entry_in_a_list(fr_tlist_t const *entry)
100{
101 return (entry->list_head != NULL);
102}
103
104
105// no fr_tlist_entry_link_after(), fr_tlist_entry_link_before(), fr_tlist_entry_move(), fr_tlist_entry_replace()
106
107/** Initialise the head structure of a tlist
108 *
109 * @note This variant does not perform talloc validation.
110 *
111 * This function should only be called for the top level of the list.
112 * Please call fr_tlist_add_child() when adding a child list to an
113 * existing entry.
114 *
115 @code{.c}
116 typedef struct {
117 fr_tlist_t tlist;
118 char const *field_a;
119 int *field_b;
120 ...
121 } my_struct_t;
122
123 int my_func(my_struct_t *a, my_struct_t *b)
124 {
125 fr_tlist_head_t head;
126
127 fr_tlist_init(&head, my_struct_t, tlist);
128 fr_tlist_insert_head(&head, a);
129 fr_tlist_insert_head(&head, b);
130 }
131 @endcode
132 *
133 * @param[in] _head structure to initialise.
134 * @param[in] _type of item being stored in the list, e.g. fr_value_box_t,
135 * fr_dict_attr_t etc...
136 * @param[in] _field Containing the #fr_tlist_t within item being stored.
137 */
138#define fr_tlist_init(_head, _type, _field) \
139 _Generic((((_type *)0)->_field), fr_tlist_t: _fr_tlist_init(_head, offsetof(_type, _field), NULL))
140
141/** Initialise the head structure of a tlist
142 *
143 * @note This variant *DOES* perform talloc validation. All items inserted
144 * into the list must be allocated with talloc.
145 *
146 * @copybrief fr_tlist_init
147 *
148 * @param[in] _head structure to initialise.
149 * @param[in] _type of item being stored in the list, e.g. fr_value_box_t,
150 * fr_dict_attr_t etc...
151 * @param[in] _field Containing the #fr_tlist_t within item being stored.
152 */
153#define fr_tlist_talloc_init(_head, _type, _field) \
154 _Generic((((_type *)0)->_field), fr_tlist_t: _fr_tlist_init(_head, offsetof(_type, _field), #_type))
155
156/** Initialise common fields in a tlist
157 *
158 * The dlist entries point to the tlist structure, which then points to the real structure.
159 */
160static inline void _fr_tlist_init(fr_tlist_head_t *list_head, size_t offset, char const *type)
161{
162 list_head->parent = NULL;
163
164 /*
165 * Initialize the dlist, but point to the ENCLOSING data
166 * structure and type, not to the #fr_tlist_t.
167 */
168 fr_dlist_init(&list_head->dlist_head, fr_tlist_t, dlist_entry);
169 list_head->dlist_head.offset += offset;
170 list_head->dlist_head.type = type;
171}
172
173/** Iterate over the contents of a list, only one level
174 *
175 * @param[in] _list_head to iterate over.
176 * @param[in] _iter Name of iteration variable.
177 * Will be declared in the scope of the loop.
178 */
179#define fr_tlist_foreach_entry(_list_head, _iter) \
180 for (void *_iter = fr_dlist_head(&_list_head->dlist_head); _iter; _iter = fr_dlist_next(&_list_head->dlist_head, _iter))
181
182/** Remove all elements in a tlist
183 *
184 * @param[in] list_head to clear.
185 */
186static inline void fr_tlist_clear(fr_tlist_head_t *list_head)
187{
188 fr_tlist_foreach_entry(list_head, item) {
189 fr_tlist_t *entry = fr_tlist_item_to_entry(list_head, item);
190
191 entry->list_head = NULL;
192 }
193 fr_dlist_clear(&list_head->dlist_head);
194}
195
196/** Check if a list entry is part of a tlist
197 *
198 * This works because the fr_tlist_head_t has an entry in the list.
199 * So if next and prev both point to the entry for the object being
200 * passed in, then it can't be part of a list with a fr_tlist_head_t.
201 *
202 * @return
203 * - True if in a list.
204 * - False otherwise.
205 */
206static inline CC_HINT(nonnull) bool fr_tlist_in_list(fr_tlist_head_t *list_head, void *ptr)
207{
208 fr_tlist_t *entry = fr_tlist_item_to_entry(list_head, ptr);
209
210 return (entry->list_head == list_head);
211}
212
213/** Insert an item into the head of a list
214 *
215 * @note If #fr_tlist_talloc_init was used to initialise #fr_tlist_head_t
216 * ptr must be a talloced chunk of the type passed to #fr_tlist_talloc_init.
217 *
218 * @param[in] list_head to insert ptr into.
219 * @param[in] ptr to insert.
220 * @return
221 * - 0 on success.
222 * - -1 on failure.
223 */
224static inline CC_HINT(nonnull) int fr_tlist_insert_head(fr_tlist_head_t *list_head, void *ptr)
225{
226 fr_tlist_t *entry = fr_tlist_item_to_entry(list_head, ptr);
227
228 if (fr_dlist_insert_head(&list_head->dlist_head, ptr) < 0) return -1;
229
230 entry->list_head = list_head;
231 return 0;
232}
233
234/** Insert an item into the tail of a list
235 *
236 * @note If #fr_tlist_talloc_init was used to initialise #fr_tlist_head_t
237 * ptr must be a talloced chunk of the type passed to #fr_tlist_talloc_init.
238 *
239 * @param[in] list_head to insert ptr into.
240 * @param[in] ptr to insert.
241 * @return
242 * - 0 on success.
243 * - -1 on failure.
244 */
245static inline CC_HINT(nonnull) int fr_tlist_insert_tail(fr_tlist_head_t *list_head, void *ptr)
246{
247 fr_tlist_t *entry = fr_tlist_item_to_entry(list_head, ptr);
248
249 if (fr_dlist_insert_tail(&list_head->dlist_head, ptr) < 0) return -1;
250
251 entry->list_head = list_head;
252 return 0;
253}
254
255/** Insert an item after an item already in the list
256 *
257 * @note If #fr_tlist_talloc_init was used to initialise #fr_tlist_head_t
258 * ptr must be a talloced chunk of the type passed to #fr_tlist_talloc_init.
259 *
260 * @param[in] list_head to insert ptr into.
261 * @param[in] pos to insert ptr after.
262 * @param[in] ptr to insert.
263 * @return
264 * - 0 on success.
265 * - -1 on failure.
266 */
267static inline CC_HINT(nonnull(1,3)) int fr_tlist_insert_after(fr_tlist_head_t *list_head, void *pos, void *ptr)
268{
269 fr_tlist_t *entry = fr_tlist_item_to_entry(list_head, ptr);
270
271 if (fr_dlist_insert_after(&list_head->dlist_head, pos, ptr) < 0) return -1;
272
273 entry->list_head = list_head;
274 return 0;
275}
276
277/** Insert an item after an item already in the list
278 *
279 * @note If #fr_tlist_talloc_init was used to initialise #fr_tlist_head_t
280 * ptr must be a talloced chunk of the type passed to #fr_tlist_talloc_init.
281 *
282 * @param[in] list_head to insert ptr into.
283 * @param[in] pos to insert ptr before.
284 * @param[in] ptr to insert.
285 * @return
286 * - 0 on success.
287 * - -1 on failure.
288 */
289static inline CC_HINT(nonnull(1,3)) int fr_tlist_insert_before(fr_tlist_head_t *list_head, void *pos, void *ptr)
290{
291 fr_tlist_t *entry = fr_tlist_item_to_entry(list_head, ptr);
292
293 if (fr_dlist_insert_before(&list_head->dlist_head, pos, ptr) < 0) return -1;
294
295 entry->list_head = list_head;
296 return 0;
297}
298
299/** Return the HEAD item of a list or NULL if the list is empty
300 *
301 * @param[in] list_head to return the HEAD item from.
302 * @return
303 * - The HEAD item.
304 * - NULL if no items exist in the list.
305 */
306static inline CC_HINT(nonnull) void *fr_tlist_head(fr_tlist_head_t const *list_head)
307{
308 return fr_dlist_head(&list_head->dlist_head);
309}
310
311/** Check whether a list has any items.
312 *
313 * @return
314 * - True if it does not.
315 * - False if it does.
316 */
317static inline bool fr_tlist_empty(fr_tlist_head_t const *list_head)
318{
319 if (!list_head) return true;
320
321 return fr_dlist_empty(&list_head->dlist_head);
322}
323
324/** Check if the list head is initialised
325 *
326 * Memory must be zeroed out or initialised.
327 *
328 * @return
329 * - True if tlist initialised.
330 * - False if tlist not initialised
331 */
332static inline CC_HINT(nonnull) bool fr_tlist_initialised(fr_tlist_head_t const *list_head)
333{
334 return fr_dlist_initialised(&list_head->dlist_head);
335}
336
337/** Return the TAIL item of a list or NULL if the list is empty
338 *
339 * @param[in] list_head to return the TAIL item from.
340 * @return
341 * - The TAIL item.
342 * - NULL if no items exist in the list.
343 */
344static inline CC_HINT(nonnull) void *fr_tlist_tail(fr_tlist_head_t const *list_head)
345{
346 return fr_dlist_tail(&list_head->dlist_head);
347}
348
349/** Get the next item in a list
350 *
351 * @note If #fr_dlist_talloc_init was used to initialise #fr_dlist_head_t
352 * ptr must be a talloced chunk of the type passed to #fr_tlist_talloc_init.
353 *
354 * @param[in] list_head containing ptr.
355 * @param[in] ptr to retrieve the next item from.
356 * If ptr is NULL, the HEAD of the list will be returned.
357 * @return
358 * - The next item in the list if ptr is not NULL.
359 * - The head of the list if ptr is NULL.
360 * - NULL if ptr is the tail of the list (no more items).
361 */
362static inline CC_HINT(nonnull(1)) void *fr_tlist_next(fr_tlist_head_t const *list_head, void const *ptr)
363{
364 return fr_dlist_next(&list_head->dlist_head, ptr);
365}
366
367/** Get the previous item in a list
368 *
369 * @note If #fr_dlist_talloc_init was used to initialise #fr_dlist_head_t
370 * ptr must be a talloced chunk of the type passed to #fr_tlist_talloc_init.
371 *
372 * @param[in] list_head containing ptr.
373 * @param[in] ptr to retrieve the prev item from.
374 * If ptr is NULL, the HEAD of the list will be returned.
375 * @return
376 * - The prev item in the list if ptr is not NULL.
377 * - The head of the list if ptr is NULL.
378 * - NULL if ptr is the tail of the list (no more items).
379 */
380static inline CC_HINT(nonnull(1)) void *fr_tlist_prev(fr_tlist_head_t const *list_head, void const *ptr)
381{
382 return fr_dlist_prev(&list_head->dlist_head, ptr);
383}
384
385/** Remove an item from the list
386 *
387 * @note If #fr_tlist_talloc_init was used to initialise #fr_tlist_head_t
388 * ptr must be a talloced chunk of the type passed to #fr_tlist_talloc_init.
389 *
390 * When removing items in an iteration loop, the iterator variable must be
391 * assigned the item returned by this function.
392 *
393 * If the iterator variable is not updated, the item removed will be the last item
394 * iterated over, as its prev/prev pointers are set to point to itself.
395 @code{.c}
396 my_item_t *item = NULL;
397
398 while ((item = fr_tlist_prev(&head, item))) {
399 if (item->should_be_removed) {
400 ...do things with item
401 item = fr_tlist_remove(&head, item);
402 continue;
403 }
404 }
405 @endcode
406 *
407 * @param[in] list_head to remove ptr from.
408 * @param[in] ptr to remove.
409 * @return
410 * - The previous item in the list (makes iteration easier).
411 * - NULL if we just removed the head of the list.
412 */
413static inline CC_HINT(nonnull(1)) void *fr_tlist_remove(fr_tlist_head_t *list_head, void *ptr)
414{
415 fr_tlist_t *entry = fr_tlist_item_to_entry(list_head, ptr);
416
417 entry->list_head = NULL;
418
419 return fr_dlist_remove(&list_head->dlist_head, ptr);
420}
421
422/** Remove the head item in a list
423 *
424 * @param[in] list_head to remove head item from.
425 * @return
426 * - The item removed.
427 * - NULL if not items in tlist.
428 */
429static inline CC_HINT(nonnull(1)) void *fr_tlist_pop_head(fr_tlist_head_t *list_head)
430{
431 void *item = fr_tlist_head(list_head);
432
433 (void) fr_tlist_remove(list_head, item);
434
435 return item;
436}
437
438/** Remove the tail item in a list
439 *
440 * @param[in] list_head to remove tail item from.
441 * @return
442 * - The item removed.
443 * - NULL if not items in tlist.
444 */
445static inline CC_HINT(nonnull(1)) void *fr_tlist_pop_tail(fr_tlist_head_t *list_head)
446{
447 void *item = fr_dlist_tail(&list_head->dlist_head);
448
449 (void) fr_dlist_remove(&list_head->dlist_head, item);
450
451 return item;
452}
453
454/** Replace an item in a dlist
455 *
456 * @param list_head in which the original item is.
457 * @param item to replace.
458 * @param ptr replacement item.
459 * @return
460 * - The item replaced
461 * - NULL if nothing replaced
462 */
463static inline CC_HINT(nonnull) void *fr_tlist_replace(fr_tlist_head_t *list_head, void *item, void *ptr)
464{
465 fr_tlist_t *item_entry;
466 fr_tlist_t *ptr_entry;
467
468 if (!fr_tlist_in_list(list_head, item)) return NULL;
469
470 ptr_entry = fr_tlist_item_to_entry(list_head, ptr);
471 fr_dlist_replace(&list_head->dlist_head, item, ptr);
472 ptr_entry->list_head = list_head;
473
474 item_entry = fr_tlist_item_to_entry(list_head, item);
475 item_entry->list_head = NULL;
476
477 return item;
478}
479
480
481/** Check all items in the list are valid
482 *
483 * Checks item talloc headers and types to ensure they're consistent
484 * with what we expect.
485 *
486 * Does nothing if the list was not initialised with #fr_tlist_talloc_init.
487 */
488#ifndef TALLOC_GET_TYPE_ABORT_NOOP
489static inline CC_HINT(nonnull) void fr_tlist_verify(char const *file, int line, fr_tlist_head_t const *list_head)
490{
491 void *item;
492
493 if (!tlist_type(list_head)) return;
494
495 fr_assert_msg(fr_tlist_initialised(list_head), "CONSISTENCY CHECK FAILED %s[%i]: tlist not initialised",
496 file, line);
497
498 for (item = fr_tlist_head(list_head);
499 item;
500 item = fr_tlist_next(list_head, item)) {
501 fr_tlist_t *entry = fr_tlist_item_to_entry(list_head, item);
502
503 fr_assert_msg(entry->list_head == list_head, "CONSISTENCY CHECK FAILED %s[%i]: tlist entry %p has wrong parent",
504 file, line, entry);
505
506 item = _talloc_get_type_abort(item, tlist_type(list_head), __location__);
507
508 if (entry->children) {
509 fr_assert_msg(tlist_type(entry->children) != NULL, "CONSISTENCY CHECK FAILED %s[%i]: tlist entry %p has non-talloc'd child list",
510 file, line, entry);
511
512 fr_assert_msg(strcmp(tlist_type(entry->children), tlist_type(list_head)) == 0,
513 "CONSISTENCY CHECK FAILED %s[%i]: tlist entry %p has different child type from parent",
514 file, line, entry);
515
517 }
518 }
519}
520# define FR_TLIST_VERIFY(_head) fr_tlist_verify(__FILE__, __LINE__, _head)
521#elif !defined(NDEBUG)
522# define FR_TLIST_VERIFY(_head) fr_assert(_head)
523#else
524# define FR_TLIST_VERIFY(_head)
525#endif
526
527
528/** Merge two lists, inserting the source at the tail of the destination
529 *
530 * @return
531 * - 0 on success.
532 * - -1 on failure.
533 */
534static inline CC_HINT(nonnull) int fr_tlist_move(fr_tlist_head_t *list_dst, fr_tlist_head_t *list_src)
535{
536 void *item;
537
538#ifdef WITH_VERIFY_PTR
539 /*
540 * Must be both talloced or both not
541 */
542 if (!fr_cond_assert((tlist_type(list_dst) && tlist_type(list_src)) || (!tlist_type(list_dst) && !tlist_type(list_src)))) return -1;
543
544 /*
545 * Must be of the same type
546 */
547 if (!fr_cond_assert(!tlist_type(list_dst) || (strcmp(tlist_type(list_dst), tlist_type(list_src)) == 0))) return -1;
548#endif
549
550 item = fr_dlist_head(&list_src->dlist_head);
551 if (!item) return 0;
552
553 if (fr_dlist_move(&list_dst->dlist_head, &list_src->dlist_head) < 0) return -1;
554
555 /*
556 * Update new parent from the middle of the list to the end.
557 */
558 do {
559 fr_tlist_t *entry = fr_tlist_item_to_entry(list_src, item);
560 entry->list_head = list_dst;
561 } while ((item = fr_dlist_next(&list_dst->dlist_head, item)) != NULL);
562
563 return 0;
564}
565
566/** Merge two lists, inserting the source at the head of the destination
567 *
568 * @return
569 * - 0 on success.
570 * - -1 on failure.
571 */
572static inline CC_HINT(nonnull) int fr_tlist_move_head(fr_tlist_head_t *list_dst, fr_tlist_head_t *list_src)
573{
574 void *item, *middle;
575
576#ifdef WITH_VERIFY_PTR
577 /*
578 * Must be both talloced or both not
579 */
580 if (!fr_cond_assert((tlist_type(list_dst) && tlist_type(list_src)) || (!tlist_type(list_dst) && !tlist_type(list_src)))) return -1;
581
582 /*
583 * Must be of the same type
584 */
585 if (!fr_cond_assert(!tlist_type(list_dst) || (strcmp(tlist_type(list_dst), tlist_type(list_src)) == 0))) return -1;
586#endif
587
588 middle = fr_dlist_head(&list_dst->dlist_head);
589
590 if (fr_dlist_move_head(&list_dst->dlist_head, &list_src->dlist_head) < 0) return -1;
591
592 /*
593 * Update new parent from the start of the list to the middle.
594 */
595 for (item = fr_tlist_head(list_dst);
596 item && (item != middle);
597 item = fr_tlist_next(list_dst, item)) {
598 fr_tlist_t *entry = fr_tlist_item_to_entry(list_src, item);
599 entry->list_head = list_dst;
600 }
601
602 return 0;
603}
604
605/** Free the first item in the list
606 *
607 * @param[in] list_head to free head item in.
608 */
609static inline void fr_tlist_talloc_free_head(fr_tlist_head_t *list_head)
610{
611 talloc_free(fr_tlist_pop_head(list_head));
612}
613
614/** Free the last item in the list
615 *
616 * @param[in] list_head to free tail item in.
617 */
618static inline void fr_tlist_talloc_free_tail(fr_tlist_head_t *list_head)
619{
620 talloc_free(fr_tlist_pop_head(list_head));
621}
622
623/** Free the item specified
624 *
625 * @param[in] list_head to free item in.
626 * @param[in] ptr to remove and free.
627 * @return
628 * - NULL if no more items in the list.
629 * - Previous item in the list
630 */
631static inline void *fr_tlist_talloc_free_item(fr_tlist_head_t *list_head, void *ptr)
632{
633 void *prev;
634
635 prev = fr_tlist_remove(list_head, ptr);
636 talloc_free(ptr);
637 return prev;
638}
639
640/** Free items in a doubly linked list (with talloc)
641 *
642 * @param[in] head of list to free.
643 * @param[in] ptr remove and free from this to the tail.
644 */
645static inline void fr_tlist_talloc_free_to_tail(fr_tlist_head_t *head, void *ptr)
646{
647 void *e = ptr, *p;
648
649 if (!ptr) return; /* uninitialized means don't do anything */
650
651 while (e) {
652 p = fr_tlist_next(head, e);
653 (void) fr_tlist_remove(head, e);
654 talloc_free(e);
655 e = p;
656 }
657}
658
659/** Free all items in a doubly linked list (with talloc)
660 *
661 * @param[in] head of list to free.
662 */
664{
665 void *e = NULL, *p;
666
667 while ((e = fr_tlist_next(head, e))) {
668 p = fr_tlist_remove(head, e);
669 talloc_free(e);
670 e = p;
671 }
672}
673
674/** Free all items in a doubly linked list from the tail backwards
675 *
676 * @param[in] head of list to free.
677 */
679{
680 void *e = NULL, *p;
681
682 e = fr_tlist_tail(head);
683 do {
684 p = fr_tlist_remove(head, e);
685 talloc_free(e);
686 e = p;
687 } while (e);
688}
689
690/** Return the number of elements in the tlist
691 *
692 * @param[in] list_head of list to count elements for.
693 */
694static inline unsigned int fr_tlist_num_elements(fr_tlist_head_t const *list_head)
695{
696 return fr_dlist_num_elements(&list_head->dlist_head);
697}
698
699/** Split phase of a merge sort of a dlist
700 *
701 * @note Only to be used within a merge sort
702 *
703 * @param[in] head of the original list being sorted
704 * @param[in] source first item in the section of the list to split
705 * @param[out] front first item of the first half of the split list
706 * @param[out] back first item of the second half of the split list
707 */
708static inline void fr_tlist_sort_split(fr_tlist_head_t *head, void **source, void **front, void **back)
709{
710 fr_dlist_sort_split(&head->dlist_head, source, front, back);
711}
712
713/** Merge phase of a merge sort of a dlist
714 *
715 * @note Only to be used within a merge sort
716 *
717 * @param[in] head of the original list being sorted
718 * @param[in] a first element of first list being merged
719 * @param[in] b first element of second list being merged
720 * @param[in] cmp comparison function for the sort
721 * @returns pointer to first item in merged list
722 */
723static inline void *fr_tlist_sort_merge(fr_tlist_head_t *head, void **a, void **b, fr_cmp_t cmp)
724{
725 return fr_dlist_sort_merge(&head->dlist_head, a, b, cmp);
726}
727
728/** Recursive sort routine for tlist
729 *
730 * @param[in] head of the list being sorted
731 * @param[in,out] ptr to the first item in the current section of the list being sorted.
732 * @param[in] cmp comparison function to sort with
733 */
734static inline void fr_tlist_recursive_sort(fr_tlist_head_t *head, void **ptr, fr_cmp_t cmp)
735{
736 fr_dlist_recursive_sort(&head->dlist_head, ptr, cmp);
737}
738
739/** Sort a tlist using merge sort
740 *
741 * @note This routine temporarily breaks the doubly linked nature of the list
742 *
743 * @param[in,out] list to sort
744 * @param[in] cmp comparison function to sort with
745 */
746static inline void fr_tlist_sort(fr_tlist_head_t *list, fr_cmp_t cmp)
747{
748 fr_dlist_sort(&list->dlist_head, cmp);
749}
750
751static inline void fr_tlist_noop(void)
752{
753 return;
754}
755
756
757/** Expands to the type name used for the entry wrapper structure
758 *
759 * @param[in] _name Prefix we add to type-specific structures.
760 * @return fr_tlist_<name>_entry_t
761 */
762#define FR_TLIST_ENTRY(_name) _name ## _entry_t
763
764/** Expands to the type name used for the head wrapper structure
765 *
766 * @param[in] _name Prefix we add to type-specific structures.
767 * @return fr_tlist_<name>_head_t
768 */
769#define FR_TLIST_HEAD(_name) _name ## _head_t
770
771/** Define type specific wrapper structs for tlists
772 *
773 * @note This macro should be used inside the header for the area of code
774 * which will use type specific functions.
775 */
776#define FR_TLIST_TYPES(_name) \
777 typedef struct { fr_tlist_t entry; } FR_TLIST_ENTRY(_name); \
778 typedef struct { fr_tlist_head_t head; } FR_TLIST_HEAD(_name); \
779
780
781/** Define type specific wrapper functions for tlists
782 *
783 * @note This macro should be used inside the source file that will use
784 * the type specific functions.
785 *
786 * @param[in] _name Prefix we add to type-specific tlist functions.
787 * @param[in] _element_type Type of structure that'll be inserted into the tlist.
788 * @param[in] _element_entry Field in the _element_type that holds the tlist entry information.
789 */
790#define FR_TLIST_FUNCS(_name, _element_type, _element_entry) \
791DIAG_OFF(unused-function) \
792 _Static_assert(IS_FIELD_COMPATIBLE(_element_type, _element_entry, FR_TLIST_ENTRY(_name)) == 1, "Bad tlist entry field type");\
793 static inline fr_tlist_head_t *_name ## _list_head(FR_TLIST_HEAD(_name) const *list) \
794 { return UNCONST(fr_tlist_head_t *, &list->head); } \
795\
796 static inline fr_dlist_head_t *_name ## _dlist_head(FR_TLIST_HEAD(_name) const *list) \
797 { return UNCONST(fr_dlist_head_t *, &list->head.dlist_head); } \
798\
799 static inline void _name ## _entry_init(_element_type *entry) \
800 { \
801 _Generic((&entry->_element_entry), \
802 FR_TLIST_ENTRY(_name) *: fr_tlist_entry_init(UNCONST(fr_tlist_t *, &entry->_element_entry.entry)), \
803 FR_TLIST_ENTRY(_name) const *: fr_tlist_noop()\
804 ); \
805 } \
806\
807 static inline void _name ## _init(FR_TLIST_HEAD(_name) *list) \
808 { _fr_tlist_init(&list->head, offsetof(_element_type, _element_entry), NULL); } \
809\
810 static inline void _name ## _talloc_init(FR_TLIST_HEAD(_name) *list) \
811 { _fr_tlist_init(&list->head, offsetof(_element_type, _element_entry), #_element_type); } \
812\
813 static inline void _name ## _clear(FR_TLIST_HEAD(_name) *list) \
814 { fr_tlist_clear(&list->head); } \
815\
816 static inline bool _name ## _in_list(FR_TLIST_HEAD(_name) *list, _element_type *ptr) \
817 { return fr_tlist_in_list(&list->head, ptr); } \
818\
819 static inline bool _name ## _in_a_list(_element_type *ptr) \
820 { return fr_tlist_entry_in_a_list(&ptr->_element_entry.entry); } \
821\
822 static inline int _name ## _insert_head(FR_TLIST_HEAD(_name) *list, _element_type *ptr) \
823 { return fr_tlist_insert_head(&list->head, ptr); } \
824\
825 static inline int _name ## _insert_tail(FR_TLIST_HEAD(_name) *list, _element_type *ptr) \
826 { return fr_tlist_insert_tail(&list->head, ptr); } \
827\
828 static inline int _name ## _insert_after(FR_TLIST_HEAD(_name) *list, _element_type *pos, _element_type *ptr) \
829 { return fr_tlist_insert_after(&list->head, pos, ptr); } \
830\
831 static inline int _name ## _insert_before(FR_TLIST_HEAD(_name) *list, _element_type *pos, _element_type *ptr) \
832 { return fr_tlist_insert_before(&list->head, pos, ptr); } \
833\
834 static inline _element_type *_name ## _head(FR_TLIST_HEAD(_name) const *list) \
835 { return fr_tlist_head(&list->head); } \
836\
837 static inline bool _name ## _empty(FR_TLIST_HEAD(_name) const *list) \
838 { return fr_tlist_empty(&list->head); } \
839\
840 static inline bool _name ## _initialised(FR_TLIST_HEAD(_name) const *list) \
841 { return fr_tlist_initialised(&list->head); } \
842\
843 static inline _element_type *_name ## _tail(FR_TLIST_HEAD(_name) const *list) \
844 { return fr_tlist_tail(&list->head); } \
845\
846 static inline _element_type *_name ## _next(FR_TLIST_HEAD(_name) const *list, _element_type const *ptr) \
847 { return fr_tlist_next(&list->head, ptr); } \
848\
849 static inline _element_type *_name ## _prev(FR_TLIST_HEAD(_name) const *list, _element_type const *ptr) \
850 { return fr_tlist_prev(&list->head, ptr); } \
851\
852 static inline _element_type *_name ## _remove(FR_TLIST_HEAD(_name) *list, _element_type *ptr) \
853 { return fr_tlist_remove(&list->head, ptr); } \
854\
855 static inline _element_type *_name ## _pop_head(FR_TLIST_HEAD(_name) *list) \
856 { return fr_tlist_pop_head(&list->head); } \
857\
858 static inline _element_type *_name ## _pop_tail(FR_TLIST_HEAD(_name) *list) \
859 { return fr_tlist_pop_tail(&list->head); } \
860\
861 static inline _element_type *_name ## _replace(FR_TLIST_HEAD(_name) *list, _element_type *item, _element_type *ptr) \
862 { return fr_tlist_replace(&list->head, item, ptr); } \
863\
864 static inline int _name ## _move(FR_TLIST_HEAD(_name) *dst, FR_TLIST_HEAD(_name) *src) \
865 { return fr_tlist_move(&dst->head, &src->head); } \
866\
867 static inline int _name ## _move_head(FR_TLIST_HEAD(_name) *dst, FR_TLIST_HEAD(_name) *src) \
868 { return fr_tlist_move_head(&dst->head, &src->head); } \
869\
870 static inline void _name ## _talloc_free_head(FR_TLIST_HEAD(_name) *list) \
871 { fr_tlist_talloc_free_head(&list->head); } \
872\
873 static inline void _name ## _talloc_free_tail(FR_TLIST_HEAD(_name) *list) \
874 { fr_tlist_talloc_free_tail(&list->head); } \
875\
876 static inline void _name ## _talloc_free_item(FR_TLIST_HEAD(_name) *list, _element_type *ptr) \
877 { fr_tlist_talloc_free_item(&list->head, ptr); } \
878\
879 static inline void _name ## _talloc_free(FR_TLIST_HEAD(_name) *list) \
880 { fr_tlist_talloc_free(&list->head); } \
881\
882 static inline void _name ## _talloc_free_to_tail(FR_TLIST_HEAD(_name) *list, _element_type *ptr) \
883 { fr_tlist_talloc_free_to_tail(&list->head, ptr); } \
884\
885 static inline void _name ## _talloc_reverse_free(FR_TLIST_HEAD(_name) *list) \
886 { fr_tlist_talloc_reverse_free(&list->head); } \
887\
888 static inline unsigned int _name ## _num_elements(FR_TLIST_HEAD(_name) const *list) \
889 { return fr_tlist_num_elements(&list->head); } \
890\
891 static inline void _name ## _sort(FR_TLIST_HEAD(_name) *list, fr_cmp_t cmp) \
892 { fr_tlist_sort(&list->head, cmp); } \
893\
894 static inline FR_TLIST_HEAD(_name) *_name ## _parent(const _element_type *ptr) \
895 { return (FR_TLIST_HEAD(_name) *) (ptr->_element_entry.entry.list_head); } \
896\
897 static inline FR_TLIST_HEAD(_name) *_name ## _children(_element_type *ptr) \
898 { return (FR_TLIST_HEAD(_name) *) (ptr->_element_entry.entry.children); } \
899\
900 static inline void _name ## _talloc_init_children(_element_type *ptr, FR_TLIST_HEAD(_name) *children) \
901 { _name ## _talloc_init(children); ptr->_element_entry.entry.children = &children->head; } \
902\
903 static inline void _name ## _add_children(_element_type *ptr, FR_TLIST_HEAD(_name) *children) \
904 { fr_tlist_add_children(&ptr->_element_entry.entry, &children->head); } \
905\
906 static inline FR_TLIST_HEAD(_name) * _name ## _remove_children(_element_type *ptr) \
907 { return (FR_TLIST_HEAD(_name) *) fr_tlist_remove_children(&ptr->_element_entry.entry); } \
908\
909 static inline void _name ## _set_head(fr_tlist_head_t *list, _element_type *ptr) \
910 { ptr->_element_entry.entry.list_head = list; }
911DIAG_ON(unused-function)
912
913static inline void *fr_tlist_parent(fr_tlist_head_t *list_head, void const *ptr)
914{
915 fr_tlist_t *entry;
916
917 if (!ptr || !list_head) return NULL;
918
919 entry = fr_tlist_item_to_entry(list_head, ptr);
920 if (!entry->list_head) return NULL;
921
922 if (!entry->list_head->parent) return NULL;
923
924 return fr_tlist_entry_to_item(entry->list_head, entry->list_head->parent);
925}
926
927/** Initialize a child tlist based on a parent entry
928 *
929 * @param[in] entry the entry which will be the parent of the children
930 * @param[in] children structure to initialise. Usually in the same parent structure as "entry"
931 */
932static inline void fr_tlist_init_children(fr_tlist_t *entry, fr_tlist_head_t *children)
933{
934 fr_tlist_head_t *list_head;
935
936 fr_assert(entry->children == NULL);
937 fr_assert(entry->list_head != NULL);
938
939 list_head = entry->list_head;
940
941 /*
942 * Manually re-do fr_tlist_init() here, as we copy offset/type from the parent list.
943 */
944 fr_dlist_init(&children->dlist_head, fr_tlist_t, dlist_entry);
945 children->dlist_head.offset = list_head->dlist_head.offset;
946 children->dlist_head.type = list_head->dlist_head.type;
947
948 children->parent = NULL;
949
950 entry->children = children;
951}
952
953/** Add a pre-initialized child tlist to a parent entry.
954 *
955 * @param[in] entry the entry which will be the parent of the children
956 * @param[in] children structure to initialise. Usually in the same parent structure as "entry"
957 */
958static inline int fr_tlist_add_children(fr_tlist_t *entry, fr_tlist_head_t *children)
959{
960 if (entry->children) return -1;
961
962 children->parent = entry;
963
964 entry->children = children;
965
966 return 0;
967}
968
969
970/** Remove a child tlist from a parent entry
971 *
972 * @param[in] entry the entry which will have the children removed
973 */
975{
976 fr_tlist_head_t *children = entry->children;
977
978 if (!entry->children) return NULL;
979
980 entry->children->parent = NULL;
981 entry->children = NULL;
982
983 return children;
984}
985
986#ifdef __cplusplus
987}
988#endif
int const char * file
Definition acutest.h:702
int const char int line
Definition acutest.h:702
#define DIAG_ON(_x)
Definition build.h:458
#define RCSIDH(h, id)
Definition build.h:484
#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
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:260
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_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
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_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 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_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_replace(fr_dlist_head_t *list_head, void *item, void *ptr)
Replace an item in a dlist.
Definition dlist.h:706
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 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
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 void fr_tlist_entry_init(fr_tlist_t *entry)
Initialise a linked list without metadata.
Definition tlist.h:77
static void * fr_tlist_head(fr_tlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition tlist.h:306
static void fr_tlist_talloc_free(fr_tlist_head_t *head)
Free all items in a doubly linked list (with talloc)
Definition tlist.h:663
static void fr_tlist_talloc_free_to_tail(fr_tlist_head_t *head, void *ptr)
Free items in a doubly linked list (with talloc)
Definition tlist.h:645
fr_tlist_t * parent
the parent entry which holds this list. May be NULL.
Definition tlist.h:36
static void fr_tlist_sort_split(fr_tlist_head_t *head, void **source, void **front, void **back)
Split phase of a merge sort of a dlist.
Definition tlist.h:708
static void * fr_tlist_pop_tail(fr_tlist_head_t *list_head)
Remove the tail item in a list.
Definition tlist.h:445
static bool fr_tlist_empty(fr_tlist_head_t const *list_head)
Check whether a list has any items.
Definition tlist.h:317
static void fr_tlist_talloc_free_tail(fr_tlist_head_t *list_head)
Free the last item in the list.
Definition tlist.h:618
static void fr_tlist_talloc_free_head(fr_tlist_head_t *list_head)
Free the first item in the list.
Definition tlist.h:609
static void * fr_tlist_sort_merge(fr_tlist_head_t *head, void **a, void **b, fr_cmp_t cmp)
Merge phase of a merge sort of a dlist.
Definition tlist.h:723
static int fr_tlist_insert_tail(fr_tlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition tlist.h:245
fr_tlist_head_t * list_head
the list which holds this entry
Definition tlist.h:44
static void fr_tlist_noop(void)
Definition tlist.h:751
static void fr_tlist_sort(fr_tlist_head_t *list, fr_cmp_t cmp)
Sort a tlist using merge sort.
Definition tlist.h:746
static void fr_tlist_verify(char const *file, int line, fr_tlist_head_t const *list_head)
Check all items in the list are valid.
Definition tlist.h:489
static void * fr_tlist_talloc_free_item(fr_tlist_head_t *list_head, void *ptr)
Free the item specified.
Definition tlist.h:631
static void fr_tlist_recursive_sort(fr_tlist_head_t *head, void **ptr, fr_cmp_t cmp)
Recursive sort routine for tlist.
Definition tlist.h:734
#define fr_tlist_foreach_entry(_list_head, _iter)
Iterate over the contents of a list, only one level.
Definition tlist.h:179
static int fr_tlist_insert_before(fr_tlist_head_t *list_head, void *pos, void *ptr)
Insert an item after an item already in the list.
Definition tlist.h:289
static void * fr_tlist_entry_to_item(fr_tlist_head_t const *list_head, fr_tlist_t const *entry)
Get the item from a fr_tlist_t.
Definition tlist.h:61
static void * fr_tlist_pop_head(fr_tlist_head_t *list_head)
Remove the head item in a list.
Definition tlist.h:429
static int fr_tlist_insert_head(fr_tlist_head_t *list_head, void *ptr)
Insert an item into the head of a list.
Definition tlist.h:224
static void * fr_tlist_parent(fr_tlist_head_t *list_head, void const *ptr)
Definition tlist.h:913
static void * fr_tlist_prev(fr_tlist_head_t const *list_head, void const *ptr)
Get the previous item in a list.
Definition tlist.h:380
static int fr_tlist_add_children(fr_tlist_t *entry, fr_tlist_head_t *children)
Add a pre-initialized child tlist to a parent entry.
Definition tlist.h:958
static fr_tlist_t * fr_tlist_item_to_entry(fr_tlist_head_t const *list_head, void const *item)
Find the tlist pointers within a list item.
Definition tlist.h:53
fr_dlist_head_t dlist_head
Definition tlist.h:38
static void * fr_tlist_next(fr_tlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition tlist.h:362
static int fr_tlist_move(fr_tlist_head_t *list_dst, fr_tlist_head_t *list_src)
Merge two lists, inserting the source at the tail of the destination.
Definition tlist.h:534
static void * fr_tlist_remove(fr_tlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition tlist.h:413
static unsigned int fr_tlist_num_elements(fr_tlist_head_t const *list_head)
Return the number of elements in the tlist.
Definition tlist.h:694
static bool fr_tlist_entry_in_a_list(fr_tlist_t const *entry)
Check if a list entry is part of a list.
Definition tlist.h:99
static void * fr_tlist_replace(fr_tlist_head_t *list_head, void *item, void *ptr)
Replace an item in a dlist.
Definition tlist.h:463
static void fr_tlist_init_children(fr_tlist_t *entry, fr_tlist_head_t *children)
Initialize a child tlist based on a parent entry.
Definition tlist.h:932
static int fr_tlist_insert_after(fr_tlist_head_t *list_head, void *pos, void *ptr)
Insert an item after an item already in the list.
Definition tlist.h:267
static int fr_tlist_move_head(fr_tlist_head_t *list_dst, fr_tlist_head_t *list_src)
Merge two lists, inserting the source at the head of the destination.
Definition tlist.h:572
static void fr_tlist_entry_unlink(fr_tlist_t *entry)
Definition tlist.h:83
static void fr_tlist_clear(fr_tlist_head_t *list_head)
Remove all elements in a tlist.
Definition tlist.h:186
static void fr_tlist_talloc_reverse_free(fr_tlist_head_t *head)
Free all items in a doubly linked list from the tail backwards.
Definition tlist.h:678
static void _fr_tlist_init(fr_tlist_head_t *list_head, size_t offset, char const *type)
Initialise common fields in a tlist.
Definition tlist.h:160
#define tlist_type(_list)
Definition tlist.h:41
static fr_tlist_head_t * fr_tlist_remove_children(fr_tlist_t *entry)
Remove a child tlist from a parent entry.
Definition tlist.h:974
fr_tlist_head_t * children
any child list
Definition tlist.h:45
static void * fr_tlist_tail(fr_tlist_head_t const *list_head)
Return the TAIL item of a list or NULL if the list is empty.
Definition tlist.h:344
static bool fr_tlist_in_list(fr_tlist_head_t *list_head, void *ptr)
Check if a list entry is part of a tlist.
Definition tlist.h:206
static fr_tlist_head_t * fr_tlist_head_from_dlist(fr_dlist_head_t *dlist_head)
Get a fr_tlist_head_t from a fr_dlist_head_t.
Definition tlist.h:69
fr_dlist_t dlist_entry
the doubly linked list of entries.
Definition tlist.h:46
static bool fr_tlist_initialised(fr_tlist_head_t const *list_head)
Check if the list head is initialised.
Definition tlist.h:332
static fr_slen_t head
Definition xlat.h:422
int nonnull(2, 5))