From: Rusty Russell Date: Wed, 30 Nov 2011 03:02:18 +0000 (+1030) Subject: list: don't multiple-evaluate arguments to tlist_top and tlist_tail X-Git-Url: http://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=c1e57dbfbd00dee590ab0403763796bff0e2eb2e list: don't multiple-evaluate arguments to tlist_top and tlist_tail --- diff --git a/ccan/list/list.h b/ccan/list/list.h index bda2922b..1feb5891 100644 --- a/ccan/list/list.h +++ b/ccan/list/list.h @@ -279,8 +279,15 @@ static inline void list_del_from(struct list_head *h, struct list_node *n) * struct child *first; * first = list_top(&parent->children, struct child, list); */ -#define list_top(h, type, member) \ - (list_empty(h) ? NULL : list_entry((h)->n.next, type, member)) +#define list_top(h, type, member) \ + ((type *)list_top_((h), container_off((h)->n.next, type, member))) + +static inline const void *list_top_(const struct list_head *h, size_t off) +{ + if (list_empty(h)) + return NULL; + return (const char *)h->n.next - off; +} /** * list_tail - get the last entry in a list @@ -295,11 +302,18 @@ static inline void list_del_from(struct list_head *h, struct list_node *n) * last = list_tail(&parent->children, struct child, list); */ #define list_tail(h, type, member) \ - (list_empty(h) ? NULL : list_entry((h)->n.prev, type, member)) + ((type *)list_tail_((h), container_off((h)->n.next, type, member))) + +static inline const void *list_tail_(const struct list_head *h, size_t off) +{ + if (list_empty(h)) + return NULL; + return (const char *)h->n.prev - off; +} /** * list_for_each - iterate through a list. - * @h: the list_head + * @h: the list_head (warning: evaluated multiple times!) * @i: the structure containing the list_node * @member: the list_node member of the structure * diff --git a/ccan/list/test/run-single-eval.c b/ccan/list/test/run-single-eval.c new file mode 100644 index 00000000..f90eed35 --- /dev/null +++ b/ccan/list/test/run-single-eval.c @@ -0,0 +1,168 @@ +/* Make sure macros only evaluate their args once. */ +#include +#include +#include + +struct parent { + const char *name; + struct list_head children; + unsigned int num_children; + int eval_count; +}; + +struct child { + const char *name; + struct list_node list; +}; + +static LIST_HEAD(static_list); + +#define ref(obj, counter) ((counter)++, (obj)) + +int main(int argc, char *argv[]) +{ + struct parent parent; + struct child c1, c2, c3, *c, *n; + unsigned int i; + unsigned int static_count = 0, parent_count = 0, list_count = 0, + node_count = 0; + struct list_head list = LIST_HEAD_INIT(list); + + plan_tests(74); + /* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */ + ok1(list_empty(ref(&static_list, static_count))); + ok1(static_count == 1); + ok1(list_check(ref(&static_list, static_count), NULL)); + ok1(static_count == 2); + ok1(list_empty(ref(&list, list_count))); + ok1(list_count == 1); + ok1(list_check(ref(&list, list_count), NULL)); + ok1(list_count == 2); + + parent.num_children = 0; + list_head_init(ref(&parent.children, parent_count)); + ok1(parent_count == 1); + /* Test list_head_init */ + ok1(list_empty(ref(&parent.children, parent_count))); + ok1(parent_count == 2); + ok1(list_check(ref(&parent.children, parent_count), NULL)); + ok1(parent_count == 3); + + c2.name = "c2"; + list_add(ref(&parent.children, parent_count), &c2.list); + ok1(parent_count == 4); + /* Test list_add and !list_empty. */ + ok1(!list_empty(ref(&parent.children, parent_count))); + ok1(parent_count == 5); + ok1(c2.list.next == &parent.children.n); + ok1(c2.list.prev == &parent.children.n); + ok1(parent.children.n.next == &c2.list); + ok1(parent.children.n.prev == &c2.list); + /* Test list_check */ + ok1(list_check(ref(&parent.children, parent_count), NULL)); + ok1(parent_count == 6); + + c1.name = "c1"; + list_add(ref(&parent.children, parent_count), &c1.list); + ok1(parent_count == 7); + /* Test list_add and !list_empty. */ + ok1(!list_empty(ref(&parent.children, parent_count))); + ok1(parent_count == 8); + ok1(c2.list.next == &parent.children.n); + ok1(c2.list.prev == &c1.list); + ok1(parent.children.n.next == &c1.list); + ok1(parent.children.n.prev == &c2.list); + ok1(c1.list.next == &c2.list); + ok1(c1.list.prev == &parent.children.n); + /* Test list_check */ + ok1(list_check(ref(&parent.children, parent_count), NULL)); + ok1(parent_count == 9); + + c3.name = "c3"; + list_add_tail(ref(&parent.children, parent_count), &c3.list); + ok1(parent_count == 10); + /* Test list_add_tail and !list_empty. */ + ok1(!list_empty(ref(&parent.children, parent_count))); + ok1(parent_count == 11); + ok1(parent.children.n.next == &c1.list); + ok1(parent.children.n.prev == &c3.list); + ok1(c1.list.next == &c2.list); + ok1(c1.list.prev == &parent.children.n); + ok1(c2.list.next == &c3.list); + ok1(c2.list.prev == &c1.list); + ok1(c3.list.next == &parent.children.n); + ok1(c3.list.prev == &c2.list); + /* Test list_check */ + ok1(list_check(ref(&parent.children, parent_count), NULL)); + ok1(parent_count == 12); + + /* Test list_check_node */ + ok1(list_check_node(&c1.list, NULL)); + ok1(list_check_node(&c2.list, NULL)); + ok1(list_check_node(&c3.list, NULL)); + + /* Test list_top */ + ok1(list_top(ref(&parent.children, parent_count), struct child, list) == &c1); + ok1(parent_count == 13); + + /* Test list_tail */ + ok1(list_tail(ref(&parent.children, parent_count), struct child, list) == &c3); + ok1(parent_count == 14); + + /* Test list_for_each. */ + i = 0; + list_for_each(&parent.children, c, list) { + switch (i++) { + case 0: + ok1(c == &c1); + break; + case 1: + ok1(c == &c2); + break; + case 2: + ok1(c == &c3); + break; + } + if (i > 2) + break; + } + ok1(i == 3); + + /* Test list_for_each_safe, list_del and list_del_from. */ + i = 0; + list_for_each_safe(&parent.children, c, n, list) { + switch (i++) { + case 0: + ok1(c == &c1); + list_del(ref(&c->list, node_count)); + ok1(node_count == 1); + break; + case 1: + ok1(c == &c2); + list_del_from(ref(&parent.children, parent_count), + ref(&c->list, node_count)); + ok1(node_count == 2); + break; + case 2: + ok1(c == &c3); + list_del_from(ref(&parent.children, parent_count), + ref(&c->list, node_count)); + ok1(node_count == 3); + break; + } + ok1(list_check(ref(&parent.children, parent_count), NULL)); + if (i > 2) + break; + } + ok1(i == 3); + ok1(parent_count == 19); + ok1(list_empty(ref(&parent.children, parent_count))); + ok1(parent_count == 20); + + /* Test list_top/list_tail on empty list. */ + ok1(list_top(ref(&parent.children, parent_count), struct child, list) == NULL); + ok1(parent_count == 21); + ok1(list_tail(ref(&parent.children, parent_count), struct child, list) == NULL); + ok1(parent_count == 22); + return exit_status(); +}