The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
24 RCSIDH(dlist_h, "$Id: a500692c3d1e629036eb246a3aae2ae4c3715e1d $")
25 
26 #ifdef __cplusplus
27 extern "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 
36 typedef struct fr_dlist_s fr_dlist_t;
37 
38 /** Entry in a doubly linked list
39  *
40  */
41 struct fr_dlist_s {
44 };
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  */
51 typedef 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 
65 static_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  */
122 static 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  */
130 static 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  */
138 static 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  */
146 static 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  */
163 static 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  */
176 static 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  */
189 static 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  */
206 static 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  */
221 static 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  */
281 static 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  */
295 static 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  */
320 static 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  */
338 static inline CC_HINT(nonnull) int fr_dlist_insert_head(fr_dlist_head_t *list_head, void *ptr)
339 {
340  fr_dlist_t *entry;
341  fr_dlist_t *head;
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  */
378 static inline CC_HINT(nonnull) int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
379 {
380  fr_dlist_t *entry;
381  fr_dlist_t *head;
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  */
414 static 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  */
450 static 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  */
486 static 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  */
501 static 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  */
515 static 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  */
531 static 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  */
555 static 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  */
588 static 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  */
638 static inline CC_HINT(nonnull(1)) void *fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
639 {
640  fr_dlist_t *entry;
641  fr_dlist_t *head;
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  */
672 static 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  */
688 static 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  */
706 static 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
735 static 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
750 static 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  */
763 static 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 
800  fr_dlist_entry_init(src);
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  */
812 static 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 
844  fr_dlist_entry_init(src);
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  */
854 static 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  */
863 static 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  */
876 static 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  */
889 static 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  */
939 static 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  */
953 static 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  */
1002 static 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  */
1038 static 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  */
1064 static 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 
1105 static 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) \
1153 DIAG_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); } \
1255 DIAG_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:445
#define static_assert
For systems with an old version libc, define static_assert.
Definition: build.h:35
#define UNUSED
Definition: build.h:313
#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
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 void * fr_dlist_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
Definition: dlist.h:672
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_talloc_free_item(fr_dlist_head_t *list_head, void *ptr)
Free the item specified.
Definition: dlist.h:876
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
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_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_pop_tail(fr_dlist_head_t *list_head)
Remove the tail item in a list.
Definition: dlist.h:688
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 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 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_talloc_free_tail(fr_dlist_head_t *list_head)
Free the last item in the list.
Definition: dlist.h:863
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_entry_to_item(size_t offset, fr_dlist_t const *entry)
Get the item from a fr_dlist_t.
Definition: dlist.h:130
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 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_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition: dlist.h:638
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_prev(fr_dlist_head_t const *list_head, void const *ptr)
Get the previous item in a list.
Definition: dlist.h:588
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
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
#define CHECK_ELEMENT_COUNT(_head, _add)
Verify we're not going to overflow the element count.
Definition: dlist.h:304
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 fr_slen_t head
Definition: xlat.h:408
int nonnull(2, 5))