Skip to content

Commit

Permalink
htab: add an iterator
Browse files Browse the repository at this point in the history
This commit defines an iterator in the libctf _next style because it's
*so much* nicer to use than the function-calling _iter form:

extern void *dt_htab_next(const dt_htab_t *htab, dt_htab_next_t **it);
extern void dt_htab_next_destroy(dt_htab_next_t *i);

Call dt_htab_next with the htab to iterate over and a dt_htab_next_t
initialized to NULL to allocate an iterator and return the first
element.  Subsequent calls with the allocated iterator will return
further elements from the hash until iteration is complete, at which
time the iterator is freed and reset to NULL, ready for another
iteration cycle.

There are no restrictions whatsoever on what you can do inside iteration
(iterate over other things, iterate over the same thing, fork, longjmp
out of it, etc) except that you should not delete hash entries that the
iterator has not yet returned: if you insert new ones, they may or may
not be returned by this iteration cycle.  If you need to exit early or
longjmp out, dt_htab_next_destroy frees the iterator for you.

Signed-off-by: Nick Alcock <nick.alcock@oracle.com>
Reviewed-by: Eugene Loh <eugene.loh@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
  • Loading branch information
nickalcock authored and kvanhees committed Dec 1, 2021
1 parent 1b022e4 commit 06ee554
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 3 deletions.
3 changes: 2 additions & 1 deletion libdtrace/dt_consume.c
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,8 @@ static dt_htab_ops_t dt_spec_buf_htab_ops = {
.hval = (htab_hval_fn)dt_spec_buf_hval,
.cmp = (htab_cmp_fn)dt_spec_buf_cmp,
.add = (htab_add_fn)dt_spec_buf_add,
.del = (htab_del_fn)dt_spec_buf_del_buf
.del = (htab_del_fn)dt_spec_buf_del_buf,
.next = (htab_next_fn)dt_spec_buf_next
};

static int
Expand Down
93 changes: 93 additions & 0 deletions libdtrace/dt_htab.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* cmp(void *e1, void *e2) - compare two entries
* add(void *h, void *e) - add an entry to a list (h is head)
* del(void *h, void *e) - delete an entry from a list (h is head)
* next(void *e) - return the next entry after e
*
* Entries are hashed into a hashtable slot based on the return value of
* hval(e). Each hashtable slot holds a list of buckets, with each bucket
Expand Down Expand Up @@ -47,6 +48,21 @@ struct dt_htab {
dt_htab_ops_t *ops;
};

/*
* The dt_htab_next iteration state is somewhat unusual. It points not to the
* element most recently returned by the iterator, but to the element which
* will be returned on the *next* iteration cycle (so it spends the last
* iteration cycle "off-the-end", all-NULL). This allows the user to
* delete the current element returned by dt_htab_next().
*/
struct dt_htab_next {
const dt_htab_t *htab;
int idx;
dt_hbucket_t *bucket;
void *ent;
int exhausted;
};

/*
* Create a new (empty) hashtable.
*/
Expand Down Expand Up @@ -251,6 +267,83 @@ int dt_htab_delete(dt_htab_t *htab, void *entry)
return 0;
}

/*
* Iterate over a hashtable, returning each element in turn (as a pointer to
* void). NULL is returned at end-of-iteration (and OOM).
*/
void *
dt_htab_next(const dt_htab_t *htab, dt_htab_next_t **it)
{
dt_htab_next_t *i = *it;
void *ret;

/*
* Start of iteration.
*/
if (!i) {
if ((i = calloc(1, sizeof(dt_htab_next_t))) == NULL)
return NULL;
i->htab = htab;
*it = i;

/*
* Tick through one iteration. Ignore the return value,
* since it is meaningless at this stage.
*/
(void) dt_htab_next(htab, it);
}

/*
* Handle end-of-iteration, then capture the value we will return after
* advancing the iterator.
*/

if (i->exhausted) {
free(i);
*it = NULL;
return NULL;
}
ret = i->ent;

/*
* One iteration cycle. Exhaust the current idx: moving on to the next
* restarts the bucket and ent subloops.
*/
for (; i->idx < i->htab->size; i->idx++, i->bucket = NULL, i->ent = NULL) {

if (i->htab->tab[i->idx] == NULL)
continue;
if (i->bucket == NULL)
i->bucket = i->htab->tab[i->idx];

/*
* Check the current bucket: moving on to the next restarts the
* ent subloop.
*/
for (; i->bucket; i->bucket = i->bucket->next, i->ent = NULL) {

if (i->ent == NULL)
i->ent = i->bucket->head;
else
i->ent = i->htab->ops->next(i->ent);
if (i->ent)
return ret;
}
}

/*
* End of advancement, but that doesn't mean we can't return.
*/
i->exhausted = 1;
return ret;
}

void
dt_htab_next_destroy(dt_htab_next_t *i)
{
free(i);
}

/*
* Return the number of entries in the hashtable.
*/
Expand Down
13 changes: 12 additions & 1 deletion libdtrace/dt_htab.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ typedef uint32_t (*htab_hval_fn)(const void *);
typedef int (*htab_cmp_fn)(const void *, const void *);
typedef void *(*htab_add_fn)(void *, void *);
typedef void *(*htab_del_fn)(void *, void *);
typedef void *(*htab_next_fn)(void *);

#define DEFINE_HE_STD_LINK_FUNCS(ID, TYPE, HE) \
static TYPE *ID##_add(TYPE *head, TYPE *new) \
Expand Down Expand Up @@ -59,6 +60,11 @@ typedef void *(*htab_del_fn)(void *, void *);
ent->HE.prev = ent->HE.next = NULL; \
\
return head; \
} \
\
static TYPE *ID##_next(TYPE *ent) \
{ \
return ent->HE.next; \
}

#define DEFINE_HTAB_STD_OPS(ID) \
Expand All @@ -67,6 +73,7 @@ typedef void *(*htab_del_fn)(void *, void *);
.cmp = (htab_cmp_fn)ID##_cmp, \
.add = (htab_add_fn)ID##_add, \
.del = (htab_del_fn)ID##_del, \
.next = (htab_next_fn)ID##_next, \
};


Expand All @@ -75,21 +82,25 @@ typedef struct dt_htab_ops {
htab_cmp_fn cmp;
htab_add_fn add;
htab_del_fn del;
htab_next_fn next;
} dt_htab_ops_t;

typedef struct dt_hentry {
void *next;
void *prev;
} dt_hentry_t;

typedef struct dt_htab dt_htab_t;
typedef struct dt_htab dt_htab_t;
typedef struct dt_htab_next dt_htab_next_t;

extern dt_htab_t *dt_htab_create(struct dtrace_hdl *dtp, dt_htab_ops_t *ops);
extern void dt_htab_destroy(struct dtrace_hdl *dtp, dt_htab_t *htab);
extern int dt_htab_insert(dt_htab_t *htab, void *entry);
extern void *dt_htab_lookup(const dt_htab_t *htab, const void *entry);
extern size_t dt_htab_entries(const dt_htab_t *htab);
extern int dt_htab_delete(dt_htab_t *htab, void *entry);
extern void *dt_htab_next(const dt_htab_t *htab, dt_htab_next_t **it);
extern void dt_htab_next_destroy(dt_htab_next_t *i);
extern void dt_htab_stats(const char *name, const dt_htab_t *htab);

#ifdef __cplusplus
Expand Down
3 changes: 2 additions & 1 deletion libdtrace/dt_kernel_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ static dt_htab_ops_t kernpath_htab_ops = {
.hval = (htab_hval_fn)kernpath_hval,
.cmp = (htab_cmp_fn)kernpath_cmp,
.add = (htab_add_fn)kernpath_add,
.del = (htab_del_fn)kernpath_del_path
.del = (htab_del_fn)kernpath_del_path,
.next = (htab_next_fn)kernpath_next
};

/*
Expand Down

0 comments on commit 06ee554

Please sign in to comment.