Commit 7a4e71c4 authored by Cody P Schafer's avatar Cody P Schafer Committed by Rusty Russell

tlist2: a alternate to tlist that encodes the member offset into the container type

Signed-off-by: default avatarCody P Schafer <dev@codyps.com>
parent 95573b36
...@@ -123,6 +123,7 @@ MODS := a_star \ ...@@ -123,6 +123,7 @@ MODS := a_star \
time \ time \
timer \ timer \
tlist \ tlist \
tlist2 \
ttxml \ ttxml \
typesafe_cb \ typesafe_cb \
version \ version \
......
../../licenses/LGPL-3
\ No newline at end of file
#include "config.h"
#include <stdio.h>
#include <string.h>
/**
* 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 <rusty@rustcorp.com.au>
*
* Example:
* #include <err.h>
* #include <stdio.h>
* #include <stdlib.h>
* #include <ccan/tlist2/tlist2.h>
*
* 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 <dev@codyps.com>
*/
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;
}
#include <ccan/tlist/tlist.h>
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;
}
#include <ccan/tlist2/tlist2.h>
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;
}
#include <ccan/tlist2/tlist2.h>
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;
}
#include <ccan/tlist2/tlist2.h>
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;
}
#include <ccan/tlist2/tlist2.h>
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;
}
#include <ccan/tlist2/tlist2.h>
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;
}
#include <ccan/tlist2/tlist2.h>
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;
}
#include <ccan/tlist2/tlist2.h>
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;
}
#define CCAN_LIST_DEBUG 1
#include <ccan/tlist2/tlist2.h>
#include <ccan/tap/tap.h>
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();
}
/* Licensed under LGPL - see LICENSE file for details */
#ifndef CCAN_TLIST2_H
#define CCAN_TLIST2_H
#include <ccan/list/list.h>
#include <ccan/tcon/tcon.h>
/**
* 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 <ccan/list/list.h>
* #include <ccan/tlist2/tlist2.h>
* 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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment