[ccan] [PATCH] list: add list_for_each_rev_safe
Eric Wong
normalperson at yhbt.net
Sun Oct 5 23:20:45 EST 2014
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 <normalperson at yhbt.net>
---
ccan/list/list.h | 27 ++++++++++++++++++++++++++-
ccan/list/test/run.c | 31 ++++++++++++++++++++++++++++++-
2 files changed, 56 insertions(+), 2 deletions(-)
diff --git a/ccan/list/list.h b/ccan/list/list.h
index 983675b..b4a6cdf 100644
--- a/ccan/list/list.h
+++ b/ccan/list/list.h
@@ -527,6 +527,32 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
i = container_of_var(i->member.prev, 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) \
+ for (i = container_of_var(list_debug(h, LIST_LOC)->n.prev, i, member), \
+ nxt = container_of_var(i->member.prev, i, member); \
+ &i->member != &(h)->n; \
+ i = nxt, \
+ nxt = container_of_var(i->member.prev, i, member))
+
+/**
* list_for_each_safe - iterate through a list, maybe during deletion
* @h: the list_head
* @i: the structure containing the list_node
@@ -538,7 +564,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--;
diff --git a/ccan/list/test/run.c b/ccan/list/test/run.c
index 3067445..7616af6 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);
--
EW
More information about the ccan
mailing list