From: Eric Wong Date: Fri, 24 Oct 2014 22:02:32 +0000 (+0000) Subject: list: add list_for_each_rev_safe{,_off} macros X-Git-Url: http://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=c2fbfe5282ba264f3485586e7efa8a5967f2d386;ds=sidebyside list: add list_for_each_rev_safe{,_off} macros Deleting while iterating backwards will be needed in the Ruby st_foreach_reverse_check implementation if we decide to port Ruby's st.c to use ccan/list. ref: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/65408 Signed-off-by: Eric Wong Reviewed-by: David Gibson Signed-off-by: Rusty Russell --- diff --git a/ccan/list/list.h b/ccan/list/list.h index aaf135d1..275442d2 100644 --- a/ccan/list/list.h +++ b/ccan/list/list.h @@ -524,6 +524,28 @@ static inline const void *list_tail_(const struct list_head *h, size_t off) #define list_for_each_rev(h, i, member) \ list_for_each_rev_off(h, i, list_off_var_(i, member)) +/** + * list_for_each_rev_safe - iterate through a list backwards, + * maybe during deletion + * @h: the list_head + * @i: the structure containing the list_node + * @nxt: the structure containing the list_node + * @member: the list_node member of the structure + * + * This is a convenient wrapper to iterate @i over the entire list backwards. + * It's a for loop, so you can break and continue as normal. The extra + * variable * @nxt is used to hold the next element, so you can delete @i + * from the list. + * + * Example: + * struct child *next; + * list_for_each_rev_safe(&parent->children, child, next, list) { + * printf("Name: %s\n", child->name); + * } + */ +#define list_for_each_rev_safe(h, i, nxt, member) \ + list_for_each_rev_safe_off(h, i, nxt, list_off_var_(i, member)) + /** * list_for_each_safe - iterate through a list, maybe during deletion * @h: the list_head @@ -536,7 +558,6 @@ static inline const void *list_tail_(const struct list_head *h, size_t off) * @nxt is used to hold the next element, so you can delete @i from the list. * * Example: - * struct child *next; * list_for_each_safe(&parent->children, child, next, list) { * list_del(&child->list); * parent->num_children--; @@ -731,6 +752,25 @@ static inline void list_prepend_list_(struct list_head *to, #define list_for_each_safe_off(h, i, nxt, off) \ list_for_each_safe_off_dir_((h),(i),(nxt),(off),next) +/** + * list_for_each_rev_safe_off - iterate backwards through a list of + * memory regions, maybe during deletion + * @h: the list_head + * @i: the pointer to a memory region wich contains list node data. + * @nxt: the structure containing the list_node + * @off: offset(relative to @i) at which list node data resides. + * + * For details see `list_for_each_rev_off' and `list_for_each_rev_safe' + * descriptions. + * + * Example: + * list_for_each_rev_safe_off(&parent->children, child, + * next, offsetof(struct child, list)) + * printf("Name: %s\n", child->name); + */ +#define list_for_each_rev_safe_off(h, i, nxt, off) \ + list_for_each_safe_off_dir_((h),(i),(nxt),(off),prev) + /* Other -off variants. */ #define list_entry_off(n, type, off) \ ((type *)list_node_from_off_((n), (off))) diff --git a/ccan/list/test/run.c b/ccan/list/test/run.c index 30674459..7616af6c 100644 --- a/ccan/list/test/run.c +++ b/ccan/list/test/run.c @@ -24,8 +24,9 @@ int main(int argc, char *argv[]) struct list_head list = LIST_HEAD_INIT(list); opaque_t *q, *nq; struct list_head opaque_list = LIST_HEAD_INIT(opaque_list); + LIST_HEAD(rev); - plan_tests(84); + plan_tests(92); /* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */ ok1(list_empty(&static_list)); ok1(list_check(&static_list, NULL)); @@ -148,6 +149,10 @@ int main(int argc, char *argv[]) list_del_from(&parent.children, &c->list); break; } + + /* prepare for list_for_each_rev_safe test */ + list_add(&rev, &c->list); + ok1(list_check(&parent.children, NULL)); if (i > 2) break; @@ -155,6 +160,30 @@ int main(int argc, char *argv[]) ok1(i == 3); ok1(list_empty(&parent.children)); + /* Test list_for_each_rev_safe, list_del and list_del_from. */ + i = 0; + list_for_each_rev_safe(&rev, c, n, list) { + switch (i++) { + case 0: + ok1(c == &c1); + list_del(&c->list); + break; + case 1: + ok1(c == &c2); + list_del_from(&rev, &c->list); + break; + case 2: + ok1(c == &c3); + list_del_from(&rev, &c->list); + break; + } + ok1(list_check(&rev, NULL)); + if (i > 2) + break; + } + ok1(i == 3); + ok1(list_empty(&rev)); + /* Test list_node_init: safe to list_del after this. */ list_node_init(&c->list); list_del(&c->list);