[ccan] [PATCH 5/5] list: add list_for_each_rev_safe{,_off} macros

Eric Wong normalperson at yhbt.net
Sat Oct 25 09:02:32 AEDT 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     | 42 +++++++++++++++++++++++++++++++++++++++++-
 ccan/list/test/run.c | 31 ++++++++++++++++++++++++++++++-
 2 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/ccan/list/list.h b/ccan/list/list.h
index aaf135d..275442d 100644
--- a/ccan/list/list.h
+++ b/ccan/list/list.h
@@ -525,6 +525,28 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
 	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
  * @i: the structure containing the list_node
@@ -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 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