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