The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
dcursor.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, cursor 2 of the
5  * License as published by the Free Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /** Functions to iterate over a sets and subsets of items stored in dlists
18  *
19  * @file src/lib/util/dcursor.h
20  *
21  * @copyright 2020 The FreeRADIUS server project
22  */
23 RCSIDH(dcursor_h, "$Id: 97e8e2cbdc04e98f106f8e70211cccd07ea8a77d $")
24 
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 #include <freeradius-devel/build.h>
30 #include <freeradius-devel/missing.h>
31 #include <freeradius-devel/util/dlist.h>
32 #include <freeradius-devel/util/talloc.h>
33 
34 #include <stddef.h>
35 #include <stdbool.h>
36 
37 DIAG_OFF(unused-function)
38 typedef struct fr_dcursor_s fr_dcursor_t;
39 
40 /** Callback for implementing custom iterators
41  *
42  * @param[in] list head of the dlist.
43  * @param[in] to_eval the next item in the list. Iterator should check to
44  * see if it matches the iterator's filter, and if it doesn't
45  * iterate over the items until one is found that does.
46  * @param[in] uctx passed to #fr_dcursor_init.
47  * @return
48  * - to_eval if to_eval matched, or a subsequent attribute if that matched.
49  * - NULL if no more matching attributes were found.
50  */
51 typedef void *(*fr_dcursor_iter_t)(fr_dlist_head_t *list, void *to_eval, void *uctx);
52 
53 /** Callback for performing additional actions on insert
54  *
55  * @param[in] list head of the dlist.
56  * @param[in] to_insert The item being inserted into the cursor.
57  * @param[in] uctx passed to #fr_dcursor_init.
58  * @return
59  * - 0 on success.
60  * - -1 on failure.
61  */
62 typedef int (*fr_dcursor_insert_t)(fr_dlist_head_t *list, void *to_insert, void *uctx);
63 
64 /** Callback for performing additional actions on removal
65  *
66  * @param[in] list head of the dlist.
67  * @param[in] to_delete The item being removed from the cursor.
68  * @param[in] uctx passed to #fr_dcursor_init.
69  * @return
70  * - 0 on success if the caller should do the list removal.
71  * - 1 on success if the callback has done the list removal.
72  * - -1 on failure.
73  */
74 typedef int (*fr_dcursor_remove_t)(fr_dlist_head_t *list, void *to_delete, void *uctx);
75 
76 /** Copy callback for duplicating complex dcursor state
77  *
78  * @param[out] out dcursor to copy to.
79  * @param[in] in dcursor to copy from.
80  */
81 typedef void (*fr_dcursor_copy_t)(fr_dcursor_t *out, fr_dcursor_t const *in);
82 
83 /** Type of evaluation functions to pass to the fr_dcursor_filter_*() functions.
84  *
85  * @param[in] item the item to be evaluated
86  * @param[in] uctx context that may assist with evaluation
87  * @return
88  * - true if the evaluation function is satisfied.
89  * - false if the evaluation function is not satisfied.
90  */
91 typedef bool (*fr_dcursor_eval_t)(void const *item, void const *uctx);
92 
93 struct fr_dcursor_s {
94  fr_dlist_head_t *dlist; //!< Head of the doubly linked list being iterated over.
95  void *current; //!< The current item in the dlist.
96 
97  fr_dcursor_iter_t iter; //!< Iterator function.
98  fr_dcursor_iter_t peek; //!< Distinct "peek" function. This is sometimes necessary
99  ///< for iterators with complex state.
100  void *iter_uctx; //!< to pass to iterator function.
101 
102  fr_dcursor_insert_t insert; //!< Callback function on insert.
103  fr_dcursor_remove_t remove; //!< Callback function on delete.
104  void *mod_uctx; //!< to pass to modification functions.
105 
106  fr_dcursor_copy_t copy; //!< Copy dcursor state.
107 
108  bool is_const; //!< The list we're iterating over is immutable.
109  bool at_end; //!< We're at the end of the list.
110 };
111 
112 typedef struct {
113  uint8_t depth; //!< Which cursor is currently in use.
114  fr_dcursor_t cursor[]; //!< Stack of cursors.
116 
117 #ifndef TALLOC_GET_TYPE_ABORT_NOOP
118 #define VALIDATE(_item) do { if (cursor->dlist->type && (_item)) _talloc_get_type_abort(_item, cursor->dlist->type, __location__); } while (0)
119 #else
120 #define VALIDATE(_item)
121 #endif
122 
123 /** If current is set to a NULL pointer, we record that fact
124  *
125  * This stops us jumping back to the start of the dlist.
126  */
127 static inline void *dcursor_current_set(fr_dcursor_t *cursor, void *current)
128 {
129  VALIDATE(current);
130 
131  cursor->at_end = (current == NULL);
132  cursor->current = current;
133 
134  return current;
135 }
136 
137 /** Internal function to get the next item
138  *
139  * @param[in] cursor to operate on.
140  * @param[in] iter function.
141  * @param[in] current attribute.
142  * @return
143  * - The next attribute.
144  * - NULL if no more attributes.
145  */
146 CC_NO_UBSAN(function) /* UBSAN: false positive - Type specific dcursor pointer trips --fasanitize=function */
147 static inline void *dcursor_next(fr_dcursor_t *cursor, fr_dcursor_iter_t iter, void *current)
148 {
149  void *next;
150 
151  /*
152  * Fast path without custom iter
153  */
154  if (!iter) {
155  if (likely(current != NULL)) return fr_dlist_next(cursor->dlist, current);
156 
157  if (cursor->at_end) return NULL; /* At tail of the list */
158 
159  return fr_dlist_head(cursor->dlist);
160  }
161 
162  /*
163  * First time next has been called, or potentially
164  * another call after we hit the end of the list.
165  */
166  if (!current) {
167  if (cursor->at_end) return NULL; /* At tail of the list */
168 
169  next = iter(cursor->dlist, NULL, cursor->iter_uctx);
170  VALIDATE(next);
171  return next;
172  }
174 
175  /*
176  * The iterator will advance to the next matching entry.
177  */
178  next = iter(cursor->dlist, current, cursor->iter_uctx);
180 
181  return next;
182 }
183 
184 /** Copy cursor parameters and state.
185  *
186  * @param[out] out Where to copy the cursor to.
187  * @param[in] in cursor to copy.
188  *
189  * @hidecallergraph
190  */
191 CC_HINT(nonnull)
192 static inline void fr_dcursor_copy(fr_dcursor_t *out, fr_dcursor_t const *in)
193 {
194  memcpy(out, in, sizeof(*out));
195 
196  if (in->copy) fr_dcursor_copy(out, in);
197 }
198 
199 /** Copy a read-only iterator from a parent to a child cursor
200  *
201  * @param[out] out Where to copy the cursor to.
202  * @param[in] in cursor to copy.
203  *
204  * @hidecallergraph
205  */
206 CC_HINT(nonnull)
208 {
209  fr_assert(!out->iter);
210  fr_assert(!out->iter_uctx);
211 
212  out->iter = in->iter;
213  out->iter_uctx = in->iter_uctx;
214 
215 #ifndef NDEBUG
216  /*
217  * The output cursor has to be read-only.
218  */
219  out->insert = NULL;
220  out->remove = NULL;
221  out->mod_uctx = NULL;
222  out->copy = NULL;
223 #endif
224 }
225 
226 /** Rewind cursor to the start of the list
227  *
228  * @param[in] cursor to operate on.
229  * @return item at the start of the list.
230  *
231  * @hidecallergraph
232  */
233 CC_HINT(nonnull)
234 static inline void *fr_dcursor_head(fr_dcursor_t *cursor)
235 {
236  /*
237  * If we have a custom iterator, the dlist attribute
238  * may not be in the subset the iterator would
239  * return, so set everything to NULL and have
240  * dcursor_next figure it out.
241  */
242  if (cursor->iter) {
243  cursor->at_end = false; /* reset the flag, else next will just return NULL */
244 
245  return dcursor_current_set(cursor, dcursor_next(cursor, cursor->iter, NULL));
246  }
247 
248  return dcursor_current_set(cursor, fr_dlist_head(cursor->dlist));
249 }
250 
251 /** Wind cursor to the tail item in the list
252  *
253  * @param[in] cursor to operate on.
254  * @return item at the end of the list.
255  *
256  * @hidecallergraph
257  */
258 CC_HINT(nonnull)
259 static inline void *fr_dcursor_tail(fr_dcursor_t *cursor)
260 {
261  /*
262  * Keep calling next on the custom iterator
263  * until we hit the end of the list.
264  */
265  if (cursor->iter) {
266  void *current = cursor->current;
267 
268  while ((cursor->current = dcursor_next(cursor, cursor->iter, cursor->current))) {
269  current = cursor->current;
270  }
271 
272  return dcursor_current_set(cursor, current);
273  }
274 
275  return dcursor_current_set(cursor, fr_dlist_tail(cursor->dlist));
276 }
277 
278 /** Advanced the cursor to the next item
279  *
280  * @param[in] cursor to operate on.
281  * @return
282  * - Next item.
283  * - NULL if the list is empty, or the cursor has advanced past the end of the list.
284  *
285  * @hidecallergraph
286  */
287 CC_HINT(nonnull)
288 static inline void *fr_dcursor_next(fr_dcursor_t *cursor)
289 {
290  return dcursor_current_set(cursor, dcursor_next(cursor, cursor->iter, cursor->current));
291 }
292 
293 /** Return the next iterator item without advancing the cursor
294  *
295  * @param[in] cursor to operate on.
296  * @return
297  * - Next item.
298  * - NULL if the list is empty, or the cursor has advanced past the end of the list.
299  *
300  * @hidecallergraph
301  */
302 CC_HINT(nonnull)
303 static inline void *fr_dcursor_next_peek(fr_dcursor_t *cursor)
304 {
305  return dcursor_next(cursor, cursor->peek, cursor->current);
306 }
307 
308 /** Returns the next list item without advancing the cursor
309  *
310  * @note This returns the next item in the list, which may not match the
311  * next iterator value. It's mostly used for debugging. You probably
312  * want #fr_dcursor_next_peek.
313  *
314  * @param[in] cursor to operator on.
315  * @return
316  * - Next item in list.
317  * - NULL if the list is empty, or the cursor has advanced past the end of the list.
318  *
319  * @hidecallergraph
320  */
321 CC_HINT(nonnull)
322 static inline void *fr_dcursor_list_next_peek(fr_dcursor_t *cursor)
323 {
324  return dcursor_next(cursor, NULL, cursor->current);
325 }
326 
327 /** Return the item the cursor current points to
328  *
329  * @param[in] cursor to operate on.
330  * @return
331  * - The item the cursor currently points to.
332  * - NULL if the list is empty, or the cursor has advanced past the end of the list.
333  *
334  * @hidecallergraph
335  */
336 CC_HINT(nonnull)
337 static inline void *fr_dcursor_current(fr_dcursor_t *cursor)
338 {
339  VALIDATE(cursor->current);
340  return cursor->current;
341 }
342 
343 /** Set the cursor to a specified item
344  *
345  * @param[in] cursor to operate on.
346  * @param[in] item to point the cursor at
347  * @return
348  * - item cursor points at.
349  * - NULL if the list is empty.
350  *
351  * @hidecallergraph
352  */
353 static inline void *fr_dcursor_set_current(fr_dcursor_t *cursor, void *item)
354 {
355  if (!fr_cond_assert_msg(!cursor->is_const, "attempting to modify const list")) return NULL;
356 
357  if (!item ||
358  !fr_dlist_in_list(cursor->dlist, item) ||
359  (cursor->iter && !cursor->iter(cursor->dlist, item, cursor->iter_uctx))) return NULL;
360 
361  return dcursor_current_set(cursor, item);
362 }
363 
364 /** Insert a single item at the start of the list
365  *
366  * @note Will not advance cursor position to r attribute, but will set cursor
367  * to this attribute, if it's the head one in the list.
368  *
369  * Insert a void at the start of the list.
370  *
371  * @param cursor to operate on.
372  * @param v to insert.
373  *
374  * @hidecallergraph
375  */
376 static inline int fr_dcursor_prepend(fr_dcursor_t *cursor, void *v)
377 {
378  int ret;
379 
380  if (!fr_cond_assert_msg(!cursor->is_const, "attempting to modify const list")) return -1;
381 
382  VALIDATE(v);
383 
384  if (cursor->insert) if ((ret = cursor->insert(cursor->dlist, v, cursor->mod_uctx)) < 0) return ret;
385 
386  /*
387  * Insert at the head of the list
388  */
389  fr_dlist_insert_head(cursor->dlist, v);
390 
391  return 0;
392 }
393 
394 /** Insert a single item at the end of the list
395  *
396  * @note Does not change the current pointer.
397  *
398  * @param[in] cursor to operate on.
399  * @param[in] v to insert.
400  * @return
401  * - 0 on success.
402  * - -1 if the insert callback failed or a modification was attempted on a const'd list.
403  *
404  * @hidecallergraph
405  */
406 static inline int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
407 {
408  int ret;
409 
410  if (!fr_cond_assert_msg(!cursor->is_const, "attempting to modify const list")) return -1;
411 
412  VALIDATE(v);
413 
414  if (cursor->insert) if ((ret = cursor->insert(cursor->dlist, v, cursor->mod_uctx)) < 0) return ret;
415 
416  fr_dlist_insert_tail(cursor->dlist, v);
417 
418  cursor->at_end = false; /* Can't be at the end if we just inserted something */
419 
420  return 0;
421 }
422 
423 /** Insert directly after the current item
424  *
425  * @note Does not change the current pointer.
426  *
427  * @param[in] cursor to operate on.
428  * @param[in] v Item to insert.
429  * @return
430  * - 0 on success.
431  * - -1 if the insert callback failed or a modification was attempted on a const'd list.
432  *
433  * @hidecallergraph
434  */
435 static inline int fr_dcursor_insert(fr_dcursor_t *cursor, void *v)
436 {
437  int ret;
438 
439  if (!fr_cond_assert_msg(!cursor->is_const, "attempting to modify const list")) return -1;
440 
441  VALIDATE(v);
442 
443  if (!cursor->current) {
444  if (fr_dcursor_append(cursor, v) < 0) return -1;
445  return 0;
446  }
447 
448  if (cursor->insert) if ((ret = cursor->insert(cursor->dlist, v, cursor->mod_uctx)) < 0) return ret;
449 
450  fr_dlist_insert_after(cursor->dlist, cursor->current, v);
451 
452  return 0;
453 }
454 
455 /** Remove the current item
456  *
457  * The current item will be set to the one after the item
458  * being removed. An example check and remove loop:
459  *
460  @code {.c}
461  for (v = fr_dcursor_init(&cursor, head);
462  v;
463  v = fr_dcursor_current(&cursor) {
464  if (<condition>) {
465  v = fr_dcursor_remove(&cursor);
466  talloc_free(v);
467  continue;
468  }
469  v = fr_dcursor_next(&cursor);
470  }
471  @endcode
472  *
473  * @param[in] cursor to remove the current item from.
474  * @return
475  * - item we just removed.
476  * - NULL on error.
477  *
478  * @hidecallergraph
479  */
480 static inline void *fr_dcursor_remove(fr_dcursor_t *cursor)
481 {
482  void *v;
483  int i = 0;
484 
485  if (!fr_cond_assert_msg(!cursor->is_const, "attempting to modify const list")) return NULL;
486 
487  if (!cursor->current) return NULL; /* don't do anything fancy, it's just a noop */
488 
489  v = cursor->current;
490  VALIDATE(v);
491 
492  if (cursor->remove) {
493  i = cursor->remove(cursor->dlist, v, cursor->mod_uctx);
494  if (i < 0) return NULL;
495  }
496 
497  dcursor_current_set(cursor, dcursor_next(cursor, cursor->iter, v));
498 
499  if (i > 0) return v;
500 
501  fr_dlist_remove(cursor->dlist, v);
502 
503  return v;
504 }
505 
506 /** Moves items from one cursor to another.
507  *
508  * Move multiple items from one cursor to another.
509  *
510  * @note Will only move items from the current position of to_append
511  * up to the end of to_append. Items will be removed from the original
512  * cursor. Items will be inserted after the current position of the
513  * destination cursor (which will not be changed).
514  *
515  * @param[in] cursor to operate on.
516  * @param[in] to_append Items to append.
517  *
518  * @hidecallergraph
519  */
520 static inline void fr_dcursor_merge(fr_dcursor_t *cursor, fr_dcursor_t *to_append)
521 {
522  void *v, *p;
523 
524  if (!fr_cond_assert_msg(!cursor->is_const, "dst list in merge is const")) return;
525  if (!fr_cond_assert_msg(!to_append->is_const, "src list in merge is const")) return;
526 
527  p = cursor->current;
528  while ((v = fr_dcursor_remove(to_append))) {
529  fr_dcursor_insert(cursor, v);
530  dcursor_current_set(cursor, v);
531  }
532  dcursor_current_set(cursor, p);
533 }
534 
535 /** Return the next item, skipping the current item, that satisfies an evaluation function.
536  *
537  * @param[in] cursor to operate on
538  * @param[in] eval evaluation function
539  * @param[in] uctx context for the evaluation function
540  * @return the next item satisfying eval, or NULL if no such item exists
541  *
542  * @hidecallergraph
543  */
544 CC_NO_UBSAN(function) /* UBSAN: false positive - Type specific dcursor pointer trips --fasanitize=function */
545 static inline void *fr_dcursor_filter_next(fr_dcursor_t *cursor, fr_dcursor_eval_t eval, void const *uctx)
546 {
547  void *item;
548 
549  do {
550  item = fr_dcursor_next(cursor);
551  } while (item && !eval(item, uctx));
552 
553  return item;
554 }
555 
556 /** Return the first item that satisfies an evaluation function.
557  *
558  * @param[in] cursor to operate on
559  * @param[in] eval evaluation function
560  * @param[in] uctx context for the evaluation function
561  * @return the first item satisfying eval, or NULL if no such item exists
562  *
563  * @hidecallergraph
564  */
565 CC_NO_UBSAN(function) /* UBSAN: false positive - Type specific dcursor pointer trips --fasanitize=function */
566 static inline void *fr_dcursor_filter_head(fr_dcursor_t *cursor, fr_dcursor_eval_t eval, void const *uctx)
567 {
568  void *item;
569 
570  item = fr_dcursor_head(cursor);
571  if (eval(item, uctx)) return item;
572 
573  return fr_dcursor_filter_next(cursor, eval, uctx);
574 }
575 
576 /** Return the next item, starting with the current item, that satisfies an evaluation function.
577  *
578  * @param[in] cursor to operate on
579  * @param[in] eval evaluation function
580  * @param[in] uctx context for the evaluation function
581  * @return the next item satisfying eval, or NULL if no such item exists
582  *
583  * @hidecallergraph
584  */
585  CC_NO_UBSAN(function) /* UBSAN: false positive - Type specific dcursor pointer trips --fasanitize=function */
586 static inline void *fr_dcursor_filter_current(fr_dcursor_t *cursor, fr_dcursor_eval_t eval, void const *uctx)
587 {
588  void *item;
589 
590  while ((item = fr_dcursor_current(cursor)) && !eval(item, uctx)) {
591  fr_dcursor_next(cursor);
592  }
593 
594  return item;
595 }
596 
597 /** Replace the current item
598  *
599  * After replacing the current item, the cursor will be rewound,
600  * and the next item selected by the iterator function will become current.
601  *
602  * @param[in] cursor to replace the current item in.
603  * @param[in] r item to insert.
604  * @return
605  * - item we just replaced.
606  * - NULL on error.
607  *
608  * @hidecallergraph
609  */
610 static inline void *fr_dcursor_replace(fr_dcursor_t *cursor, void *r)
611 {
612  void *v;
613 
614  if (!fr_cond_assert_msg(!cursor->is_const, "attempting to modify const list")) return NULL;
615 
616  VALIDATE(r);
617 
618  /*
619  * Correct behaviour here is debatable
620  */
621  if (fr_dlist_empty(cursor->dlist)) {
622  fr_dcursor_prepend(cursor, r);
623  return NULL;
624  }
625 
626  /*
627  * If there's a head, but no current,
628  * we've iterated off the end of the list,
629  * so the replace becomes an append.
630  */
631  v = cursor->current;
632  if (!v) {
633  fr_dcursor_append(cursor, r);
634  return NULL;
635  }
636 
637  if (cursor->remove) if (cursor->remove(cursor->dlist, v, cursor->mod_uctx) < 0) return NULL;
638 
639  fr_dlist_replace(cursor->dlist, cursor->current, r);
640 
641  /*
642  * Fixup current pointer.
643  */
644  if (cursor->iter) {
645  dcursor_current_set(cursor, cursor->iter(cursor->dlist, r, cursor->iter_uctx)); /* Verify r matches */
646  } else {
647  dcursor_current_set(cursor, r); /* Current becomes replacement */
648  }
649 
650  return v;
651 }
652 
653 /** Free the current item and all items after it
654  *
655  * @note Use fr_dcursor_remove and talloc_free to free single items.
656  *
657  * Current should be the item *after* the one freed.
658  *
659  * @param[in] cursor to free items in.
660  *
661  * @hidecallergraph
662  */
663 static inline void fr_dcursor_free_list(fr_dcursor_t *cursor)
664 {
665  void *v;
666 
667  if (fr_dlist_empty(cursor->dlist)) return; /* noop */
668 
669  do {
670  v = fr_dcursor_remove(cursor);
671  talloc_free(v);
672  } while (v);
673 }
674 
675 /** Initialise a cursor with a custom iterator
676  *
677  * @param[in] _cursor to initialise.
678  * @param[in] _list to iterate over.
679  * @param[in] _iter function.
680  * @param[in] _peek function. If NULL _iter will be used for peeking.
681  * @param[in] _iter_uctx _iter function _uctx.
682  * @param[in] _insert function.
683  * @param[in] _remove function.
684  * @param[in] _mod_uctx _insert and _remove function _uctx.
685  * @return
686  * - NULL if _head does not point to any items, or the iterator matches no items
687  * in the current list.
688  * - The first item returned by the iterator.
689  */
690 #define fr_dcursor_iter_mod_init(_cursor, _list, _iter, _peek, _iter_uctx, _insert, _remove, _mod_uctx) \
691  _fr_dcursor_init(_cursor, \
692  _list, \
693  _iter, \
694  _peek, \
695  _iter_uctx, \
696  _insert, \
697  _remove, \
698  _mod_uctx, \
699  IS_CONST(fr_dlist_head_t *, _list))
700 
701 /** Initialise a cursor with a custom iterator
702  *
703  * @param[in] _cursor to initialise.
704  * @param[in] _head of item list.
705  * @param[in] _iter function.
706  * @param[in] _peek function. If NULL _iter will be used for peeking.
707  * @param[in] _uctx _iter function _uctx.
708  * @return
709  * - NULL if _head does not point to any items, or the iterator matches no items
710  * in the current list.
711  * - The first item returned by the iterator.
712  */
713 #define fr_dcursor_iter_init(_cursor, _head, _iter, _peek, _uctx) \
714  _fr_dcursor_init(_cursor, \
715  _head, \
716  _iter, \
717  _peek, \
718  _uctx, \
719  NULL, \
720  NULL, \
721  NULL, \
722  IS_CONST(fr_dlist_head_t *, _head))
723 
724 /** Initialise a cursor
725  *
726  * @param[in] _cursor to initialise.
727  * @param[in] _head of item list.
728  * @return
729  * - NULL if _head does not point to any items.
730  * - The first item in the list.
731  */
732 #define fr_dcursor_init(_cursor, _head) \
733  _fr_dcursor_init(_cursor, \
734  _head, \
735  NULL, \
736  NULL, \
737  NULL, \
738  NULL, \
739  NULL, \
740  NULL, \
741  IS_CONST(fr_dlist_head_t *, _head))
742 
743 /** Setup a cursor to iterate over attribute items in dlists
744  *
745  * @param[in] cursor Where to initialise the cursor (uses existing structure).
746  * @param[in] head of dlist.
747  * @param[in] iter Iterator callback.
748  * @param[in] peek Iterator callback that should not modify iterator state.
749  * @param[in] iter_uctx to pass to iterator function.
750  * @param[in] insert Callback for inserts.
751  * @param[in] remove Callback for removals.
752  * @param[in] mod_uctx to pass to modification functions.
753  * @param[in] is_const Don't allow modification of the underlying list.
754  * @return the attribute pointed to by v.
755  *
756  * @hidecallergraph
757  */
758 static inline CC_HINT(nonnull(1,2))
760  fr_dcursor_iter_t iter, fr_dcursor_iter_t peek, void const *iter_uctx,
761  fr_dcursor_insert_t insert, fr_dcursor_remove_t remove, void const *mod_uctx, bool is_const)
762 {
763  *cursor = (fr_dcursor_t){
764  .dlist = UNCONST(fr_dlist_head_t *, head),
765  .iter = iter,
766  .peek = peek ? peek : iter,
767  .iter_uctx = UNCONST(void *, iter_uctx),
768  .insert = insert,
769  .remove = remove,
770  .mod_uctx = UNCONST(void *, mod_uctx),
771  .is_const = is_const
772  };
773  if (!fr_dlist_empty(cursor->dlist)) return fr_dcursor_next(cursor); /* Initialise current */
774 
775  if (iter) return fr_dcursor_next(cursor); /* An iterator may do something, even on an empty list */
776 
777  return NULL;
778 }
779 
780 /** re-initialise a cursor, changing its list
781  *
782  * @param[in] _cursor to re-initialise.
783  * @param[in] _head of item list.
784  * @return
785  * - NULL if _head does not point to any items.
786  * - The first item in the list.
787  */
788 #define fr_dcursor_reinit(_cursor, _head) \
789  _fr_dcursor_reinit(_cursor, \
790  _head, \
791  IS_CONST(fr_dlist_head_t *, _head))
792 
793 static inline CC_HINT(nonnull(1,2))
794 void _fr_dcursor_list_reinit(fr_dcursor_t *cursor, fr_dlist_head_t const *head, bool is_const)
795 {
796  cursor->dlist = UNCONST(fr_dlist_head_t *, head);
797  cursor->current = NULL;
798  cursor->is_const = is_const;
799 }
800 
801 /** talloc_free the current item
802  *
803  * @param[in] cursor to free items from.
804  */
805 static inline void fr_dcursor_free_item(fr_dcursor_t *cursor)
806 {
807  if (!cursor) return;
808 
810 }
811 
812 /** Allocate a stack of cursors for traversing trees
813  *
814  * @param[in] ctx to allocate the cursor stack in.
815  * @param[in] depth Maximum depth of the cursor stack.
816  * @return
817  * - A new cursor stack.
818  * - NULL on error.
819  */
820 static inline fr_dcursor_stack_t *fr_dcursor_stack_alloc(TALLOC_CTX *ctx, uint8_t depth)
821 {
823 
824  stack = talloc_array_size(ctx, sizeof(fr_dcursor_stack_t) + (sizeof(fr_dcursor_t) * depth), 1);
825  if (unlikely(!stack)) return NULL;
826 
827  talloc_set_name_const(stack, "fr_dcursor_stack_t");
828 
829  return stack;
830 }
831 
833 
835 
836 DIAG_OFF(unused-function)
837 
838 /** Expands to the type name used for the dcursor wrapper structure
839  *
840  * @param[in] _name Prefix we add to type-specific structures.
841  * @return ``<name>_dcursor_t``
842  */
843 #define FR_DCURSOR(_name) _name ## _dcursor_t
844 
845 /** Expands to the type name used for the dcursor iterator type
846  *
847  * @param[in] _name Prefix we add to type-specific structures.
848  * @return ``<name>_iter_t``
849  */
850 #define FR_DCURSOR_ITER(_name) _name ## _iter_t
851 
852 /** Expands to the type name used for the dcursor evaluator type
853  *
854  * @param[in] _name Prefix we add to type-specific structures.
855  * @return ``<name>_eval_t``
856  */
857 #define FR_DCURSOR_EVAL(_name) _name ## _eval_t
858 
859 /** Expands to the type name used for the dcursor insert function type
860  *
861  * @param[in] _name Prefix we add to type-specific structures.
862  * @return ``<name>_insert_t``
863  */
864 #define FR_DCURSOR_INSERT(_name) _name ## _insert_t
865 
866 /** Expands to the type name used for the dcursor remove function type
867  *
868  * @param[in] _name Prefix we add to type-specific structures.
869  * @return ``<name>_remove_t``
870  */
871 #define FR_DCURSOR_REMOVE(_name) _name ## _remove_t
872 
873 /** Expands to the type name used for the dcursor copy function type
874  *
875  * @param[in] _name Prefix we add to type-specific structures.
876  * @return ``<name>_copy_t``
877  */
878 #define FR_DCURSOR_COPY(_name) _name ## _copy_t
879 
880 /** Define type specific wrapper structs for dcursors
881  *
882  * @param[in] _name Prefix we add to type-specific structures.
883  * @param[in] _list_name The identifier used for type qualifying dlists.
884  * Should be the same as that use for
885  * - #FR_DLIST_HEAD
886  * - #FR_DLIST_ENTRY
887  * - #FR_DLIST_TYPES
888  * - #FR_DLIST_FUNCS
889  * @param[in] _element_type Type of element in the dlists.
890  *
891  * @note This macro should be used inside the header for the area of code
892  * which will use type specific functions.
893  */
894 #define FR_DCURSOR_DLIST_TYPES(_name, _list_name, _element_type) \
895  typedef struct { fr_dcursor_t dcursor; } FR_DCURSOR(_name); \
896  typedef _element_type *(*FR_DCURSOR_ITER(_name))(FR_DLIST_HEAD(_list_name) *list, _element_type *to_eval, void *uctx); \
897  typedef bool (*FR_DCURSOR_EVAL(_name))(_element_type const *item, void const *uctx); \
898  typedef int (*FR_DCURSOR_INSERT(_name))(FR_DLIST_HEAD(_list_name) *list, FR_DLIST_ENTRY(_list_name) *to_insert, void *uctx); \
899  typedef int (*FR_DCURSOR_REMOVE(_name))(FR_DLIST_HEAD(_list_name) *list, FR_DLIST_ENTRY(_list_name) *to_delete, void *uctx); \
900  typedef void (*FR_DCURSOR_COPY(_name))(FR_DCURSOR(_name) *out, FR_DCURSOR(_name) const *in);
901 
902 /** Define type specific wrapper functions for dcursors
903  *
904  * @note This macro should be used inside the source file that will use
905  * the type specific functions.
906  *
907  * @param[in] _name Prefix we add to type-specific dcursor functions.
908  * @param[in] _list_name Prefix for type-specific dlist used by this dcursor.
909  * @param[in] _element_type Type of structure that'll be inserted into the dlist and returned by the dcursor.
910  */
911 #define FR_DCURSOR_FUNCS(_name, _list_name, _element_type) \
912 DIAG_OFF(unused-function) \
913  static inline CC_HINT(nonnull) _element_type *_name ## _init(FR_DCURSOR(_name) *dcursor, \
914  FR_DLIST_HEAD(_list_name) *head) \
915  { return (_element_type *)_fr_dcursor_init(&dcursor->dcursor, &head->head, \
916  NULL, NULL, NULL, NULL, NULL, NULL, \
917  IS_CONST(FR_DLIST_HEAD(_list_name) *, head)); } \
918 \
919  static inline CC_HINT(nonnull(1,2)) _element_type *_name ## _iter_init(FR_DCURSOR(_name) *dcursor, \
920  FR_DLIST_HEAD(_list_name) *head, \
921  FR_DCURSOR_ITER(_name) iter, \
922  FR_DCURSOR_ITER(_name) peek, \
923  void const *iter_uctx) \
924  { return (_element_type *)_fr_dcursor_init(&dcursor->dcursor, &head->head, \
925  (fr_dcursor_iter_t)iter, \
926  (fr_dcursor_iter_t)peek, \
927  iter_uctx, \
928  NULL, NULL, NULL, \
929  IS_CONST(FR_DLIST_HEAD(_list_name) *, head)); } \
930 \
931  static inline CC_HINT(nonnull(1,2)) _element_type *_name ## _iter_mod_init(FR_DCURSOR(_name) *dcursor, \
932  FR_DLIST_HEAD(_list_name) *head, \
933  FR_DCURSOR_ITER(_name) iter, \
934  FR_DCURSOR_ITER(_name) peek, \
935  void const *iter_uctx, \
936  FR_DCURSOR_INSERT(_name) insert, \
937  FR_DCURSOR_REMOVE(_name) remove, \
938  void const *mod_uctx) \
939  { return (_element_type *)_fr_dcursor_init(&dcursor->dcursor, &head->head, \
940  (fr_dcursor_iter_t)iter, \
941  (fr_dcursor_iter_t)peek, \
942  iter_uctx, \
943  (fr_dcursor_insert_t)insert, \
944  (fr_dcursor_remove_t)remove, \
945  mod_uctx, \
946  IS_CONST(FR_DLIST_HEAD(_list_name) *, head)); } \
947 \
948  static inline CC_HINT(nonnull) _element_type *_name ## _current(FR_DCURSOR(_name) *dcursor) \
949  { return (_element_type *)fr_dcursor_current(&dcursor->dcursor); } \
950 \
951  static inline CC_HINT(nonnull) _element_type *_name ## _next_peek(FR_DCURSOR(_name) *dcursor) \
952  { return (_element_type *)fr_dcursor_next_peek(&dcursor->dcursor); } \
953 \
954  static inline CC_HINT(nonnull) _element_type *_name ## _list_next_peek(FR_DCURSOR(_name) *dcursor) \
955  { return (_element_type *)fr_dcursor_list_next_peek(&dcursor->dcursor); } \
956 \
957  static inline CC_HINT(nonnull) _element_type *_name ## _next(FR_DCURSOR(_name) *dcursor) \
958  { return (_element_type *)fr_dcursor_next(&dcursor->dcursor); } \
959 \
960  static inline CC_HINT(nonnull) void _name ## _copy(FR_DCURSOR(_name) *out, \
961  FR_DCURSOR(_name) const *in) \
962  { fr_dcursor_copy(&out->dcursor, &in->dcursor); } \
963 \
964  static inline CC_HINT(nonnull) _element_type *_name ## _head(FR_DCURSOR(_name) *dcursor) \
965  { return (_element_type *)fr_dcursor_head(&dcursor->dcursor); } \
966 \
967  static inline CC_HINT(nonnull) _element_type *_name ## _tail(FR_DCURSOR(_name) *dcursor) \
968  { return (_element_type *)fr_dcursor_tail(&dcursor->dcursor); } \
969 \
970  static inline CC_HINT(nonnull) _element_type *_name ## _set_current(FR_DCURSOR(_name) *dcursor, \
971  _element_type *v) \
972  { return (_element_type *)fr_dcursor_set_current(&dcursor->dcursor, v); } \
973 \
974  static inline CC_HINT(nonnull) int _name ## _prepend(FR_DCURSOR(_name) *dcursor, \
975  _element_type *v) \
976  { return fr_dcursor_prepend(&dcursor->dcursor, v); } \
977 \
978  static inline CC_HINT(nonnull) int _name ## _append(FR_DCURSOR(_name) *dcursor, \
979  _element_type *v) \
980  { return fr_dcursor_append(&dcursor->dcursor, v); } \
981 \
982  static inline CC_HINT(nonnull) int _name ## _insert(FR_DCURSOR(_name) *dcursor, \
983  _element_type *v) \
984  { return fr_dcursor_insert(&dcursor->dcursor, v); } \
985 \
986  static inline CC_HINT(nonnull) _element_type *_name ## _replace(FR_DCURSOR(_name) *dcursor, \
987  _element_type *v) \
988  { return fr_dcursor_replace(&dcursor->dcursor, v); } \
989 \
990  static inline CC_HINT(nonnull) _element_type *_name ## _remove(FR_DCURSOR(_name) *dcursor) \
991  { return (_element_type *)fr_dcursor_remove(&dcursor->dcursor); } \
992 \
993  static inline CC_HINT(nonnull) void _name ## _merge(FR_DCURSOR(_name) *cursor, \
994  FR_DCURSOR(_name) *to_append) \
995  { fr_dcursor_merge(&cursor->dcursor, &to_append->dcursor); } \
996 \
997  static inline CC_HINT(nonnull) void _name ## _free_list(FR_DCURSOR(_name) *dcursor) \
998  { fr_dcursor_free_list(&dcursor->dcursor); } \
999 \
1000  static inline CC_HINT(nonnull) void _name ## _free_item(FR_DCURSOR(_name) *dcursor) \
1001  { fr_dcursor_free_item(&dcursor->dcursor); } \
1002 \
1003  static inline CC_HINT(nonnull) _element_type *_name ## _intersect_head(FR_DCURSOR(_name) *a, \
1004  FR_DCURSOR(_name) *b) \
1005  { return (_element_type *)fr_dcursor_intersect_head(&a->dcursor, &b->dcursor); } \
1006 \
1007  static inline CC_HINT(nonnull) _element_type *_name ## _intersect_next(FR_DCURSOR(_name) *a, \
1008  FR_DCURSOR(_name) *b) \
1009  { return (_element_type *)fr_dcursor_intersect_next(&a->dcursor, &b->dcursor); } \
1010 \
1011  static inline CC_HINT(nonnull(1,2)) _element_type *_name ## _filter_head(FR_DCURSOR(_name) *dcursor, \
1012  FR_DCURSOR_EVAL(_name) eval, \
1013  void const *uctx) \
1014  { return (_element_type *)fr_dcursor_filter_head(&dcursor->dcursor, (fr_dcursor_eval_t)eval, uctx); } \
1015 \
1016  static inline CC_HINT(nonnull(1,2)) _element_type *_name ## _filter_next(FR_DCURSOR(_name) *dcursor, \
1017  FR_DCURSOR_EVAL(_name) eval, \
1018  void const *uctx) \
1019  { return (_element_type *)fr_dcursor_filter_next(&dcursor->dcursor, (fr_dcursor_eval_t)eval, uctx); } \
1020 \
1021  static inline CC_HINT(nonnull(1,2)) _element_type *_name ## _filter_current(FR_DCURSOR(_name) *dcursor, \
1022  FR_DCURSOR_EVAL(_name) eval, \
1023  void const *uctx) \
1024  { return (_element_type *)fr_dcursor_filter_current(&dcursor->dcursor, (fr_dcursor_eval_t)eval, uctx); }
1025 
1026 DIAG_ON(unused-function)
1027 
1028 #ifdef __cplusplus
1029 }
1030 #endif
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition: build.h:165
#define DIAG_ON(_x)
Definition: build.h:456
#define RCSIDH(h, id)
Definition: build.h:482
#define unlikely(_x)
Definition: build.h:379
#define DIAG_OFF(_x)
Definition: build.h:455
static void * fr_dcursor_remove(fr_dcursor_t *cursor)
Remove the current item.
Definition: dcursor.h:480
fr_dcursor_iter_t iter
Iterator function.
Definition: dcursor.h:97
uint8_t depth
Which cursor is currently in use.
Definition: dcursor.h:113
static void * fr_dcursor_list_next_peek(fr_dcursor_t *cursor)
Returns the next list item without advancing the cursor.
Definition: dcursor.h:322
int(* fr_dcursor_remove_t)(fr_dlist_head_t *list, void *to_delete, void *uctx)
Callback for performing additional actions on removal.
Definition: dcursor.h:74
void(* fr_dcursor_copy_t)(fr_dcursor_t *out, fr_dcursor_t const *in)
Copy callback for duplicating complex dcursor state.
Definition: dcursor.h:81
static void * _fr_dcursor_init(fr_dcursor_t *cursor, fr_dlist_head_t const *head, fr_dcursor_iter_t iter, fr_dcursor_iter_t peek, void const *iter_uctx, fr_dcursor_insert_t insert, fr_dcursor_remove_t remove, void const *mod_uctx, bool is_const)
Setup a cursor to iterate over attribute items in dlists.
Definition: dcursor.h:759
return fr_dcursor_filter_next(cursor, eval, uctx)
fr_dcursor_remove_t remove
Callback function on delete.
Definition: dcursor.h:103
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition: dcursor.h:406
static void fr_dcursor_copy(fr_dcursor_t *out, fr_dcursor_t const *in)
Copy cursor parameters and state.
Definition: dcursor.h:192
static void fr_dcursor_merge(fr_dcursor_t *cursor, fr_dcursor_t *to_append)
Moves items from one cursor to another.
Definition: dcursor.h:520
static void * fr_dcursor_tail(fr_dcursor_t *cursor)
Wind cursor to the tail item in the list.
Definition: dcursor.h:259
bool at_end
We're at the end of the list.
Definition: dcursor.h:109
bool(* fr_dcursor_eval_t)(void const *item, void const *uctx)
Type of evaluation functions to pass to the fr_dcursor_filter_*() functions.
Definition: dcursor.h:91
static int fr_dcursor_insert(fr_dcursor_t *cursor, void *v)
Insert directly after the current item.
Definition: dcursor.h:435
static void * fr_dcursor_next_peek(fr_dcursor_t *cursor)
Return the next iterator item without advancing the cursor.
Definition: dcursor.h:303
void * mod_uctx
to pass to modification functions.
Definition: dcursor.h:104
static void * dcursor_current_set(fr_dcursor_t *cursor, void *current)
If current is set to a NULL pointer, we record that fact.
Definition: dcursor.h:127
#define VALIDATE(_item)
Definition: dcursor.h:118
static void fr_dcursor_free_item(fr_dcursor_t *cursor)
talloc_free the current item
Definition: dcursor.h:805
static void * fr_dcursor_set_current(fr_dcursor_t *cursor, void *item)
Set the cursor to a specified item.
Definition: dcursor.h:353
fr_dcursor_insert_t insert
Callback function on insert.
Definition: dcursor.h:102
next
Definition: dcursor.h:178
fr_dcursor_eval_t eval
Definition: dcursor.h:545
struct fr_dcursor_s fr_dcursor_t
Definition: dcursor.h:38
int(* fr_dcursor_insert_t)(fr_dlist_head_t *list, void *to_insert, void *uctx)
Callback for performing additional actions on insert.
Definition: dcursor.h:62
static void fr_dcursor_free_list(fr_dcursor_t *cursor)
Free the current item and all items after it.
Definition: dcursor.h:663
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
void * current
The current item in the dlist.
Definition: dcursor.h:95
void *(* fr_dcursor_iter_t)(fr_dlist_head_t *list, void *to_eval, void *uctx)
Callback for implementing custom iterators.
Definition: dcursor.h:51
fr_dcursor_iter_t void * current
Definition: dcursor.h:148
CC_NO_UBSAN(function) static inline void *dcursor_next(fr_dcursor_t *cursor
Internal function to get the next item.
void * fr_dcursor_intersect_next(fr_dcursor_t *a, fr_dcursor_t *b)
Return the next item matching the iterator in cursor a and cursor b.
Definition: dcursor.c:76
fr_dlist_head_t * dlist
Head of the doubly linked list being iterated over.
Definition: dcursor.h:94
fr_dcursor_iter_t iter
Definition: dcursor.h:147
static int fr_dcursor_prepend(fr_dcursor_t *cursor, void *v)
Insert a single item at the start of the list.
Definition: dcursor.h:376
void * fr_dcursor_intersect_head(fr_dcursor_t *a, fr_dcursor_t *b)
Return the first item matching the iterator in cursor a and cursor b.
Definition: dcursor.c:47
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:288
static void * fr_dcursor_replace(fr_dcursor_t *cursor, void *r)
Replace the current item.
Definition: dcursor.h:610
void * iter_uctx
to pass to iterator function.
Definition: dcursor.h:100
static void _fr_dcursor_list_reinit(fr_dcursor_t *cursor, fr_dlist_head_t const *head, bool is_const)
Definition: dcursor.h:794
fr_dcursor_iter_t peek
Distinct "peek" function.
Definition: dcursor.h:98
static void fr_dcursor_copy_iter(fr_dcursor_t *out, fr_dcursor_t const *in)
Copy a read-only iterator from a parent to a child cursor.
Definition: dcursor.h:207
static fr_dcursor_stack_t * fr_dcursor_stack_alloc(TALLOC_CTX *ctx, uint8_t depth)
Allocate a stack of cursors for traversing trees.
Definition: dcursor.h:820
return item
Definition: dcursor.h:553
fr_dcursor_copy_t copy
Copy dcursor state.
Definition: dcursor.h:106
static void * fr_dcursor_head(fr_dcursor_t *cursor)
Rewind cursor to the start of the list.
Definition: dcursor.h:234
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition: dcursor.h:337
bool is_const
The list we're iterating over is immutable.
Definition: dcursor.h:108
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:156
static fr_slen_t in
Definition: dict.h:821
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 bool fr_dlist_in_list(fr_dlist_head_t *list_head, void *ptr)
Check if a list entry is part of a list.
Definition: dlist.h:320
static void * fr_dlist_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 void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition: dlist.h:638
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 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
Head of a doubly linked list.
Definition: dlist.h:51
talloc_free(reap)
static char * stack[MAX_STACK]
Definition: radmin.c:158
unsigned char bool
Definition: merged_model.c:19
unsigned char uint8_t
Definition: merged_model.c:30
static uint8_t depth(fr_minmax_heap_index_t i)
Definition: minmax_heap.c:83
fr_assert(0)
static fr_slen_t head
Definition: xlat.h:406
int nonnull(2, 5))
static size_t char ** out
Definition: value.h:997