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