From: Rusty Russell Date: Fri, 28 Dec 2007 12:59:36 +0000 (+1100) Subject: New package: list, complete reimplementation of Linux's list macros. X-Git-Url: https://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=2040a0c4726800b60f0a1967989c0004ca30d1d9 New package: list, complete reimplementation of Linux's list macros. --- diff --git a/list/_info.c b/list/_info.c new file mode 100644 index 00000000..5e228821 --- /dev/null +++ b/list/_info.c @@ -0,0 +1,63 @@ +#include +#include +#include "config.h" + +/** + * list - double linked list routines + * + * The list header contains routines for manipulating double linked lists. + * It defined two types: struct list_head used for anchoring lists, and + * struct list_node which is usually embedded in the structure which is placed + * in the list. + * + * Example: + * #include + * #include "list/list.h" + * + * struct parent { + * const char *name; + * struct list_head children; + * unsigned int num_children; + * }; + * + * struct child { + * const char *name; + * struct list_node list; + * }; + * + * int main(int argc, char *argv[]) + * { + * struct parent p; + * struct child *c; + * unsigned int i; + * + * if (argc < 2) + * errx(1, "Usage: %s parent children...", argv[0]); + * + * p.name = argv[1]; + * for (i = 2; i < argc, i++) { + * c = malloc(sizeof(*c)); + * c->name = argv[i]; + * list_add(&p.children, &c->list); + * p.num_children++; + * } + * + * printf("%s has %u children:", p.name, p.num_children); + * list_for_each(&p.children, c, list) + * printf("%s ", c->name); + * printf("\n"); + * return 0; + * } + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("container_of\n"); + return 0; + } + + return 1; +} diff --git a/list/list.c b/list/list.c new file mode 100644 index 00000000..b72f8f67 --- /dev/null +++ b/list/list.c @@ -0,0 +1,33 @@ +#include +#include +#include "list/list.h" + +struct list_head *list_check(struct list_head *h, const char *abortstr) +{ + struct list_node *n, *p; + int count = 0; + + if (h->n.next == &h->n) { + if (h->n.prev != &h->n) { + if (!abortstr) + return NULL; + fprintf(stderr, "%s: prev corrupt in empty %p\n", + abortstr, h); + abort(); + } + return h; + } + + for (p = &h->n, n = h->n.next; n != &h->n; p = n, n = n->next) { + count++; + if (n->prev != p) { + if (!abortstr) + return NULL; + fprintf(stderr, + "%s: prev corrupt in node %p (%u) of %p\n", + abortstr, n, count, h); + abort(); + } + } + return h; +} diff --git a/list/list.h b/list/list.h new file mode 100644 index 00000000..c664d834 --- /dev/null +++ b/list/list.h @@ -0,0 +1,253 @@ +#ifndef CCAN_LIST_H +#define CCAN_LIST_H +#include +#include "container_of/container_of.h" + +/** + * struct list_node - an entry in a doubly-linked list + * @next: next entry (self if empty) + * @prev: previous entry (self if empty) + * + * This is used as an entry in a linked list. + * Example: + * struct child { + * const char *name; + * // Linked list of all us children. + * struct list_node list; + * }; + */ +struct list_node +{ + struct list_node *next, *prev; +}; + +/** + * struct list_head - the head of a doubly-linked list + * @h: the list_head (containing next and prev pointers) + * + * This is used as the head of a linked list. + * Example: + * struct parent { + * const char *name; + * struct list_head children; + * unsigned int num_children; + * }; + */ +struct list_head +{ + struct list_node n; +}; + +/** + * list_check - check a list for consistency + * @h: the list_head + * @abortstr: the location to print on aborting, or NULL. + * + * Because list_nodes have redundant information, consistency checking between + * the back and forward links can be done. This is useful as a debugging check. + * If @abortstr is non-NULL, that will be printed in a diagnostic if the list + * is inconsistent, and the function will abort. + * + * Returns the list head if the list is consistent, NULL if not (it + * can never return NULL if @abortstr is set). + * + * Example: + * static void dump_parent(struct parent *p) + * { + * struct child *c; + * + * printf("%s (%u children):\n", p->name, parent->num_children); + * list_check(&p->children, "bad child list"); + * list_for_each(&p->children, c, list) + * printf(" -> %s\n", c->name); + * } + */ +struct list_head *list_check(struct list_head *h, const char *abortstr); + +#ifdef CCAN_LIST_DEBUG +#define debug_list(h) list_check((h), __func__) +#else +#define debug_list(h) (h) +#endif + +/** + * list_head_init - initialize a list_head + * @h: the list_head to set to the empty list + * + * Example: + * list_head_init(&parent->children); + * parent->num_children = 0; + */ +static inline void list_head_init(struct list_head *h) +{ + h->n.next = h->n.prev = &h->n; +} + +/** + * LIST_HEAD - define and initalized empty list_head + * @name: the name of the list. + * + * The LIST_HEAD macro defines a list_head and initializes it to an empty + * list. It can be prepended by "static" to define a static list_head. + * + * Example: + * // Header: + * extern struct list_head my_list; + * + * // C file: + * LIST_HEAD(my_list); + */ +#define LIST_HEAD(name) \ + struct list_head name = { { &name.n, &name.n } } + +/** + * list_add - add an entry at the start of a linked list. + * @h: the list_head to add the node to + * @n: the list_node to add to the list. + * + * The list_node does not need to be initialized; it will be overwritten. + * Example: + * list_add(&parent->children, &child->list); + * parent->num_children++; + */ +static inline void list_add(struct list_head *h, struct list_node *n) +{ + n->next = h->n.next; + n->prev = &h->n; + h->n.next->prev = n; + h->n.next = n; + (void)debug_list(h); +} + +/** + * list_add_tail - add an entry at the end of a linked list. + * @h: the list_head to add the node to + * @n: the list_node to add to the list. + * + * The list_node does not need to be initialized; it will be overwritten. + * Example: + * list_add_tail(&parent->children, &child->list); + * parent->num_children++; + */ +static inline void list_add_tail(struct list_head *h, struct list_node *n) +{ + n->next = &h->n; + n->prev = h->n.prev; + h->n.prev->next = n; + h->n.prev = n; + (void)debug_list(h); +} + +/** + * list_del - delete an entry from a linked list. + * @n: the list_node to delete from the list. + * + * Example: + * list_del(&child->list); + * parent->num_children--; + */ +static inline void list_del(struct list_node *n) +{ + n->next->prev = n->prev; + n->prev->next = n->next; + (void)debug_list(n->next); +#ifdef CCAN_LIST_DEBUG + /* Catch use-after-del. */ + n->next = n->prev = NULL; +#endif +} + +/** + * list_empty - is a list empty? + * @h: the list_head + * + * If the list is empty, returns true. + * + * Example: + * assert(list_empty(&parent->children) == (parent->num_children == 0)); + */ +static inline bool list_empty(struct list_head *h) +{ + (void)debug_list(h); + return h->n.next == &h->n; +} + +/** + * list_entry - convert a list_node back into the structure containing it. + * @n: the list_node + * @type: the type of the entry + * @member: the list_node member of the type + * + * Example: + * struct child *c; + * // First list entry is children.next; convert back to child. + * c = list_entry(parent->children.next, struct child, list); + */ +#define list_entry(n, type, member) container_of(n, type, member) + +/** + * list_top - get the first entry in a list + * @h: the list_head + * @type: the type of the entry + * @member: the list_node member of the type + * + * If the list is empty, returns NULL. + * + * Example: + * struct child *first; + * first = list_top(&parent->children, struct child, list); + */ +#define list_top(h, type, member) \ + list_entry(_list_top(h), type, member) + +static inline struct list_node *_list_top(struct list_head *h) +{ + (void)debug_list(h); + if (list_empty(h)) + return NULL; + return h->n.next; +} + +/** + * list_for_each - iterate through a list. + * @h: the list_head + * @i: 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. It's + * a for loop, so you can break and continue as normal. + * + * Example: + * struct child *c; + * list_for_each(&parent->children, c, list) + * printf("Name: %s\n", c->name); + */ +#define list_for_each(h, i, member) \ + for (i = container_of_var(debug_list(h)->n.next, i, member); \ + &i->member != &(h)->n; \ + i = container_of_var(i->member.next, i, member)) + +/** + * list_for_each_safe - iterate through a list, 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. 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 *c, *n; + * list_for_each_safe(&parent->children, c, n, list) { + * list_del(&c->list); + * parent->num_children--; + * } + */ +#define list_for_each_safe(h, i, nxt, member) \ + for (i = container_of_var(debug_list(h)->n.next, i, member), \ + nxt = container_of_var(i->member.next, i, member); \ + &i->member != &(h)->n; \ + i = nxt, nxt = container_of_var(i->member.next, i, member)) +#endif /* CCAN_LIST_H */ diff --git a/list/test/run.c b/list/test/run.c new file mode 100644 index 00000000..2297aede --- /dev/null +++ b/list/test/run.c @@ -0,0 +1,118 @@ +#include "list/list.h" +#include "tap.h" +#include "list/list.c" + +struct parent { + const char *name; + struct list_head children; + unsigned int num_children; +}; + +struct child { + const char *name; + struct list_node list; +}; + +static LIST_HEAD(static_list); + +int main(int argc, char *argv[]) +{ + struct parent parent; + struct child c1, c2, c3, *c, *n; + unsigned int i; + + plan_tests(41); + /* Test LIST_HEAD, list_empty and check_list */ + ok1(list_empty(&static_list)); + ok1(list_check(&static_list, NULL)); + + parent.num_children = 0; + list_head_init(&parent.children); + /* Test list_head_init */ + ok1(list_empty(&parent.children)); + ok1(list_check(&parent.children, NULL)); + + c2.name = "c2"; + list_add(&parent.children, &c2.list); + /* Test list_add and !list_empty. */ + ok1(!list_empty(&parent.children)); + 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(&parent.children, NULL)); + + c1.name = "c1"; + list_add(&parent.children, &c1.list); + /* Test list_add and !list_empty. */ + ok1(!list_empty(&parent.children)); + 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(&parent.children, NULL)); + + c3.name = "c3"; + list_add_tail(&parent.children, &c3.list); + /* Test list_add_tail and !list_empty. */ + ok1(!list_empty(&parent.children)); + 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(&parent.children, NULL)); + + /* Test list_top */ + ok1(list_top(&parent.children, struct child, list) == &c1); + + /* 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 and list_del. */ + i = 0; + list_for_each_safe(&parent.children, c, n, list) { + switch (i++) { + case 0: + ok1(c == &c1); + break; + case 1: + ok1(c == &c2); + break; + case 2: + ok1(c == &c3); + break; + } + list_del(&c->list); + ok1(list_check(&parent.children, NULL)); + if (i > 2) + break; + } + ok1(i == 3); + ok1(list_empty(&parent.children)); + return exit_status(); +}