]> git.ozlabs.org Git - ccan/commitdiff
list: add list_for_each_rev_safe{,_off} macros
authorEric Wong <normalperson@yhbt.net>
Fri, 24 Oct 2014 22:02:32 +0000 (22:02 +0000)
committerRusty Russell <rusty@rustcorp.com.au>
Mon, 30 Mar 2015 06:46:33 +0000 (17:16 +1030)
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@yhbt.net>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ccan/list/list.h
ccan/list/test/run.c

index aaf135d1c7a835a7d5ba09db0272c1b80e3831f9..275442d23da05cb8fe9c462e75ffd589fa97e374 100644 (file)
@@ -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)))
index 306744598e2461a5bd6eb28de70593c7b009088e..7616af6c6095e8ca0978d35608ba56a2846e7b73 100644 (file)
@@ -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);