list: add list_next and list_prev helpers.
authorRusty Russell <rusty@rustcorp.com.au>
Thu, 29 Aug 2013 00:48:47 +0000 (10:18 +0930)
committerRusty Russell <rusty@rustcorp.com.au>
Thu, 29 Aug 2013 00:48:47 +0000 (10:18 +0930)
Some way towards iterating in the middle of the list.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ccan/list/list.h
ccan/list/test/run-list_prev-list_next.c [new file with mode: 0644]

index ab3240871773d90ceee149c3cb30931e228326fb..03614873ef6a718aaaadec32a27356f82c6c7912 100644 (file)
@@ -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))
 
 #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
 /**
  * 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))
 
        (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 */
 #endif /* CCAN_LIST_H */
diff --git a/ccan/list/test/run-list_prev-list_next.c b/ccan/list/test/run-list_prev-list_next.c
new file mode 100644 (file)
index 0000000..85611d9
--- /dev/null
@@ -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();
+}