From b160cdd09b23e03768428cda48a4c7981046405b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 29 Aug 2013 10:18:47 +0930 Subject: [PATCH] list: add list_next and list_prev helpers. Some way towards iterating in the middle of the list. Signed-off-by: Rusty Russell --- ccan/list/list.h | 52 ++++++++++++++++++++++++ ccan/list/test/run-list_prev-list_next.c | 50 +++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 ccan/list/test/run-list_prev-list_next.c diff --git a/ccan/list/list.h b/ccan/list/list.h index ab324087..03614873 100644 --- a/ccan/list/list.h +++ b/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 */ 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 index 00000000..85611d9d --- /dev/null +++ b/ccan/list/test/run-list_prev-list_next.c @@ -0,0 +1,50 @@ +#include +#include +#include +#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(); +} -- 2.39.5