The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
24 RCSIDH(tlist_h, "$Id: 5d6d3918a13b36a43049c5a0d32f9a3c2c3cbfea $")
25 
26 #ifdef __cplusplus
27 extern "C" {
28 #endif
29 
30 #include <freeradius-devel/util/dlist.h>
31 
32 typedef struct fr_tlist_head_s fr_tlist_head_t;
33 typedef 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 
43 struct 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  */
53 static 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  */
61 static 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  */
77 static inline void fr_tlist_entry_init(fr_tlist_t *entry)
78 {
80  entry->list_head = NULL;
81 }
82 
83 static 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  */
99 static 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  */
160 static 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  */
186 static 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  */
206 static 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  */
224 static 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  */
245 static 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  */
267 static 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  */
289 static 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  */
306 static 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  */
317 static 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  */
332 static 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  */
344 static 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  */
362 static 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  */
380 static 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  */
413 static 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  */
429 static 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  */
445 static 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  */
463 static 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
489 static 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 
516  fr_tlist_verify(file, line, entry->children);
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  */
534 static 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  */
572 static 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  */
609 static 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  */
618 static 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  */
631 static 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  */
645 static 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  */
694 static 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  */
708 static 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  */
723 static 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  */
734 static 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  */
746 static 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 
751 static 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) \
791 DIAG_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; }
911 DIAG_ON(unused-function)
912 
913 static 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  */
932 static 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  */
958 static 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:419
#define RCSIDH(h, id)
Definition: build.h:445
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:137
#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:208
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 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_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition: dlist.h:555
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 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 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 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_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_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_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_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition: dlist.h:638
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 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 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
fr_assert(0)
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 fr_tlist_head_t * fr_tlist_remove_children(fr_tlist_t *entry)
Remove a child tlist from a parent entry.
Definition: tlist.h:974
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_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_replace(fr_tlist_head_t *list_head, void *item, void *ptr)
Replace an item in a dlist.
Definition: tlist.h:463
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
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_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 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
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_pop_tail(fr_tlist_head_t *list_head)
Remove the tail item in a list.
Definition: tlist.h:445
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
static void * fr_tlist_pop_head(fr_tlist_head_t *list_head)
Remove the head item in a list.
Definition: tlist.h:429
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_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 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_recursive_sort(fr_tlist_head_t *head, void **ptr, fr_cmp_t cmp)
Recursive sort routine for tlist.
Definition: tlist.h:734
static void * fr_tlist_talloc_free_item(fr_tlist_head_t *list_head, void *ptr)
Free the item specified.
Definition: tlist.h:631
#define fr_tlist_foreach_entry(_list_head, _iter)
Iterate over the contents of a list, only one level.
Definition: tlist.h:179
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_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 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 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
fr_dlist_head_t dlist_head
Definition: tlist.h:38
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 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_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 void * fr_tlist_parent(fr_tlist_head_t *list_head, void const *ptr)
Definition: tlist.h:913
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_remove(fr_tlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition: tlist.h:413
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
fr_tlist_head_t * children
any child list
Definition: tlist.h:45
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
fr_dlist_t dlist_entry
the doubly linked list of entries.
Definition: tlist.h:46
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 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 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:408
int nonnull(2, 5))