From: Nick Porter Date: Tue, 12 Jan 2021 10:06:02 +0000 (+0000) Subject: Initial implementation of dcursor X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3a0316fa229b3eed83fc6c5eae9a66bcefce362a;p=thirdparty%2Ffreeradius-server.git Initial implementation of dcursor Cursors designed to work with dlists --- diff --git a/src/lib/util/cursor.h b/src/lib/util/cursor.h index cbbc59c953f..1e9690dd1a2 100644 --- a/src/lib/util/cursor.h +++ b/src/lib/util/cursor.h @@ -20,6 +20,9 @@ * * @copyright 2016 The FreeRADIUS server project */ +#ifdef USE_DOUBLE_LIST +#include +#endif RCSIDH(cursor_h, "$Id$") #ifdef __cplusplus diff --git a/src/lib/util/dcursor.c b/src/lib/util/dcursor.c new file mode 100644 index 00000000000..ce80a6b4919 --- /dev/null +++ b/src/lib/util/dcursor.c @@ -0,0 +1,149 @@ +/* + * This program is is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2 of the + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** Functions to iterate over a sets and subsets of items in dlists + * + * @file src/lib/util/dcursor.c + * + * @note Do not modify collections of items pointed to by a cursor + * with non fr_dcursor_* functions over the lifetime of that cursor. + * + * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org) + * @copyright 2013-2016 Arran Cudbard-Bell (a.cudbardb@freeradius.org) + * @copyright 2013-2016 The FreeRADIUS Server Project. + */ +RCSID("$Id$") + +#include +#include +#include +#include +#include + +/** Return the first item matching the iterator in cursor a and cursor b + * + * If a and b are not currently set to the same item, b will be reset, + * and wound to the item before a's current item. + * + * @note Both cursors must operate on the same list of items. + * + * @param[in] a First cursor. + * @param[in] b Second cursor. + * @return item at the start of the list. + * + * @hidecallergraph + */ +void *fr_dcursor_intersect_head(fr_dcursor_t *a, fr_dcursor_t *b) +{ + void *a_item, *b_item; + + if (unlikely(a->dlist != b->dlist)) return NULL; + + a_item = fr_dcursor_head(a); + b_item = fr_dcursor_head(b); + + if (a_item == b_item) return a_item; + + return fr_dcursor_intersect_next(a, b); +} + +/** Return the next item matching the iterator in cursor a and cursor b + * + * If a and b are not currently set to the same item, b will be reset, + * and wound to the item before a's current item. + * + * @note Both cursors must operate on the same list of items. + * + * @param[in] a First cursor. + * @param[in] b Second cursor. + * @return next item in the list. + * + * @hidecallergraph + */ +void *fr_dcursor_intersect_next(fr_dcursor_t *a, fr_dcursor_t *b) +{ + fr_dcursor_iter_t b_iter; + void *b_uctx; + + if (unlikely(a->dlist != b->dlist)) return NULL; + + /* + * If either of the iterators lack an iterator + * just use cursor_next... + */ + if (!a->iter) return fr_dcursor_next(b); + if (!b->iter) return fr_dcursor_next(a); + + /* + * Both have iterators... + */ + b_iter = b->iter; + b_uctx = b->uctx; + + /* + * Deal with the case where the two iterators + * are out of sync. + */ + if (a->current != b->current) { + fr_dcursor_head(b); /* reset */ + } else { + a->current = dcursor_next(a, a->current); + a->prev = fr_dlist_prev(a->dlist, a->current); + } + + /* + * Use a's iterator to select the item to + * check. + */ + do { + void *a_prev = fr_dcursor_list_prev_peek(a); + b->iter = NULL; /* Disable b's iterator */ + + /* + * Find a in b (the slow way *sigh*) + */ + while ((b->current = dcursor_next(b, b->current)) && (b->current != a_prev)); + + /* + * No more items... + */ + if (!b->current) { + fr_dcursor_copy(a, b); + return NULL; + } + + /* + * We're now one item before the item + * returned by a, see if b's iterator + * returns the same item as a's. + */ + b->iter = b_iter; + b->current = dcursor_next(b, b->current); + + /* + * Matched, we're done... + */ + if (a->current == b->current) return a->current; + + /* + * Reset b's position to a's and try again. + */ + fr_dcursor_copy(b, a); + b->iter = b_iter; + b->uctx = b_uctx; + } while ((a->current = dcursor_next(a, a->current))); + + return NULL; +} diff --git a/src/lib/util/dcursor.h b/src/lib/util/dcursor.h new file mode 100644 index 00000000000..61e35a05cf0 --- /dev/null +++ b/src/lib/util/dcursor.h @@ -0,0 +1,720 @@ +#pragma once +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, cursor 2 of the + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** Functions to iterate over a sets and subsets of items stored in dlists + * + * @file src/lib/util/dcursor.h + * + * @copyright 2020 The FreeRADIUS server project + */ +RCSIDH(dcursor_h, "$Id$") + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include +#include +#include + +/** Callback for implementing custom iterators + * + * @param[in] list head of the dlist. + * @param[in] to_eval the next item in the list. Iterator should check to + * see if it matches the iterator's filter, and if it doesn't + * iterate over the items until one is found that does. + * @param[in] uctx passed to #fr_dcursor_init. + * @return + * - to_eval if to_eval matched, or a subsequent attribute if that matched. + * - NULL if no more matching attributes were found. + */ +typedef void *(*fr_dcursor_iter_t)(fr_dlist_head_t *list, void *to_eval, void *uctx); +/** Callback for performing additional actions on insert + * + * @param[in] list head of the dlist. + * @param[in] to_insert The item being inserted into the cursor. + * @param[in] uctx passed to #fr_dcursor_init. + * @return + * - 0 on success. + * - -1 on failure. + */ +typedef int (*fr_dcursor_insert_t)(fr_dlist_head_t *list, void *to_insert, void *uctx); +/** Callback for performing additional actions on removal + * + * @param[in] list head of the dlist. + * @param[in] to_delete The item being removed from the cursor. + * @param[in] uctx passed to #fr_dcursor_init. + * @return + * - 0 on success. + * - -1 on failure. + */ +typedef int (*fr_dcursor_delete_t)(fr_dlist_head_t *list, void *to_delete, void *uctx); +/** Type of evaluation functions to pass to the fr_dcursor_filter_*() functions. + * + * @param[in] item the item to be evaluated + * @param[in] uctx context that may assist with evaluation + * @return + * - true if the evaluation function is satisfied. + * - false if the evaluation function is not satisfied. + */ +typedef bool (*fr_dcursor_eval_t)(void const *item, void const *uctx); +typedef struct { + fr_dlist_head_t *dlist; //!< Head of the doubly linked list being iterated over. + void *current; //!< The current item in the dlist. + void *prev; //!< The previous item in the dlist. + fr_dcursor_iter_t iter; //!< Iterator function. + fr_dcursor_insert_t insert; //!< Callback function on insert. + fr_dcursor_delete_t delete; //!< Callback function on delete. + void *uctx; //!< to pass to iterator function. +} fr_dcursor_t; + +typedef struct { + uint8_t depth; //!< Which cursor is currently in use. + fr_dcursor_t cursor[]; //!< Stack of cursors. +} fr_dcursor_stack_t; + +/** Internal function to get the next item + * + * @param[in] cursor to operate on. + * @param[in] current attribute. + * @return + * - The next attribute. + * - NULL if no more attributes. + */ +static inline void *dcursor_next(fr_dcursor_t *cursor, void *current) +{ + void *next; + + /* + * First time next has been called + */ + if (!current) { + if (fr_dlist_empty(cursor->dlist)) return NULL; + if (cursor->prev) return NULL; /* At tail of the list */ + if (!cursor->iter) return (fr_dlist_head(cursor->dlist)); /* Fast path without custom iter */ + + current = fr_dlist_head(cursor->dlist); + return cursor->iter(cursor->dlist, current, cursor->uctx); + } + +#ifndef TALLOC_GET_TYPE_ABORT_NOOP + if (cursor->dlist->type) _talloc_get_type_abort(current, cursor->dlist->type, __location__); +#endif + + if (!cursor->iter) { + return fr_dlist_next(cursor->dlist, current); /* Fast path without custom iter */ + } + + /* + * Pre-advance current + */ + next = fr_dlist_next(cursor->dlist, current); + + /* + * The iterator can just return what it was passed for curr + * if it just wants to advance by one. + */ + return cursor->iter(cursor->dlist, next, cursor->uctx); +} + +/** Copy cursor parameters and state. + * + * @param[out] out Where to copy the cursor to. + * @param[in] in cursor to copy. + * + * @hidecallergraph + */ +static inline void fr_dcursor_copy(fr_dcursor_t *out, fr_dcursor_t const *in) +{ + memcpy(out, in, sizeof(*out)); +} + +/** Rewind cursor to the start of the list + * + * @param[in] cursor to operate on. + * @return item at the start of the list. + * + * @hidecallergraph + */ +static inline void *fr_dcursor_head(fr_dcursor_t *cursor) +{ + if (unlikely(!cursor)) return NULL; + + cursor->prev = NULL; + /* + * If we have a custom iterator, the dlist attribute + * may not be in the subset the iterator would + * return, so set everything to NULL and have + * dcursor_next figure it out. + */ + if (cursor->iter) { + cursor->current = dcursor_next(cursor, NULL); + return cursor->current; + } + + cursor->current = fr_dlist_head(cursor->dlist); + + return cursor->current; +} + +/** Wind cursor to the tail item in the list + * + * @param[in] cursor to operate on. + * @return item at the end of the list. + * + * @hidecallergraph + */ +static inline void *fr_dcursor_tail(fr_dcursor_t *cursor) +{ + if (!cursor || fr_dlist_empty(cursor->dlist)) return NULL; + + cursor->current = fr_dlist_tail(cursor->dlist); + if (cursor->current) { + cursor->prev = fr_dlist_prev(cursor->dlist, cursor->current); + } else { + cursor->prev = NULL; + } + + return cursor->current; +} + +/** Advanced the cursor to the next item + * + * @param[in] cursor to operate on. + * @return + * - Next item. + * - NULL if the list is empty, or the cursor has advanced past the end of the list. + * + * @hidecallergraph + */ +static inline void * CC_HINT(hot) fr_dcursor_next(fr_dcursor_t *cursor) +{ + if (!cursor || fr_dlist_empty(cursor->dlist)) return NULL; + cursor->current = dcursor_next(cursor, cursor->current); + + cursor->prev = fr_dlist_prev(cursor->dlist, cursor->current); + return cursor->current; +} + +/** Return the next iterator item without advancing the cursor + * + * @param[in] cursor to operate on. + * @return + * - Next item. + * - NULL if the list is empty, or the cursor has advanced past the end of the list. + * + * @hidecallergraph + */ +static inline void *fr_dcursor_next_peek(fr_dcursor_t *cursor) +{ + return dcursor_next(cursor, cursor->current); +} + +/** Returns the next list item without advancing the cursor + * + * @note This returns the next item in the list, which may not match the + * next iterator value. It's mostly used for debugging. You probably + * want #fr_dcursor_next_peek. + * + * @param[in] cursor to operator on. + * @return + * - Next item in list. + * - NULL if the list is empty, or the cursor has advanced past the end of the list. + * + * @hidecallergraph + */ +static inline void *fr_dcursor_list_next_peek(fr_dcursor_t *cursor) +{ + if (!cursor || !cursor->current) return NULL; + + return fr_dlist_next(cursor->dlist, cursor->current); +} + +/** Returns the previous list item without rewinding the cursor + * + * @note This returns the previous item in the list, which may not be the + * previous 'current' value. + * + * @param[in] cursor to operator on. + * @return + * - Previous item. + * - NULL if no previous item available. + * + * @hidecallergraph + */ +static inline void *fr_dcursor_list_prev_peek(fr_dcursor_t *cursor) +{ + if (unlikely(!cursor)) return NULL; + + /* + * If cursor->current is not set then there's no prev. + * fr_dlist_prev would return the tail + */ + if (!cursor->prev) return NULL; + + return fr_dlist_prev(cursor->dlist, cursor->current); +} + +/** Return the item the cursor current points to + * + * @param[in] cursor to operate on. + * @return + * - The item the cursor currently points to. + * - NULL if the list is empty, or the cursor has advanced past the end of the list. + * + * @hidecallergraph + */ +static inline void * CC_HINT(hot) fr_dcursor_current(fr_dcursor_t *cursor) +{ + if (unlikely(!cursor)) return NULL; + + return cursor->current; +} + +/** Set the cursor to a specified item + * + * @param[in] cursor to operate on. + * @param[in] item to point the cursor at + * @return + * - item cursor points at. + * - NULL if the list is empty. + * + * @hidecallergraph + */ +static inline void * CC_HINT(hot) fr_dcursor_set_current(fr_dcursor_t *cursor, void *item) +{ + if (!cursor || fr_dlist_empty(cursor->dlist)) return NULL; + if (!item) return NULL; + + cursor->current = item; + cursor->prev = fr_dlist_prev(cursor->dlist, item); + + return cursor->current; +} + +/** Insert a single item at the start of the list + * + * @note Will not advance cursor position to r attribute, but will set cursor + * to this attribute, if it's the head one in the list. + * + * Insert a void at the start of the list. + * + * @param cursor to operate on. + * @param v to insert. + * + * @hidecallergraph + */ +static inline void CC_HINT(hot) fr_dcursor_prepend(fr_dcursor_t *cursor, void *v) +{ +#ifndef TALLOC_GET_TYPE_ABORT_NOOP + if (cursor->dlist->type) _talloc_get_type_abort(v, cursor->dlist->type, __location__); +#endif + + /* + * Insert at the head of the list + */ + fr_dlist_insert_head(cursor->dlist, v); + + /* + * Set previous if the cursor was already set but not + * prev - this will be if there was only one item in the + * list + */ + if (cursor->current && !cursor->prev) { + cursor->prev = v; + } +} + +/** Insert a single item at the end of the list + * + * @note Does not change the current pointer. + * + * @param[in] cursor to operate on. + * @param[in] v to insert. + * + * @hidecallergraph + */ +static inline void CC_HINT(hot) fr_dcursor_append(fr_dcursor_t *cursor, void *v) +{ +#ifndef TALLOC_GET_TYPE_ABORT_NOOP + if (cursor->dlist->type) _talloc_get_type_abort(v, cursor->dlist->type, __location__); +#endif + + fr_dlist_insert_tail(cursor->dlist, v); +} + +/** Insert directly after the current item + * + * @note Does not change the current pointer. + * + * @param[in] cursor to operate on. + * @param[in] v Item to insert. + * + * @hidecallergraph + */ +static inline void fr_dcursor_insert(fr_dcursor_t *cursor, void *v) +{ +#ifndef TALLOC_GET_TYPE_ABORT_NOOP + if (cursor->dlist->type) _talloc_get_type_abort(v, cursor->dlist->type, __location__); +#endif + + if (!cursor->current) { + fr_dcursor_append(cursor, v); + return; + } + fr_dlist_insert_after(cursor->dlist, cursor->current, v); +} + +/** Remove the current item + * + * The current item will be set to the one after the item + * being removed. An example check and remove loop: + * + @code {.c} + for (v = fr_dcursor_init(&cursor, head); + v; + v = fr_dcursor_current(&cursor) { + if () { + v = fr_dcursor_remove(&cursor); + talloc_free(v); + continue; + } + v = fr_dcursor_next(&cursor); + } + @endcode + * + * @param[in] cursor to remove the current item from. + * @return + * - item we just removed. + * - NULL on error. + * + * @hidecallergraph + */ +static inline void * CC_HINT(hot) fr_dcursor_remove(fr_dcursor_t *cursor) +{ + void *v, *p; + + if (!cursor->current) return NULL; /* don't do anything fancy, it's just a noop */ + + v = cursor->current; + p = fr_dcursor_list_prev_peek(cursor); + fr_dlist_remove(cursor->dlist, v); + + if (fr_dlist_head(cursor->dlist) == v) { + cursor->current = NULL; + cursor->prev = NULL; + } else { + cursor->current = p; + cursor->prev = p; + } + + /* + * Advance the cursor to the next item after the one which we just removed. + */ + cursor->current = dcursor_next(cursor, cursor->current); + return v; +} + +/** Moves items from one cursor to another. + * + * Move multiple items from one cursor to another. + * + * @note Will only move items from the current position of to_append + * up to the end of to_append. Items will be removed from the original + * cursor. Items will be inserted after the current position of the + * destination cursor (which will not be changed). + * + * @param[in] cursor to operate on. + * @param[in] to_append Items to append. + * + * @hidecallergraph + */ +static inline void fr_dcursor_merge(fr_dcursor_t *cursor, fr_dcursor_t *to_append) +{ + void *v, *p; + + p = cursor->current; + while ((v = fr_dcursor_remove(to_append))) { + fr_dcursor_insert(cursor, v); + cursor->current = v; + } + cursor->current = p; +} + +/** Return the next item, skipping the current item, that satisfies an evaluation function. + * + * @param[in] cursor to operate on + * @param[in] eval evaluation function + * @param[in] uctx context for the evaluation function + * @return the next item satisfying eval, or NULL if no such item exists + * + * @hidecallergraph + */ +static inline void *fr_dcursor_filter_next(fr_dcursor_t *cursor, fr_dcursor_eval_t eval, void const *uctx) +{ + void *item; + + do { + item = fr_dcursor_next(cursor); + } while (item && !eval(item, uctx)); + + return item; +} + +/** Return the first item that satisfies an evaluation function. + * + * @param[in] cursor to operate on + * @param[in] eval evaluation function + * @param[in] uctx context for the evaluation function + * @return the first item satisfying eval, or NULL if no such item exists + * + * @hidecallergraph + */ +static inline void *fr_dcursor_filter_head(fr_dcursor_t *cursor, fr_dcursor_eval_t eval, void const *uctx) +{ + void *item; + + item = fr_dcursor_head(cursor); + if (eval(item, uctx)) return item; + + return fr_dcursor_filter_next(cursor, eval, uctx); +} + +/** Return the next item, starting with the current item, that satisfies an evaluation function. + * + * @param[in] cursor to operate on + * @param[in] eval evaluation function + * @param[in] uctx context for the evaluation function + * @return the next item satisfying eval, or NULL if no such item exists + * + * @hidecallergraph + */ +static inline void *fr_dcursor_filter_current(fr_dcursor_t *cursor, fr_dcursor_eval_t eval, void const *uctx) +{ + void *item; + + while ((item = fr_dcursor_current(cursor)) && !eval(item, uctx)) { + fr_dcursor_next(cursor); + } + + return item; +} + +/** @hidecallergraph */ +void *fr_dcursor_intersect_head(fr_dcursor_t *a, fr_dcursor_t *b) CC_HINT(nonnull); + +/** @hidecallergraph */ +void *fr_dcursor_intersect_next(fr_dcursor_t *a, fr_dcursor_t *b) CC_HINT(nonnull); + +/** Replace the current item + * + * After replacing the current item, the cursor will be rewound, + * and the next item selected by the iterator function will become current. + * + * @param[in] cursor to replace the current item in. + * @param[in] r item to insert. + * @return + * - item we just replaced. + * - NULL on error. + * + * @hidecallergraph + */ +static inline void * CC_HINT(hot) fr_dcursor_replace(fr_dcursor_t *cursor, void *r) +{ + void *v, *p; + + /* + * Correct behaviour here is debatable + */ + if (fr_dlist_empty(cursor->dlist)) { + fr_dcursor_prepend(cursor, r); + return NULL; + } + + /* + * If there's a head, but no current, + * we've iterated off the end of the list, + * so the replace becomes an append. + */ + v = cursor->current; + if (!v) { + fr_dcursor_append(cursor, r); + return NULL; + } + p = fr_dcursor_list_prev_peek(cursor); + + fr_dlist_replace(cursor->dlist, cursor->current, r); + + /* + * Fixup current pointer. + */ + cursor->current = p; + + /* + * re-advance the cursor. + * + * This ensures if the iterator skips the item + * we just replaced, it doesn't become current. + */ + fr_dcursor_next(cursor); + + return v; +} + +/** Free the current item and all items after it + * + * @note Use fr_dcursor_remove and talloc_free to free single items. + * + * Current should be the item *after* the one freed. + * + * @param[in] cursor to free items in. + * + * @hidecallergraph + */ +static inline void fr_dcursor_free_list(fr_dcursor_t *cursor) +{ + void *v; + + if (fr_dlist_empty(cursor->dlist)) return; /* noop */ + + do { + v = fr_dcursor_remove(cursor); + talloc_free(v); + } while (v); +} + +/** Initialise a cursor with runtime talloc type safety checks and a custom iterator + * + * @param[in] _cursor to initialise. + * @param[in] _head of item list. + * @param[in] _iter function. + * @param[in] _uctx _iter function _uctx. + * @param[in] _type Talloc type i.e. fr_pair_t or fr_value_box_t. + * @return + * - NULL if _head does not point to any items, or the iterator matches no items + * in the current list. + * - The first item returned by the iterator. + */ +#define fr_dcursor_talloc_iter_init(_cursor, _head, _iter, _uctx, _type) \ + _fr_dcursor_init(_cursor, (fr_dlist_head_t const *)_head, _iter, NULL, NULL, _uctx) + +/** Initialise a cursor with a custom iterator + * + * @param[in] _cursor to initialise. + * @param[in] _head of item list. + * @param[in] _iter function. + * @param[in] _uctx _iter function _uctx. + * @return + * - NULL if _head does not point to any items, or the iterator matches no items + * in the current list. + * - The first item returned by the iterator. + */ +#define fr_dcursor_iter_init(_cursor, _head, _iter, _uctx) \ + _fr_dcursor_init(_cursor, (fr_dlist_head_t *)_head, _iter, NULL, NULL, _uctx) + +/** Initialise a cursor with runtime talloc type safety checks + * + * @param[in] _cursor to initialise. + * @param[in] _head of item list. + * @param[in] _type Talloc type i.e. fr_pair_t or fr_value_box_t. + * @return + * - NULL if _head does not point to any items. + * - The first item in the list. + */ +#define fr_dcursor_talloc_init(_cursor, _head, _type) \ + _fr_dcursor_init(_cursor, (fr_dlist_head_t const *)_head, NULL, NULL, NULL, NULL) + +/** Initialise a cursor + * + * @param[in] _cursor to initialise. + * @param[in] _head of item list. + * @return + * - NULL if _head does not point to any items. + * - The first item in the list. + */ +#define fr_dcursor_init(_cursor, _head) \ + _fr_dcursor_init(_cursor, (fr_dlist_head_t const *)_head, NULL, NULL, NULL, NULL) + +/** Setup a cursor to iterate over attribute items in dlists + * + * @param[in] cursor Where to initialise the cursor (uses existing structure). + * @param[in] head of dlist. + * @param[in] iter Iterator callback. + * @param[in] insert Callback for inserts. + * @param[in] delete Callback for removals. + * @param[in] uctx to pass to iterator function. + * @return the attribute pointed to by v. + * + * @hidecallergraph + */ +static inline void * CC_HINT(hot) _fr_dcursor_init(fr_dcursor_t *cursor, fr_dlist_head_t const *head, + fr_dcursor_iter_t iter, fr_dcursor_insert_t insert, + fr_dcursor_delete_t delete, void const *uctx) +{ + fr_dlist_head_t *v; + + memcpy(&v, &head, sizeof(v)); /* stupid const hacks */ + *cursor = (fr_dcursor_t){ + .dlist = v, + .iter = iter, + .insert = insert, + .delete = delete, + .prev = NULL + }; + memcpy(&cursor->uctx, &uctx, sizeof(cursor->uctx)); + if (!fr_dlist_empty(cursor->dlist)) return fr_dcursor_next(cursor); /* Initialise current */ + + return NULL; +} + +/** talloc_free the current item + * + * @param[in] cursor to free items from. + */ +static inline void fr_dcursor_free_item(fr_dcursor_t *cursor) +{ + if (!cursor) return; + + talloc_free(fr_dcursor_remove(cursor)); +} + +/** Allocate a stack of cursors for traversing trees + * + * @param[in] ctx to allocate the cursor stack in. + * @param[in] depth Maximum depth of the cursor stack. + * @return + * - A new cursor stack. + * - NULL on error. + */ +static inline fr_dcursor_stack_t *fr_dcursor_stack_alloc(TALLOC_CTX *ctx, uint8_t depth) +{ + fr_dcursor_stack_t *stack; + + stack = talloc_array_size(ctx, sizeof(fr_dcursor_stack_t) + (sizeof(fr_dcursor_t) * depth), 1); + if (unlikely(!stack)) return NULL; + + talloc_set_name_const(stack, "fr_dcursor_stack_t"); + + return stack; +} + +#ifdef __cplusplus +} +#endif diff --git a/src/lib/util/libfreeradius-util.mk b/src/lib/util/libfreeradius-util.mk index 7270f49d76f..d0345f77fca 100644 --- a/src/lib/util/libfreeradius-util.mk +++ b/src/lib/util/libfreeradius-util.mk @@ -10,6 +10,7 @@ SOURCES := \ cap.c \ cursor.c \ dbuff.c \ + dcursor.c \ debug.c \ dict_ext.c \ dict_fixup.c \