From: Cody P Schafer Date: Tue, 31 May 2016 19:09:13 +0000 (-0400) Subject: tlist2: a alternate to tlist that encodes the member offset into the container type X-Git-Url: http://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=7a4e71c4180eaecc8799c050eeb052b0de8cf302 tlist2: a alternate to tlist that encodes the member offset into the container type Signed-off-by: Cody P Schafer --- diff --git a/Makefile-ccan b/Makefile-ccan index 56dc82cd..47249b90 100644 --- a/Makefile-ccan +++ b/Makefile-ccan @@ -123,6 +123,7 @@ MODS := a_star \ time \ timer \ tlist \ + tlist2 \ ttxml \ typesafe_cb \ version \ diff --git a/ccan/tlist2/LICENSE b/ccan/tlist2/LICENSE new file mode 120000 index 00000000..74550445 --- /dev/null +++ b/ccan/tlist2/LICENSE @@ -0,0 +1 @@ +../../licenses/LGPL-3 \ No newline at end of file diff --git a/ccan/tlist2/_info b/ccan/tlist2/_info new file mode 100644 index 00000000..3546beb0 --- /dev/null +++ b/ccan/tlist2/_info @@ -0,0 +1,78 @@ +#include "config.h" +#include +#include + +/** + * tlist2 - typesafe double linked list routines, alternative form + * + * The list header contains routines for manipulating double linked lists; + * this extends it so you can create list head types which only accomodate + * a specific entry type. + * + * Compared to 'tlist', this: + * + * - does not allow (or require) declaring an extra struct (uses anonymous + * structs) + * - encodes the member offset into the type (and as a result doesn't need the + * member name, but requires the source list) + * + * Based on tlist by: Rusty Russell + * + * Example: + * #include + * #include + * #include + * #include + * + * struct child { + * const char *name; + * struct list_node list; + * }; + * + * struct parent { + * const char *name; + * TLIST2(struct child, list) children; + * unsigned int num_children; + * }; + * + * 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]; + * tlist2_init(&p.children); + * for (i = 2; i < argc; i++) { + * c = malloc(sizeof(*c)); + * c->name = argv[i]; + * tlist2_add(&p.children, c); + * p.num_children++; + * } + * + * printf("%s has %u children:", p.name, p.num_children); + * tlist2_for_each(&p.children, c) + * printf("%s ", c->name); + * printf("\n"); + * return 0; + * } + * + * License: LGPL + * Author: Cody P Schafer + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/list\n"); + printf("ccan/tcon\n"); + return 0; + } + + return 1; +} diff --git a/ccan/tlist2/test/.c b/ccan/tlist2/test/.c new file mode 100644 index 00000000..29b08fe5 --- /dev/null +++ b/ccan/tlist2/test/.c @@ -0,0 +1,35 @@ +#include + +TLIST_TYPE(children, struct child); +TLIST_TYPE(cousins, struct cousin); + +struct child { + const char *name; + struct list_node list; +}; + +struct cousin { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + struct tlist2_children children; + struct tlist2_cousins cousins; + struct child child = { "child" }; + struct cousin cousin = { "cousin" }; + + tlist2_init(&children); + tlist2_init(&cousins); + tlist2_add(&children, &child, list); + tlist2_add(&cousins, &cousin, list); + tlist2_del_from(&cousins, &cousin, list); +#ifdef FAIL +#if !HAVE_FLEXIBLE_ARRAY_MEMBER +#error Need flexible array members to check type +#endif + tlist2_add(&children, &cousin, list); +#endif + return 0; +} diff --git a/ccan/tlist2/test/compile_fail-tlist_add_2.c b/ccan/tlist2/test/compile_fail-tlist_add_2.c new file mode 100644 index 00000000..2151b2d1 --- /dev/null +++ b/ccan/tlist2/test/compile_fail-tlist_add_2.c @@ -0,0 +1,32 @@ +#include + +struct child { + const char *name; + struct list_node list; +}; + +struct cousin { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + TLIST2(struct child, list) children; + TLIST2(struct cousin, list) cousins; + struct child child = { "child" }; + struct cousin cousin = { "cousin" }; + + tlist2_init(&children); + tlist2_init(&cousins); + tlist2_add(&children, &child); + tlist2_add(&cousins, &cousin); + tlist2_del_from(&cousins, &cousin); +#ifdef FAIL +#if !HAVE_FLEXIBLE_ARRAY_MEMBER +#error Need flexible array members to check type +#endif + tlist2_add(&children, &cousin); +#endif + return 0; +} diff --git a/ccan/tlist2/test/compile_fail-tlist_add_tail_2.c b/ccan/tlist2/test/compile_fail-tlist_add_tail_2.c new file mode 100644 index 00000000..2b4652cc --- /dev/null +++ b/ccan/tlist2/test/compile_fail-tlist_add_tail_2.c @@ -0,0 +1,32 @@ +#include + +struct child { + const char *name; + struct list_node list; +}; + +struct cousin { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + TLIST2(struct child, list) children; + TLIST2(struct cousin, list) cousins; + struct child child = { "child" }; + struct cousin cousin = { "cousin" }; + + tlist2_init(&children); + tlist2_init(&cousins); + tlist2_add(&children, &child); + tlist2_add(&cousins, &cousin); + tlist2_del_from(&cousins, &cousin); +#ifdef FAIL +#if !HAVE_FLEXIBLE_ARRAY_MEMBER +#error Need flexible array members to check type +#endif + tlist2_add_tail(&children, &cousin); +#endif + return 0; +} diff --git a/ccan/tlist2/test/compile_fail-tlist_del_from_2.c b/ccan/tlist2/test/compile_fail-tlist_del_from_2.c new file mode 100644 index 00000000..c5651e58 --- /dev/null +++ b/ccan/tlist2/test/compile_fail-tlist_del_from_2.c @@ -0,0 +1,31 @@ +#include + +struct child { + const char *name; + struct list_node list; +}; + +struct cousin { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + TLIST2(struct child, list) children; + TLIST2(struct cousin, list) cousins; + struct child child = { "child" }; + struct cousin cousin = { "cousin" }; + + tlist2_init(&children); + tlist2_init(&cousins); + tlist2_add(&children, &child); + tlist2_add(&cousins, &cousin); +#ifdef FAIL +#if !HAVE_FLEXIBLE_ARRAY_MEMBER +#error Need flexible array members to check type +#endif + tlist2_del_from(&children, &cousin); +#endif + return 0; +} diff --git a/ccan/tlist2/test/compile_fail-tlist_for_each_2.c b/ccan/tlist2/test/compile_fail-tlist_for_each_2.c new file mode 100644 index 00000000..776d141d --- /dev/null +++ b/ccan/tlist2/test/compile_fail-tlist_for_each_2.c @@ -0,0 +1,32 @@ +#include + +struct child { + const char *name; + struct list_node list; +}; + +struct cousin { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + TLIST2(struct child, list) children; + struct child child = { "child" }; +#ifdef FAIL +#if !HAVE_FLEXIBLE_ARRAY_MEMBER +#error Need flexible array members to check type +#endif + struct cousin *c; +#else + struct child *c; +#endif + + tlist2_init(&children); + tlist2_add(&children, &child); + + tlist2_for_each(&children, c) + (void) c; /* Suppress unused-but-set-variable warning. */ + return 0; +} diff --git a/ccan/tlist2/test/compile_fail-tlist_for_each_safe_2.c b/ccan/tlist2/test/compile_fail-tlist_for_each_safe_2.c new file mode 100644 index 00000000..d06f64a4 --- /dev/null +++ b/ccan/tlist2/test/compile_fail-tlist_for_each_safe_2.c @@ -0,0 +1,31 @@ +#include + +struct child { + const char *name; + struct list_node list; +}; + +struct cousin { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + TLIST2(struct child, list) children; + struct child child = { "child" }; +#ifdef FAIL +#if !HAVE_FLEXIBLE_ARRAY_MEMBER +#error Need flexible array members to check type +#endif + struct cousin *c, *n; +#else + struct child *c, *n; +#endif + + tlist2_init(&children); + tlist2_add(&children, &child); + + tlist2_for_each_safe(&children, c, n); + return 0; +} diff --git a/ccan/tlist2/test/compile_fail-tlist_tail_2.c b/ccan/tlist2/test/compile_fail-tlist_tail_2.c new file mode 100644 index 00000000..e30e1cd3 --- /dev/null +++ b/ccan/tlist2/test/compile_fail-tlist_tail_2.c @@ -0,0 +1,29 @@ +#include + +struct child { + const char *name; + struct list_node list; +}; + +struct cousin { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + TLIST2(struct child, list) children; + struct child child = { "child" }; +#ifdef FAIL + struct cousin *c; +#else + struct child *c; +#endif + + tlist2_init(&children); + tlist2_add(&children, &child); + + c = tlist2_tail(&children); + (void) c; /* Suppress unused-but-set-variable warning. */ + return 0; +} diff --git a/ccan/tlist2/test/compile_fail-tlist_top_2.c b/ccan/tlist2/test/compile_fail-tlist_top_2.c new file mode 100644 index 00000000..149dd58a --- /dev/null +++ b/ccan/tlist2/test/compile_fail-tlist_top_2.c @@ -0,0 +1,29 @@ +#include + +struct child { + const char *name; + struct list_node list; +}; + +struct cousin { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + TLIST2(struct child, list) children; + struct child child = { "child" }; +#ifdef FAIL + struct cousin *c; +#else + struct child *c; +#endif + + tlist2_init(&children); + tlist2_add(&children, &child); + + c = tlist2_top(&children); + (void) c; /* Suppress unused-but-set-variable warning. */ + return 0; +} diff --git a/ccan/tlist2/test/run_2.c b/ccan/tlist2/test/run_2.c new file mode 100644 index 00000000..43f11281 --- /dev/null +++ b/ccan/tlist2/test/run_2.c @@ -0,0 +1,145 @@ +#define CCAN_LIST_DEBUG 1 +#include +#include + +struct child { + const char *name; + struct list_node list; +}; + +struct parent { + const char *name; + TLIST2(struct child, list) children; + unsigned int num_children; +}; + +int main(int argc, char *argv[]) +{ + struct parent parent; + struct child c1, c2, c3, *c, *n; + unsigned int i; + TLIST2(struct child, list) tlist = TLIST2_INIT(tlist); + + plan_tests(48); + /* Test TLIST2_INIT, and tlist2_empty */ + ok1(tlist2_empty(&tlist)); + ok1(tlist2_check(&tlist, NULL)); + + parent.num_children = 0; + tlist2_init(&parent.children); + /* Test tlist2_init */ + ok1(tlist2_empty(&parent.children)); + ok1(tlist2_check(&parent.children, NULL)); + + c2.name = "c2"; + tlist2_add(&parent.children, &c2); + /* Test tlist2_add and !tlist2_empty. */ + ok1(!tlist2_empty(&parent.children)); + ok1(c2.list.next == &tlist2_unwrap(&parent.children)->n); + ok1(c2.list.prev == &tlist2_unwrap(&parent.children)->n); + ok1(tlist2_unwrap(&parent.children)->n.next == &c2.list); + ok1(tlist2_unwrap(&parent.children)->n.prev == &c2.list); + /* Test tlist2_check */ + ok1(tlist2_check(&parent.children, NULL)); + + c1.name = "c1"; + tlist2_add(&parent.children, &c1); + /* Test list_add and !list_empty. */ + ok1(!tlist2_empty(&parent.children)); + ok1(c2.list.next == &tlist2_unwrap(&parent.children)->n); + ok1(c2.list.prev == &c1.list); + ok1(tlist2_unwrap(&parent.children)->n.next == &c1.list); + ok1(tlist2_unwrap(&parent.children)->n.prev == &c2.list); + ok1(c1.list.next == &c2.list); + ok1(c1.list.prev == &tlist2_unwrap(&parent.children)->n); + /* Test tlist2_check */ + ok1(tlist2_check(&parent.children, NULL)); + + c3.name = "c3"; + tlist2_add_tail(&parent.children, &c3); + /* Test list_add_tail and !list_empty. */ + ok1(!tlist2_empty(&parent.children)); + ok1(tlist2_unwrap(&parent.children)->n.next == &c1.list); + ok1(tlist2_unwrap(&parent.children)->n.prev == &c3.list); + ok1(c1.list.next == &c2.list); + ok1(c1.list.prev == &tlist2_unwrap(&parent.children)->n); + ok1(c2.list.next == &c3.list); + ok1(c2.list.prev == &c1.list); + ok1(c3.list.next == &tlist2_unwrap(&parent.children)->n); + ok1(c3.list.prev == &c2.list); + /* Test tlist2_check */ + ok1(tlist2_check(&parent.children, NULL)); + + /* Test tlist2_top */ + ok1(tlist2_top(&parent.children) == &c1); + + /* Test list_tail */ + ok1(tlist2_tail(&parent.children) == &c3); + + /* Test tlist2_for_each. */ + i = 0; + tlist2_for_each(&parent.children, c) { + 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 tlist2_for_each_rev. */ + i = 0; + tlist2_for_each_rev(&parent.children, c) { + switch (i++) { + case 0: + ok1(c == &c3); + break; + case 1: + ok1(c == &c2); + break; + case 2: + ok1(c == &c1); + break; + } + if (i > 2) + break; + } + ok1(i == 3); + + /* Test tlist2_for_each_safe, tlist2_del and tlist2_del_from. */ + i = 0; + tlist2_for_each_safe(&parent.children, c, n) { + switch (i++) { + case 0: + ok1(c == &c1); + tlist2_del_from(&parent.children, c); + break; + case 1: + ok1(c == &c2); + tlist2_del_from(&parent.children, c); + break; + case 2: + ok1(c == &c3); + tlist2_del_from(&parent.children, c); + break; + } + ok1(tlist2_check(&parent.children, NULL)); + if (i > 2) + break; + } + ok1(i == 3); + ok1(tlist2_empty(&parent.children)); + + /* Test list_top/list_tail on empty list. */ + ok1(tlist2_top(&parent.children) == (struct child *)NULL); + ok1(tlist2_tail(&parent.children) == (struct child *)NULL); + return exit_status(); +} diff --git a/ccan/tlist2/tlist2.h b/ccan/tlist2/tlist2.h new file mode 100644 index 00000000..82f20eca --- /dev/null +++ b/ccan/tlist2/tlist2.h @@ -0,0 +1,242 @@ +/* Licensed under LGPL - see LICENSE file for details */ +#ifndef CCAN_TLIST2_H +#define CCAN_TLIST2_H +#include +#include + +/** + * TLIST2 - declare a typed list type (struct tlist) + * @etype: the type the list will contain + * @link: the name of the member of @etype that is the link + * + * This declares an anonymous structure to use for lists containing this type. + * The actual list can be accessed using tlist2_raw(). + * + * Example: + * #include + * #include + * struct child { + * const char *name; + * struct list_node list; + * }; + * struct parent { + * const char *name; + * TLIST2(struct child, list) children; + * unsigned int num_children; + * }; + * + */ +#define TLIST2(etype, link) \ + TCON_WRAP(struct list_head, \ + TCON_CONTAINER(canary, etype, link)) + +/** + * TLIST2_INIT - initalizer for an empty tlist + * @name: the name of the list. + * + * Explicit initializer for an empty list. + * + * See also: + * tlist2_init() + * + * Example: + * TLIST2(struct child, list) my_list = TLIST2_INIT(my_list); + */ +#define TLIST2_INIT(name) TCON_WRAP_INIT( LIST_HEAD_INIT(*tcon_unwrap(&(name))) ) + +/** + * tlist2_check - check head of a list for consistency + * @h: the tlist2 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 non-NULL if the list is consistent, NULL otherwise (it + * can never return NULL if @abortstr is set). + * + * See also: list_check() + * + * Example: + * static void dump_parent(struct parent *p) + * { + * struct child *c; + * + * printf("%s (%u children):\n", p->name, p->num_children); + * tlist2_check(&p->children, "bad child list"); + * tlist2_for_each(&p->children, c) + * printf(" -> %s\n", c->name); + * } + */ +#define tlist2_check(h, abortstr) \ + list_check(tcon_unwrap(h), (abortstr)) + +/** + * tlist2_init - initialize a tlist + * @h: the tlist to set to the empty list + * + * Example: + * ... + * struct parent *parent = malloc(sizeof(*parent)); + * + * tlist2_init(&parent->children); + * parent->num_children = 0; + */ +#define tlist2_init(h) list_head_init(tcon_unwrap(h)) + +/** + * tlist2_raw - unwrap the typed list and check the type + * @h: the tlist + * @expr: the expression to check the type against (not evaluated) + * + * This macro usually causes the compiler to emit a warning if the + * variable is of an unexpected type. It is used internally where we + * need to access the raw underlying list. + */ +#define tlist2_raw(h, expr) tcon_unwrap(tcon_container_check_ptr(h, canary, expr)) + +/** + * tlist2_unwrap - unwrap the typed list without any checks + * @h: the tlist + */ +#define tlist2_unwrap(h) tcon_unwrap(h) + +/** + * tlist2_add - add an entry at the start of a linked list. + * @h: the tlist to add the node to + * @n: the entry to add to the list. + * + * The entry's list_node does not need to be initialized; it will be + * overwritten. + * Example: + * struct child *child = malloc(sizeof(*child)); + * + * child->name = "marvin"; + * tlist2_add(&parent->children, child); + * parent->num_children++; + */ +#define tlist2_add(h, n) list_add(tlist2_raw((h), (n)), tcon_member_of(h, canary, n)) + +/** + * tlist2_add_tail - add an entry at the end of a linked list. + * @h: the tlist to add the node to + * @n: the entry to add to the list. + * + * The list_node does not need to be initialized; it will be overwritten. + * Example: + * tlist2_add_tail(&parent->children, child); + * parent->num_children++; + */ +#define tlist2_add_tail(h, n) \ + list_add_tail(tlist2_raw((h), (n)), tcon_member_of((h), canary, (n))) + +/** + * tlist2_del_from - delete an entry from a linked list. + * @h: the tlist @n is in + * @n: the entry to delete + * + * This explicitly indicates which list a node is expected to be in, + * which is better documentation and can catch more bugs. + * + * Note that this leaves @n->@member in an undefined state; it + * can be added to another list, but not deleted again. + * + * Example: + * tlist2_del_from(&parent->children, child); + * parent->num_children--; + */ +#define tlist2_del_from(h, n) \ + list_del_from(tlist2_raw((h), (n)), tcon_member_of((h), canary, (n))) + +/** + * tlist2_empty - is a list empty? + * @h: the tlist + * + * If the list is empty, returns true. + * + * Example: + * assert(tlist2_empty(&parent->children) == (parent->num_children == 0)); + */ +#define tlist2_empty(h) list_empty(tcon_unwrap(h)) + +/** + * tlist2_top - get the first entry in a list + * @h: the tlist + * + * If the list is empty, returns NULL. + * + * Example: + * struct child *first; + * first = tlist2_top(&parent->children); + * if (!first) + * printf("Empty list!\n"); + */ +#define tlist2_top(h) tcon_container_of((h), canary, list_top_(tcon_unwrap(h), 0)) + +/** + * tlist2_tail - get the last entry in a list + * @h: the tlist + * + * If the list is empty, returns NULL. + * + * Example: + * struct child *last; + * last = tlist2_tail(&parent->children); + * if (!last) + * printf("Empty list!\n"); + */ +#define tlist2_tail(h) tcon_container_of((h), canary, list_tail_(tcon_unwrap(h), 0)) + +/** + * tlist2_for_each - iterate through a list. + * @h: the tlist + * @i: an iterator of suitable type for this list. + * + * 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: + * tlist2_for_each(&parent->children, child) + * printf("Name: %s\n", child->name); + */ +#define tlist2_for_each(h, i) \ + list_for_each_off(tlist2_raw((h), (i)), (i), tcon_offset((h), canary)) + +/** + * tlist2_for_each_rev - iterate through a list backwards. + * @h: the tlist + * @i: an iterator of suitable type for this list. + * + * 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: + * tlist2_for_each_rev(&parent->children, child) + * printf("Name: %s\n", child->name); + */ +#define tlist2_for_each_rev(h, i) \ + list_for_each_rev_off(tlist2_raw((h), (i)), (i), tcon_offset((h), canary)) + +/** + * tlist2_for_each_safe - iterate through a list, maybe during deletion + * @h: the tlist + * @i: an iterator of suitable type for this list. + * @nxt: another iterator to store the next entry. + * + * 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 *next; + * tlist2_for_each_safe(&parent->children, child, next) { + * tlist2_del_from(&parent->children, child); + * parent->num_children--; + * } + */ +#define tlist2_for_each_safe(h, i, nxt) \ + list_for_each_safe_off(tlist2_raw((h), (i)), (i), (nxt), tcon_offset((h), canary)) + +#endif /* CCAN_TLIST2_H */