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: ca354f89d452123ef95438fb0638dd6efe842876 $")
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
35DIAG_OFF(unused-function)
37
38/** Callback for implementing custom iterators
39 *
40 * @param[in] cursor the cursor
41 * @param[in] to_eval the next item in the list. Iterator should check to
42 * see if it matches the iterator's filter, and if it doesn't
43 * iterate over the items until one is found that does.
44 * @param[in] uctx passed to #fr_dcursor_init.
45 * @return
46 * - to_eval if to_eval matched, or a subsequent attribute if that matched.
47 * - NULL if no more matching attributes were found.
48 */
49typedef void *(*fr_dcursor_iter_t)(fr_dcursor_t *cursor, void *to_eval, void *uctx);
50
51/** Callback for performing additional actions on insert
52 *
53 * @param[in] cursor the cursor
54 * @param[in] to_insert The item being inserted into the cursor.
55 * @param[in] uctx passed to #fr_dcursor_init.
56 * @return
57 * - 0 on success.
58 * - -1 on failure.
59 */
60typedef int (*fr_dcursor_insert_t)(fr_dcursor_t *cursor, void *to_insert, void *uctx);
61
62/** Callback for performing additional actions on removal
63 *
64 * @param[in] cursor the cursor
65 * @param[in] to_delete The item being removed from the cursor.
66 * @param[in] uctx passed to #fr_dcursor_init.
67 * @return
68 * - 0 on success if the caller should do the list removal.
69 * - 1 on success if the callback has done the list removal.
70 * - -1 on failure.
71 */
72typedef int (*fr_dcursor_remove_t)(fr_dcursor_t *cursor, void *to_delete, void *uctx);
73
74/** Copy callback for duplicating complex dcursor state
75 *
76 * @param[out] out dcursor to copy to.
77 * @param[in] in dcursor to copy from.
78 */
80
81/** Type of evaluation functions to pass to the fr_dcursor_filter_*() functions.
82 *
83 * @param[in] item the item to be evaluated
84 * @param[in] uctx context that may assist with evaluation
85 * @return
86 * - true if the evaluation function is satisfied.
87 * - false if the evaluation function is not satisfied.
88 */
89typedef bool (*fr_dcursor_eval_t)(void const *item, void const *uctx);
90
92 fr_dlist_head_t *dlist; //!< Head of the doubly linked list being iterated over.
93 void *current; //!< The current item in the cursor
94
95 fr_dcursor_iter_t iter; //!< Iterator function.
96 fr_dcursor_iter_t peek; //!< Distinct "peek" function. This is sometimes necessary
97 ///< for iterators with complex state.
98 void *iter_uctx; //!< to pass to iterator function.
99
100 fr_dcursor_insert_t insert; //!< Callback function on insert.
101 fr_dcursor_remove_t remove; //!< Callback function on delete.
102 void *mod_uctx; //!< to pass to modification functions.
103
104 fr_dcursor_copy_t copy; //!< Copy dcursor state.
105
106 bool is_const; //!< The list we're iterating over is immutable.
107 bool at_end; //!< We're at the end of the list.
108};
109
110typedef struct {
111 uint8_t depth; //!< Which cursor is currently in use.
112 fr_dcursor_t cursor[]; //!< Stack of cursors.
114
115#ifndef TALLOC_GET_TYPE_ABORT_NOOP
116#define VALIDATE(_item) do { if (cursor->dlist->type && (_item)) _talloc_get_type_abort(_item, cursor->dlist->type, __location__); } while (0)
117#else
118#define VALIDATE(_item)
119#endif
120
121/** If current is set to a NULL pointer, we record that fact
122 *
123 * This stops us jumping back to the start of the dlist.
124 */
125static inline void *dcursor_current_set(fr_dcursor_t *cursor, void *current)
126{
127 VALIDATE(current);
128
129 cursor->at_end = (current == NULL);
130 cursor->current = current;
131
132 return current;
133}
134
135/** Internal function to get the next item
136 *
137 * @param[in] cursor to operate on.
138 * @param[in] iter function.
139 * @param[in] current attribute.
140 * @return
141 * - The next attribute.
142 * - NULL if no more attributes.
143 */
144CC_NO_UBSAN(function) /* UBSAN: false positive - Type specific dcursor pointer trips --fasanitize=function */
145static inline void *dcursor_next(fr_dcursor_t *cursor, fr_dcursor_iter_t iter, void *current)
146{
147 void *next;
148
149 /*
150 * Fast path without custom iter
151 */
152 if (!iter) {
153 if (likely(current != NULL)) return fr_dlist_next(cursor->dlist, current);
154
155 if (cursor->at_end) return NULL; /* At tail of the list */
156
157 return fr_dlist_head(cursor->dlist);
158 }
159
160 /*
161 * First time next has been called, or potentially
162 * another call after we hit the end of the list.
163 */
164 if (!current) {
165 if (cursor->at_end) return NULL; /* At tail of the list */
166
167 next = iter(cursor, NULL, cursor->iter_uctx);
168 VALIDATE(next);
169 return next;
170 }
171 VALIDATE(current);
172
173 /*
174 * The iterator will advance to the next matching entry.
175 */
176 next = iter(cursor, current, cursor->iter_uctx);
177 VALIDATE(next);
178
179 return next;
180}
181
182/** Copy cursor parameters and state.
183 *
184 * @param[out] out Where to copy the cursor to.
185 * @param[in] in cursor to copy.
186 *
187 * @hidecallergraph
188 */
189CC_HINT(nonnull)
190static inline void fr_dcursor_copy(fr_dcursor_t *out, fr_dcursor_t const *in)
191{
192 memcpy(out, in, sizeof(*out));
193
194 if (in->copy) in->copy(out, in);
195}
196
197/** Copy a read-only iterator from a parent to a child cursor
198 *
199 * @param[out] out Where to copy the cursor to.
200 * @param[in] in cursor to copy.
201 *
202 * @hidecallergraph
203 */
204CC_HINT(nonnull)
206{
207 fr_assert(!out->iter);
208 fr_assert(!out->iter_uctx);
209
210 out->iter = in->iter;
211 out->iter_uctx = in->iter_uctx;
212
213#ifndef NDEBUG
214 /*
215 * The output cursor has to be read-only.
216 */
217 out->insert = NULL;
218 out->remove = NULL;
219 out->mod_uctx = NULL;
220 out->copy = NULL;
221#endif
222}
223
224/** Rewind cursor to the start of the list
225 *
226 * @param[in] cursor to operate on.
227 * @return item at the start of the list.
228 *
229 * @hidecallergraph
230 */
231CC_HINT(nonnull)
232static inline void *fr_dcursor_head(fr_dcursor_t *cursor)
233{
234 /*
235 * If we have a custom iterator, the dlist attribute
236 * may not be in the subset the iterator would
237 * return, so set everything to NULL and have
238 * dcursor_next figure it out.
239 */
240 if (cursor->iter) {
241 cursor->at_end = false; /* reset the flag, else next will just return NULL */
242
243 return dcursor_current_set(cursor, dcursor_next(cursor, cursor->iter, NULL));
244 }
245
246 fr_assert(cursor->dlist);
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 */
257CC_HINT(nonnull)
258static 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 fr_assert(cursor->dlist);
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, 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, 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, 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, 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, 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, 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, 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 fr_assert(!cursor->is_const);
668
669 if (fr_dlist_empty(cursor->dlist)) return; /* noop */
670
671 do {
672 v = fr_dcursor_remove(cursor);
673 talloc_free(v);
674 } while (v);
675}
676
677/** Initialise a cursor with a custom iterator
678 *
679 * @param[in] _cursor to initialise.
680 * @param[in] _head of item list.
681 * @param[in] _iter function.
682 * @param[in] _peek function. If NULL _iter will be used for peeking.
683 * @param[in] _uctx _iter function _uctx.
684 * @return
685 * - NULL if _head does not point to any items, or the iterator matches no items
686 * in the current list.
687 * - The first item returned by the iterator.
688 */
689#define fr_dcursor_iter_init(_cursor, _head, _iter, _peek, _uctx) \
690 _fr_dcursor_init(_cursor, \
691 _head, \
692 _iter, \
693 _peek, \
694 _uctx, \
695 NULL, \
696 NULL, \
697 NULL, \
698 IS_CONST(fr_dlist_head_t *, _head))
699
700/** Initialise a cursor
701 *
702 * @param[in] _cursor to initialise.
703 * @param[in] _head of item list.
704 * @return
705 * - NULL if _head does not point to any items.
706 * - The first item in the list.
707 */
708#define fr_dcursor_init(_cursor, _head) \
709 _fr_dcursor_init(_cursor, \
710 _head, \
711 NULL, \
712 NULL, \
713 NULL, \
714 NULL, \
715 NULL, \
716 NULL, \
717 IS_CONST(fr_dlist_head_t *, _head))
718
719/** Setup a cursor to iterate over attribute items in dlists
720 *
721 * @param[in] cursor Where to initialise the cursor (uses existing structure).
722 * @param[in] head of dlist.
723 * @param[in] iter Iterator callback.
724 * @param[in] peek Iterator callback that should not modify iterator state.
725 * @param[in] iter_uctx to pass to iterator function.
726 * @param[in] insert Callback for inserts.
727 * @param[in] remove Callback for removals.
728 * @param[in] mod_uctx to pass to modification functions.
729 * @param[in] is_const Don't allow modification of the underlying list.
730 * @return the attribute pointed to by v.
731 *
732 * @hidecallergraph
733 */
734static inline CC_HINT(nonnull(1,2))
736 fr_dcursor_iter_t iter, fr_dcursor_iter_t peek, void const *iter_uctx,
737 fr_dcursor_insert_t insert, fr_dcursor_remove_t remove, void const *mod_uctx, bool is_const)
738{
739 *cursor = (fr_dcursor_t){
740 .dlist = UNCONST(fr_dlist_head_t *, head),
741 .iter = iter,
742 .peek = peek ? peek : iter,
743 .iter_uctx = UNCONST(void *, iter_uctx),
744 .insert = is_const ? NULL : insert,
745 .remove = is_const ? NULL : remove,
746 .mod_uctx = UNCONST(void *, mod_uctx),
747 .is_const = is_const
748 };
749 if (!fr_dlist_empty(cursor->dlist)) return fr_dcursor_next(cursor); /* Initialise current */
750
751 if (iter) return fr_dcursor_next(cursor); /* An iterator may do something, even on an empty list */
752
753 return NULL;
754}
755
756/** re-initialise a cursor, changing its list
757 *
758 * @param[in] _cursor to re-initialise.
759 * @param[in] _head of item list.
760 * @return
761 * - NULL if _head does not point to any items.
762 * - The first item in the list.
763 */
764#define fr_dcursor_reinit(_cursor, _head) \
765 _fr_dcursor_reinit(_cursor, \
766 _head, \
767 IS_CONST(fr_dlist_head_t *, _head))
768
769static inline CC_HINT(nonnull(1,2))
770void _fr_dcursor_list_reinit(fr_dcursor_t *cursor, fr_dlist_head_t const *head, bool is_const)
771{
772 cursor->dlist = UNCONST(fr_dlist_head_t *, head);
773 cursor->current = NULL;
774 cursor->is_const = is_const;
775}
776
777/** talloc_free the current item
778 *
779 * @param[in] cursor to free items from.
780 */
781static inline void fr_dcursor_free_item(fr_dcursor_t *cursor)
782{
783 if (!cursor) return;
784
785 fr_assert(!cursor->is_const);
786
788}
789
790/** Allocate a stack of cursors for traversing trees
791 *
792 * @param[in] ctx to allocate the cursor stack in.
793 * @param[in] depth Maximum depth of the cursor stack.
794 * @return
795 * - A new cursor stack.
796 * - NULL on error.
797 */
799{
801
802 stack = talloc_array_size(ctx, sizeof(fr_dcursor_stack_t) + (sizeof(fr_dcursor_t) * depth), 1);
803 if (unlikely(!stack)) return NULL;
804
805 talloc_set_name_const(stack, "fr_dcursor_stack_t");
806
807 return stack;
808}
809
811
813
814DIAG_OFF(unused-function)
815
816/** Expands to the type name used for the dcursor wrapper structure
817 *
818 * @param[in] _name Prefix we add to type-specific structures.
819 * @return ``<name>_dcursor_t``
820 */
821#define FR_DCURSOR(_name) _name ## _dcursor_t
822
823/** Expands to the type name used for the dcursor iterator type
824 *
825 * @param[in] _name Prefix we add to type-specific structures.
826 * @return ``<name>_iter_t``
827 */
828#define FR_DCURSOR_ITER(_name) _name ## _iter_t
829
830/** Expands to the type name used for the dcursor evaluator type
831 *
832 * @param[in] _name Prefix we add to type-specific structures.
833 * @return ``<name>_eval_t``
834 */
835#define FR_DCURSOR_EVAL(_name) _name ## _eval_t
836
837/** Expands to the type name used for the dcursor insert function type
838 *
839 * @param[in] _name Prefix we add to type-specific structures.
840 * @return ``<name>_insert_t``
841 */
842#define FR_DCURSOR_INSERT(_name) _name ## _insert_t
843
844/** Expands to the type name used for the dcursor remove function type
845 *
846 * @param[in] _name Prefix we add to type-specific structures.
847 * @return ``<name>_remove_t``
848 */
849#define FR_DCURSOR_REMOVE(_name) _name ## _remove_t
850
851/** Expands to the type name used for the dcursor copy function type
852 *
853 * @param[in] _name Prefix we add to type-specific structures.
854 * @return ``<name>_copy_t``
855 */
856#define FR_DCURSOR_COPY(_name) _name ## _copy_t
857
858/** Define type specific wrapper structs for dcursors
859 *
860 * @param[in] _name Prefix we add to type-specific structures.
861 * @param[in] _list_name The identifier used for type qualifying dlists.
862 * Should be the same as that use for
863 * - #FR_DLIST_HEAD
864 * - #FR_DLIST_ENTRY
865 * - #FR_DLIST_TYPES
866 * - #FR_DLIST_FUNCS
867 * @param[in] _element_type Type of element in the dlists.
868 *
869 * @note This macro should be used inside the header for the area of code
870 * which will use type specific functions.
871 */
872#define FR_DCURSOR_DLIST_TYPES(_name, _list_name, _element_type) \
873 typedef struct { fr_dcursor_t dcursor; } FR_DCURSOR(_name); \
874 typedef _element_type *(*FR_DCURSOR_ITER(_name))(fr_dcursor_t *cursor, _element_type *to_eval, void *uctx); \
875 typedef bool (*FR_DCURSOR_EVAL(_name))(_element_type const *item, void const *uctx); \
876 typedef int (*FR_DCURSOR_INSERT(_name))(fr_dcursor_t *cursor, FR_DLIST_ENTRY(_list_name) *to_insert, void *uctx); \
877 typedef int (*FR_DCURSOR_REMOVE(_name))(fr_dcursor_t *cursor, FR_DLIST_ENTRY(_list_name) *to_delete, void *uctx); \
878 typedef void (*FR_DCURSOR_COPY(_name))(FR_DCURSOR(_name) *out, FR_DCURSOR(_name) const *in);
879
880/** Define type specific wrapper functions for dcursors
881 *
882 * @note This macro should be used inside the source file that will use
883 * the type specific functions.
884 *
885 * @param[in] _name Prefix we add to type-specific dcursor functions.
886 * @param[in] _list_name Prefix for type-specific dlist used by this dcursor.
887 * @param[in] _element_type Type of structure that'll be inserted into the dlist and returned by the dcursor.
888 */
889#define FR_DCURSOR_FUNCS(_name, _list_name, _element_type) \
890DIAG_OFF(unused-function) \
891 static inline CC_HINT(nonnull) _element_type *_name ## _init(FR_DCURSOR(_name) *dcursor, \
892 FR_DLIST_HEAD(_list_name) *head) \
893 { return (_element_type *)_fr_dcursor_init(&dcursor->dcursor, &head->head, \
894 NULL, NULL, NULL, NULL, NULL, NULL, \
895 IS_CONST(FR_DLIST_HEAD(_list_name) *, head)); } \
896\
897 static inline CC_HINT(nonnull(1,2)) _element_type *_name ## _iter_init(FR_DCURSOR(_name) *dcursor, \
898 FR_DLIST_HEAD(_list_name) *head, \
899 FR_DCURSOR_ITER(_name) iter, \
900 FR_DCURSOR_ITER(_name) peek, \
901 void const *iter_uctx) \
902 { return (_element_type *)_fr_dcursor_init(&dcursor->dcursor, &head->head, \
903 (fr_dcursor_iter_t)iter, \
904 (fr_dcursor_iter_t)peek, \
905 iter_uctx, \
906 NULL, NULL, NULL, \
907 IS_CONST(FR_DLIST_HEAD(_list_name) *, head)); } \
908\
909 static inline CC_HINT(nonnull) _element_type *_name ## _current(FR_DCURSOR(_name) *dcursor) \
910 { return (_element_type *)fr_dcursor_current(&dcursor->dcursor); } \
911\
912 static inline CC_HINT(nonnull) _element_type *_name ## _next_peek(FR_DCURSOR(_name) *dcursor) \
913 { return (_element_type *)fr_dcursor_next_peek(&dcursor->dcursor); } \
914\
915 static inline CC_HINT(nonnull) _element_type *_name ## _list_next_peek(FR_DCURSOR(_name) *dcursor) \
916 { return (_element_type *)fr_dcursor_list_next_peek(&dcursor->dcursor); } \
917\
918 static inline CC_HINT(nonnull) _element_type *_name ## _next(FR_DCURSOR(_name) *dcursor) \
919 { return (_element_type *)fr_dcursor_next(&dcursor->dcursor); } \
920\
921 static inline CC_HINT(nonnull) void _name ## _copy(FR_DCURSOR(_name) *out, \
922 FR_DCURSOR(_name) const *in) \
923 { fr_dcursor_copy(&out->dcursor, &in->dcursor); } \
924\
925 static inline CC_HINT(nonnull) _element_type *_name ## _head(FR_DCURSOR(_name) *dcursor) \
926 { return (_element_type *)fr_dcursor_head(&dcursor->dcursor); } \
927\
928 static inline CC_HINT(nonnull) _element_type *_name ## _tail(FR_DCURSOR(_name) *dcursor) \
929 { return (_element_type *)fr_dcursor_tail(&dcursor->dcursor); } \
930\
931 static inline CC_HINT(nonnull) _element_type *_name ## _set_current(FR_DCURSOR(_name) *dcursor, \
932 _element_type *v) \
933 { return (_element_type *)fr_dcursor_set_current(&dcursor->dcursor, v); } \
934\
935 static inline CC_HINT(nonnull) int _name ## _prepend(FR_DCURSOR(_name) *dcursor, \
936 _element_type *v) \
937 { return fr_dcursor_prepend(&dcursor->dcursor, v); } \
938\
939 static inline CC_HINT(nonnull) int _name ## _append(FR_DCURSOR(_name) *dcursor, \
940 _element_type *v) \
941 { return fr_dcursor_append(&dcursor->dcursor, v); } \
942\
943 static inline CC_HINT(nonnull) int _name ## _insert(FR_DCURSOR(_name) *dcursor, \
944 _element_type *v) \
945 { return fr_dcursor_insert(&dcursor->dcursor, v); } \
946\
947 static inline CC_HINT(nonnull) _element_type *_name ## _replace(FR_DCURSOR(_name) *dcursor, \
948 _element_type *v) \
949 { return fr_dcursor_replace(&dcursor->dcursor, v); } \
950\
951 static inline CC_HINT(nonnull) _element_type *_name ## _remove(FR_DCURSOR(_name) *dcursor) \
952 { return (_element_type *)fr_dcursor_remove(&dcursor->dcursor); } \
953\
954 static inline CC_HINT(nonnull) void _name ## _merge(FR_DCURSOR(_name) *cursor, \
955 FR_DCURSOR(_name) *to_append) \
956 { fr_dcursor_merge(&cursor->dcursor, &to_append->dcursor); } \
957\
958 static inline CC_HINT(nonnull) void _name ## _free_list(FR_DCURSOR(_name) *dcursor) \
959 { fr_dcursor_free_list(&dcursor->dcursor); } \
960\
961 static inline CC_HINT(nonnull) void _name ## _free_item(FR_DCURSOR(_name) *dcursor) \
962 { fr_dcursor_free_item(&dcursor->dcursor); } \
963\
964 static inline CC_HINT(nonnull) _element_type *_name ## _intersect_head(FR_DCURSOR(_name) *a, \
965 FR_DCURSOR(_name) *b) \
966 { return (_element_type *)fr_dcursor_intersect_head(&a->dcursor, &b->dcursor); } \
967\
968 static inline CC_HINT(nonnull) _element_type *_name ## _intersect_next(FR_DCURSOR(_name) *a, \
969 FR_DCURSOR(_name) *b) \
970 { return (_element_type *)fr_dcursor_intersect_next(&a->dcursor, &b->dcursor); } \
971\
972 static inline CC_HINT(nonnull(1,2)) _element_type *_name ## _filter_head(FR_DCURSOR(_name) *dcursor, \
973 FR_DCURSOR_EVAL(_name) eval, \
974 void const *uctx) \
975 { return (_element_type *)fr_dcursor_filter_head(&dcursor->dcursor, (fr_dcursor_eval_t)eval, uctx); } \
976\
977 static inline CC_HINT(nonnull(1,2)) _element_type *_name ## _filter_next(FR_DCURSOR(_name) *dcursor, \
978 FR_DCURSOR_EVAL(_name) eval, \
979 void const *uctx) \
980 { return (_element_type *)fr_dcursor_filter_next(&dcursor->dcursor, (fr_dcursor_eval_t)eval, uctx); } \
981\
982 static inline CC_HINT(nonnull(1,2)) _element_type *_name ## _filter_current(FR_DCURSOR(_name) *dcursor, \
983 FR_DCURSOR_EVAL(_name) eval, \
984 void const *uctx) \
985 { return (_element_type *)fr_dcursor_filter_current(&dcursor->dcursor, (fr_dcursor_eval_t)eval, uctx); }
986
987DIAG_ON(unused-function)
988
989#ifdef __cplusplus
990}
991#endif
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:186
#define DIAG_ON(_x)
Definition build.h:481
#define RCSIDH(h, id)
Definition build.h:507
#define CC_NO_UBSAN(_sanitize)
Definition build.h:449
#define unlikely(_x)
Definition build.h:402
#define DIAG_OFF(_x)
Definition build.h:480
fr_dcursor_iter_t iter
Iterator function.
Definition dcursor.h:95
uint8_t depth
Which cursor is currently in use.
Definition dcursor.h:111
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
void(* fr_dcursor_copy_t)(fr_dcursor_t *out, fr_dcursor_t const *in)
Copy callback for duplicating complex dcursor state.
Definition dcursor.h:79
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:101
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:190
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:258
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:107
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:145
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:798
int(* fr_dcursor_remove_t)(fr_dcursor_t *cursor, void *to_delete, void *uctx)
Callback for performing additional actions on removal.
Definition dcursor.h:72
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:89
static void * fr_dcursor_replace(fr_dcursor_t *cursor, void *r)
Replace the current item.
Definition dcursor.h:610
void *(* fr_dcursor_iter_t)(fr_dcursor_t *cursor, void *to_eval, void *uctx)
Callback for implementing custom iterators.
Definition dcursor.h:49
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:102
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:116
static void fr_dcursor_free_item(fr_dcursor_t *cursor)
talloc_free the current item
Definition dcursor.h:781
fr_dcursor_insert_t insert
Callback function on insert.
Definition dcursor.h:100
struct fr_dcursor_s fr_dcursor_t
Definition dcursor.h:36
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:232
void * current
The current item in the cursor.
Definition dcursor.h:93
int(* fr_dcursor_insert_t)(fr_dcursor_t *cursor, void *to_insert, void *uctx)
Callback for performing additional actions on insert.
Definition dcursor.h:60
fr_dlist_head_t * dlist
Head of the doubly linked list being iterated over.
Definition dcursor.h:92
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:98
static void _fr_dcursor_list_reinit(fr_dcursor_t *cursor, fr_dlist_head_t const *head, bool is_const)
Definition dcursor.h:770
fr_dcursor_iter_t peek
Distinct "peek" function.
Definition dcursor.h:96
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:205
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:125
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:735
fr_dcursor_copy_t copy
Copy dcursor state.
Definition dcursor.h:104
bool is_const
The list we're iterating over is immutable.
Definition dcursor.h:106
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:158
static fr_slen_t in
Definition dict.h:882
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:468
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
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:302
static bool fr_dlist_empty(fr_dlist_head_t const *list_head)
Check whether a list has any items.
Definition dlist.h:483
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:513
static void * fr_dlist_replace(fr_dlist_head_t *list_head, void *item, void *ptr)
Replace an item in a dlist.
Definition dlist.h:688
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:360
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:320
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:537
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:396
Head of a doubly linked list.
Definition dlist.h:51
talloc_free(hp)
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:121
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:37
static fr_slen_t head
Definition xlat.h:420
int nonnull(2, 5))
static size_t char ** out
Definition value.h:1030