Browse Source

list: add list_next and list_prev helpers.

Some way towards iterating in the middle of the list.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Rusty Russell 12 years ago
parent
commit
b160cdd09b
2 changed files with 102 additions and 0 deletions
  1. 52 0
      ccan/list/list.h
  2. 50 0
      ccan/list/test/run-list_prev-list_next.c

+ 52 - 0
ccan/list/list.h

@@ -399,6 +399,43 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
 #define list_for_each_safe(h, i, nxt, member)				\
 	list_for_each_safe_off(h, i, nxt, list_off_var_(i, member))
 
+/**
+ * list_next - get the next entry in a list
+ * @h: the list_head
+ * @i: a pointer to an entry in the list.
+ * @member: the list_node member of the structure
+ *
+ * If @i was the last entry in the list, returns NULL.
+ *
+ * Example:
+ *	struct child *second;
+ *	second = list_next(&parent->children, first, list);
+ *	if (!second)
+ *		printf("No second child!\n");
+ */
+#define list_next(h, i, member)						\
+	((list_typeof(i))list_entry_or_null(list_debug(h),		\
+					    (i)->member.next,		\
+					    list_off_var_((i), member)))
+
+/**
+ * list_prev - get the previous entry in a list
+ * @h: the list_head
+ * @i: a pointer to an entry in the list.
+ * @member: the list_node member of the structure
+ *
+ * If @i was the first entry in the list, returns NULL.
+ *
+ * Example:
+ *	first = list_prev(&parent->children, second, list);
+ *	if (!first)
+ *		printf("Can't go back to first child?!\n");
+ */
+#define list_prev(h, i, member)						\
+	((list_typeof(i))list_entry_or_null(list_debug(h),		\
+					    (i)->member.prev,		\
+					    list_off_var_((i), member)))
+
 /**
  * list_append_list - empty one list onto the end of another.
  * @to: the list to append into
@@ -560,4 +597,19 @@ static inline struct list_node *list_node_from_off_(void *ptr, size_t off)
 	(container_off_var(var, member) +		\
 	 check_type(var->member, struct list_node))
 
+#if HAVE_TYPEOF
+#define list_typeof(var) typeof(var)
+#else
+#define list_typeof(var) void *
+#endif
+
+/* Returns member, or NULL if at end of list. */
+static inline void *list_entry_or_null(struct list_head *h,
+				       struct list_node *n,
+				       size_t off)
+{
+	if (n == &h->n)
+		return NULL;
+	return (char *)n - off;
+}
 #endif /* CCAN_LIST_H */

+ 50 - 0
ccan/list/test/run-list_prev-list_next.c

@@ -0,0 +1,50 @@
+#include <ccan/list/list.h>
+#include <ccan/tap/tap.h>
+#include <ccan/list/list.c>
+#include "helper.h"
+
+struct parent {
+	const char *name;
+	unsigned int num_children;
+	struct list_head children;
+};
+
+struct child {
+	const char *name;
+	struct list_node list;
+};
+
+int main(int argc, char *argv[])
+{
+	struct parent parent;
+	struct child c1, c2, c3;
+
+	plan_tests(12);
+	parent.num_children = 0;
+	list_head_init(&parent.children);
+
+	c1.name = "c1";
+	list_add(&parent.children, &c1.list);
+
+	ok1(list_next(&parent.children, &c1, list) == NULL);
+	ok1(list_prev(&parent.children, &c1, list) == NULL);
+
+	c2.name = "c2";
+	list_add_tail(&parent.children, &c2.list);
+
+	ok1(list_next(&parent.children, &c1, list) == &c2);
+	ok1(list_prev(&parent.children, &c1, list) == NULL);
+	ok1(list_next(&parent.children, &c2, list) == NULL);
+	ok1(list_prev(&parent.children, &c2, list) == &c1);
+
+	c3.name = "c3";
+	list_add_tail(&parent.children, &c3.list);
+
+	ok1(list_next(&parent.children, &c1, list) == &c2);
+	ok1(list_prev(&parent.children, &c1, list) == NULL);
+	ok1(list_next(&parent.children, &c2, list) == &c3);
+	ok1(list_prev(&parent.children, &c2, list) == &c1);
+	ok1(list_next(&parent.children, &c3, list) == NULL);
+	ok1(list_prev(&parent.children, &c3, list) == &c2);
+	return exit_status();
+}