The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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 */
23RCSIDH(dcursor_h, "$Id: 97e8e2cbdc04e98f106f8e70211cccd07ea8a77d $")
24
25#ifdef __cplusplus
26extern "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
37DIAG_OFF(unused-function)
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 */
51typedef 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 */
62typedef 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 */
74typedef 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 */
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 */
91typedef bool (*fr_dcursor_eval_t)(void const *item, void const *uctx);
92
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
112typedef 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 */
127static inline void *dcursor_current_set(fr_dcursor_t *cursor, void *current)
128{
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 */
146CC_NO_UBSAN(function) /* UBSAN: false positive - Type specific dcursor pointer trips --fasanitize=function */
147static 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);
179 VALIDATE(next);
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 */
191CC_HINT(nonnull)
192static 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 */
206CC_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 */
233CC_HINT(nonnull)
234static 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 */
258CC_HINT(nonnull)
259static 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 */
287CC_HINT(nonnull)
288static 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 */
302CC_HINT(nonnull)
303static 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 */
321CC_HINT(nonnull)
322static 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 */
336CC_HINT(nonnull)
337static 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 */
353static 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 */
376static 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 */
406static 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 */
435static 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 */
480static 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 */
520static 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 */
544CC_NO_UBSAN(function) /* UBSAN: false positive - Type specific dcursor pointer trips --fasanitize=function */
545static 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 */
565CC_NO_UBSAN(function) /* UBSAN: false positive - Type specific dcursor pointer trips --fasanitize=function */
566static 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 */
586static 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 */
610static 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 */
663static 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 */
758static 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
793static inline CC_HINT(nonnull(1,2))
794void _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 */
805static 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 */
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
836DIAG_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) \
912DIAG_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
1026DIAG_ON(unused-function)
1027
1028#ifdef __cplusplus
1029}
1030#endif
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:167
#define DIAG_ON(_x)
Definition build.h:458
#define RCSIDH(h, id)
Definition build.h:484
#define CC_NO_UBSAN(_sanitize)
Definition build.h:426
#define unlikely(_x)
Definition build.h:381
#define DIAG_OFF(_x)
Definition build.h:457
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_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
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:586
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
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_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:566
static void * fr_dcursor_list_next_peek(fr_dcursor_t *cursor)
Returns the next list item without advancing the cursor.
Definition dcursor.h:322
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
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
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
static void * fr_dcursor_next_peek(fr_dcursor_t *cursor)
Return the next iterator item without advancing the cursor.
Definition dcursor.h:303
bool at_end
We're at the end of the list.
Definition dcursor.h:109
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:147
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
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 void * fr_dcursor_replace(fr_dcursor_t *cursor, void *r)
Replace the current item.
Definition dcursor.h:610
static int fr_dcursor_insert(fr_dcursor_t *cursor, void *v)
Insert directly after the current item.
Definition dcursor.h:435
void * mod_uctx
to pass to modification functions.
Definition dcursor.h:104
static void * fr_dcursor_set_current(fr_dcursor_t *cursor, void *item)
Set the cursor to a specified item.
Definition dcursor.h:353
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:545
#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
fr_dcursor_insert_t insert
Callback function on insert.
Definition dcursor.h:102
void *(* fr_dcursor_iter_t)(fr_dlist_head_t *list, void *to_eval, void *uctx)
Callback for implementing custom iterators.
Definition dcursor.h:51
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_remove(fr_dcursor_t *cursor)
Remove the current item.
Definition dcursor.h:480
static void fr_dcursor_free_list(fr_dcursor_t *cursor)
Free the current item and all items after it.
Definition dcursor.h:663
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:337
static void * fr_dcursor_head(fr_dcursor_t *cursor)
Rewind cursor to the start of the list.
Definition dcursor.h:234
void * current
The current item in the dlist.
Definition dcursor.h:95
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:376
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 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
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
fr_dcursor_copy_t copy
Copy dcursor state.
Definition dcursor.h:106
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:824
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_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:638
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 bool fr_dlist_empty(fr_dlist_head_t const *list_head)
Check whether a list has any items.
Definition dlist.h:501
static void * fr_dlist_tail(fr_dlist_head_t const *list_head)
Return the TAIL item of a list or NULL if the list is empty.
Definition dlist.h:531
static void * fr_dlist_replace(fr_dlist_head_t *list_head, void *item, void *ptr)
Replace an item in a dlist.
Definition dlist.h:706
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:378
static int fr_dlist_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_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:555
static int fr_dlist_insert_after(fr_dlist_head_t *list_head, void *pos, void *ptr)
Insert an item after an item already in the list.
Definition dlist.h:414
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 uint8_t
static uint8_t depth(fr_minmax_heap_index_t i)
Definition minmax_heap.c:83
#define fr_assert(_expr)
Definition rad_assert.h:38
static rc_request_t * current
static fr_slen_t head
Definition xlat.h:422
int nonnull(2, 5))
static size_t char ** out
Definition value.h:997