aboutsummaryrefslogtreecommitdiff
path: root/ccan/list
diff options
context:
space:
mode:
authorNicholas Piggin <npiggin@gmail.com>2021-12-09 00:15:58 +1000
committerCédric Le Goater <clg@kaod.org>2021-12-09 11:09:01 +0100
commit10d50007fb289ed5a625aa3c195b3a953f1e64c1 (patch)
tree219a676b25093770d7424872e628318b3045fd58 /ccan/list
parent03c0a9f086537310afe4ce2f5168e4fc41418f33 (diff)
downloadskiboot-10d50007fb289ed5a625aa3c195b3a953f1e64c1.zip
skiboot-10d50007fb289ed5a625aa3c195b3a953f1e64c1.tar.gz
skiboot-10d50007fb289ed5a625aa3c195b3a953f1e64c1.tar.bz2
ccan: sync to upstream ccan.git commit ca7c5a9e04f3
sync to upstream ccan.git commit ca7c5a9e04f3 ("ccan: make tal_dump() format more regular."). The recipe used to sync upstream is: $ cd ccan $ ./tools/create-ccan-tree -b make tmp \ array_size check_type container_of heap \ short_types build_assert endian list str $ # replace directories in skiboot/ccan/ with those in tmp/ccan/ $ cd ../skiboot $ patch -p1 < ccan/skiboot.patch This also adds a README.skiboot to help with future updates. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Cédric Le Goater <clg@kaod.org>
Diffstat (limited to 'ccan/list')
-rw-r--r--ccan/list/_info6
-rw-r--r--ccan/list/list.h455
-rw-r--r--ccan/list/test/run-CCAN_LIST_DEBUG.c60
-rw-r--r--ccan/list/test/run-check-corrupt.c9
-rw-r--r--ccan/list/test/run-check-nonconst.c27
-rw-r--r--ccan/list/test/run-list_del_from-assert.c6
-rw-r--r--ccan/list/test/run-list_prev-list_next.c65
-rw-r--r--ccan/list/test/run-prepend_list.c111
-rw-r--r--ccan/list/test/run-single-eval.c5
-rw-r--r--ccan/list/test/run.c116
10 files changed, 737 insertions, 123 deletions
diff --git a/ccan/list/_info b/ccan/list/_info
index 41a81fb..c4f3e2a 100644
--- a/ccan/list/_info
+++ b/ccan/list/_info
@@ -1,6 +1,6 @@
+#include "config.h"
#include <stdio.h>
#include <string.h>
-#include "config.h"
/**
* list - double linked list routines
@@ -31,7 +31,7 @@
* {
* struct parent p;
* struct child *c;
- * unsigned int i;
+ * int i;
*
* if (argc < 2)
* errx(1, "Usage: %s parent children...", argv[0]);
@@ -62,7 +62,9 @@ int main(int argc, char *argv[])
return 1;
if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/str\n");
printf("ccan/container_of\n");
+ printf("ccan/check_type\n");
return 0;
}
diff --git a/ccan/list/list.h b/ccan/list/list.h
index 6dbaabf..a15321c 100644
--- a/ccan/list/list.h
+++ b/ccan/list/list.h
@@ -1,8 +1,10 @@
/* Licensed under BSD-MIT - see LICENSE file for details */
#ifndef CCAN_LIST_H
#define CCAN_LIST_H
+//#define CCAN_LIST_DEBUG 1
#include <stdbool.h>
#include <assert.h>
+#include <ccan/str/str.h>
#include <ccan/container_of/container_of.h>
#include <ccan/check_type/check_type.h>
@@ -88,12 +90,13 @@ struct list_head *list_check(const struct list_head *h, const char *abortstr);
struct list_node *list_check_node(const struct list_node *n,
const char *abortstr);
+#define LIST_LOC __FILE__ ":" stringify(__LINE__)
#ifdef CCAN_LIST_DEBUG
-#define list_debug(h) list_check((h), __func__)
-#define list_debug_node(n) list_check_node((n), __func__)
+#define list_debug(h, loc) list_check((h), loc)
+#define list_debug_node(n, loc) list_check_node((n), loc)
#else
-#define list_debug(h) (h)
-#define list_debug_node(n) (n)
+#define list_debug(h, loc) ((void)loc, h)
+#define list_debug_node(n, loc) ((void)loc, n)
#endif
/**
@@ -108,7 +111,7 @@ struct list_node *list_check_node(const struct list_node *n,
* Example:
* static struct list_head my_list = LIST_HEAD_INIT(my_list);
*/
-#define LIST_HEAD_INIT(name) { { &name.n, &name.n } }
+#define LIST_HEAD_INIT(name) { { &(name).n, &(name).n } }
/**
* LIST_HEAD - define and initialize an empty list_head
@@ -143,6 +146,48 @@ static inline void list_head_init(struct list_head *h)
}
/**
+ * list_node_init - initialize a list_node
+ * @n: the list_node to link to itself.
+ *
+ * You don't need to use this normally! But it lets you list_del(@n)
+ * safely.
+ */
+static inline void list_node_init(struct list_node *n)
+{
+ n->next = n->prev = n;
+}
+
+/**
+ * list_add_after - add an entry after an existing node in a linked list
+ * @h: the list_head to add the node to (for debugging)
+ * @p: the existing list_node to add the node after
+ * @n: the new list_node to add to the list.
+ *
+ * The existing list_node must already be a member of the list.
+ * The new list_node does not need to be initialized; it will be overwritten.
+ *
+ * Example:
+ * struct child c1, c2, c3;
+ * LIST_HEAD(h);
+ *
+ * list_add_tail(&h, &c1.list);
+ * list_add_tail(&h, &c3.list);
+ * list_add_after(&h, &c1.list, &c2.list);
+ */
+#define list_add_after(h, p, n) list_add_after_(h, p, n, LIST_LOC)
+static inline void list_add_after_(struct list_head *h,
+ struct list_node *p,
+ struct list_node *n,
+ const char *abortstr)
+{
+ n->next = p->next;
+ n->prev = p;
+ p->next->prev = n;
+ p->next = n;
+ (void)list_debug(h, abortstr);
+}
+
+/**
* 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.
@@ -155,51 +200,40 @@ static inline void list_head_init(struct list_head *h)
* list_add(&parent->children, &child->list);
* parent->num_children++;
*/
-static inline void list_add(struct list_head *h, struct list_node *n)
+#define list_add(h, n) list_add_(h, n, LIST_LOC)
+static inline void list_add_(struct list_head *h,
+ struct list_node *n,
+ const char *abortstr)
{
- n->next = h->n.next;
- n->prev = &h->n;
- h->n.next->prev = n;
- h->n.next = n;
- (void)list_debug(h);
+ list_add_after_(h, &h->n, n, abortstr);
}
/**
- * list_add_before - add an entry before another entry.
- * @h: the list_head to add the node to (we use it for debug purposes, can be NULL)
- * @p: the list_node of the other entry
- * @n: the list_node to add to the list.
+ * list_add_before - add an entry before an existing node in a linked list
+ * @h: the list_head to add the node to (for debugging)
+ * @p: the existing list_node to add the node before
+ * @n: the new list_node to add to the list.
*
- * The list_node does not need to be initialized; it will be overwritten.
+ * The existing list_node must already be a member of the list.
+ * The new list_node does not need to be initialized; it will be overwritten.
+ *
+ * Example:
+ * list_head_init(&h);
+ * list_add_tail(&h, &c1.list);
+ * list_add_tail(&h, &c3.list);
+ * list_add_before(&h, &c3.list, &c2.list);
*/
-static inline void list_add_before(struct list_head *h, struct list_node *p,
- struct list_node *n)
+#define list_add_before(h, p, n) list_add_before_(h, p, n, LIST_LOC)
+static inline void list_add_before_(struct list_head *h,
+ struct list_node *p,
+ struct list_node *n,
+ const char *abortstr)
{
n->next = p;
n->prev = p->prev;
+ p->prev->next = n;
p->prev = n;
- n->prev->next = n;
- if (h)
- (void)list_debug(h);
-}
-
-/**
- * list_add_after - add an entry after another entry.
- * @h: the list_head to add the node to (we use it for debug purposes, can be NULL)
- * @p: the list_node of the other entry
- * @n: the list_node to add to the list.
- *
- * The list_node does not need to be initialized; it will be overwritten.
- */
-static inline void list_add_after(struct list_head *h, struct list_node *p,
- struct list_node *n)
-{
- n->next = p->next;
- n->prev = p;
- p->next = n;
- n->next->prev = n;
- if (h)
- (void)list_debug(h);
+ (void)list_debug(h, abortstr);
}
/**
@@ -212,13 +246,12 @@ static inline void list_add_after(struct list_head *h, struct list_node *p,
* list_add_tail(&parent->children, &child->list);
* parent->num_children++;
*/
-static inline void list_add_tail(struct list_head *h, struct list_node *n)
+#define list_add_tail(h, n) list_add_tail_(h, n, LIST_LOC)
+static inline void list_add_tail_(struct list_head *h,
+ struct list_node *n,
+ const char *abortstr)
{
- n->next = &h->n;
- n->prev = h->n.prev;
- h->n.prev->next = n;
- h->n.prev = n;
- (void)list_debug(h);
+ list_add_before_(h, &h->n, n, abortstr);
}
/**
@@ -230,11 +263,33 @@ static inline void list_add_tail(struct list_head *h, struct list_node *n)
* Example:
* assert(list_empty(&parent->children) == (parent->num_children == 0));
*/
-static inline bool list_empty(const struct list_head *h)
+#define list_empty(h) list_empty_(h, LIST_LOC)
+static inline bool list_empty_(const struct list_head *h, const char* abortstr)
+{
+ (void)list_debug(h, abortstr);
+ return h->n.next == &h->n;
+}
+
+/**
+ * list_empty_nodebug - is a list empty (and don't perform debug checks)?
+ * @h: the list_head
+ *
+ * If the list is empty, returns true.
+ * This differs from list_empty() in that if CCAN_LIST_DEBUG is set it
+ * will NOT perform debug checks. Only use this function if you REALLY
+ * know what you're doing.
+ *
+ * Example:
+ * assert(list_empty_nodebug(&parent->children) == (parent->num_children == 0));
+ */
+#ifndef CCAN_LIST_DEBUG
+#define list_empty_nodebug(h) list_empty(h)
+#else
+static inline bool list_empty_nodebug(const struct list_head *h)
{
- (void)list_debug(h);
return h->n.next == &h->n;
}
+#endif
/**
* list_empty_nocheck - is a list empty?
@@ -259,15 +314,16 @@ static inline bool list_empty_nocheck(const struct list_head *h)
* another list, but not deleted again.
*
* See also:
- * list_del_from()
+ * list_del_from(), list_del_init()
*
* Example:
* list_del(&child->list);
* parent->num_children--;
*/
-static inline void list_del(struct list_node *n)
+#define list_del(n) list_del_(n, LIST_LOC)
+static inline void list_del_(struct list_node *n, const char* abortstr)
{
- (void)list_debug_node(n);
+ (void)list_debug_node(n, abortstr);
n->next->prev = n->prev;
n->prev->next = n->next;
#ifdef CCAN_LIST_DEBUG
@@ -277,6 +333,27 @@ static inline void list_del(struct list_node *n)
}
/**
+ * list_del_init - delete a node, and reset it so it can be deleted again.
+ * @n: the list_node to be deleted.
+ *
+ * list_del(@n) or list_del_init() again after this will be safe,
+ * which can be useful in some cases.
+ *
+ * See also:
+ * list_del_from(), list_del()
+ *
+ * Example:
+ * list_del_init(&child->list);
+ * parent->num_children--;
+ */
+#define list_del_init(n) list_del_init_(n, LIST_LOC)
+static inline void list_del_init_(struct list_node *n, const char *abortstr)
+{
+ list_del_(n, abortstr);
+ list_node_init(n);
+}
+
+/**
* list_del_from - delete an entry from a known linked list.
* @h: the list_head the node is in.
* @n: the list_node to delete from the list.
@@ -307,6 +384,39 @@ static inline void list_del_from(struct list_head *h, struct list_node *n)
}
/**
+ * list_swap - swap out an entry from an (unknown) linked list for a new one.
+ * @o: the list_node to replace from the list.
+ * @n: the list_node to insert in place of the old one.
+ *
+ * Note that this leaves @o in an undefined state; it can be added to
+ * another list, but not deleted/swapped again.
+ *
+ * See also:
+ * list_del()
+ *
+ * Example:
+ * struct child x1, x2;
+ * LIST_HEAD(xh);
+ *
+ * list_add(&xh, &x1.list);
+ * list_swap(&x1.list, &x2.list);
+ */
+#define list_swap(o, n) list_swap_(o, n, LIST_LOC)
+static inline void list_swap_(struct list_node *o,
+ struct list_node *n,
+ const char* abortstr)
+{
+ (void)list_debug_node(o, abortstr);
+ *n = *o;
+ n->next->prev = n;
+ n->prev->next = n;
+#ifdef CCAN_LIST_DEBUG
+ /* Catch use-after-del. */
+ o->next = o->prev = NULL;
+#endif
+}
+
+/**
* list_entry - convert a list_node back into the structure containing it.
* @n: the list_node
* @type: the type of the entry
@@ -346,14 +456,23 @@ static inline const void *list_top_(const struct list_head *h, size_t off)
}
/**
- * list_pop - get the first entry in a list and dequeue it
+ * list_pop - remove 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 *one;
+ * one = list_pop(&parent->children, struct child, list);
+ * if (!one)
+ * printf("Empty list!\n");
*/
#define list_pop(h, type, member) \
((type *)list_pop_((h), list_off_(type, member)))
-static inline const void *list_pop_(struct list_head *h, size_t off)
+
+static inline const void *list_pop_(const struct list_head *h, size_t off)
{
struct list_node *n;
@@ -418,9 +537,29 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
* printf("Name: %s\n", child->name);
*/
#define list_for_each_rev(h, i, member) \
- for (i = container_of_var(list_debug(h)->n.prev, i, member); \
- &i->member != &(h)->n; \
- i = container_of_var(i->member.prev, i, member))
+ list_for_each_rev_off(h, i, list_off_var_(i, member))
+
+/**
+ * list_for_each_rev_safe - iterate through a list backwards,
+ * 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 backwards.
+ * 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;
+ * list_for_each_rev_safe(&parent->children, child, next, list) {
+ * printf("Name: %s\n", child->name);
+ * }
+ */
+#define list_for_each_rev_safe(h, i, nxt, member) \
+ list_for_each_rev_safe_off(h, i, nxt, list_off_var_(i, member))
/**
* list_for_each_safe - iterate through a list, maybe during deletion
@@ -434,7 +573,6 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
* @nxt is used to hold the next element, so you can delete @i from the list.
*
* Example:
- * struct child *next;
* list_for_each_safe(&parent->children, child, next, list) {
* list_del(&child->list);
* parent->num_children--;
@@ -444,6 +582,130 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
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, \
+ __FILE__ ":" stringify(__LINE__)), \
+ (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, \
+ __FILE__ ":" stringify(__LINE__)), \
+ (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
+ * @from: the list to empty.
+ *
+ * This takes the entire contents of @from and moves it to the end of
+ * @to. After this @from will be empty.
+ *
+ * Example:
+ * struct list_head adopter;
+ *
+ * list_append_list(&adopter, &parent->children);
+ * assert(list_empty(&parent->children));
+ * parent->num_children = 0;
+ */
+#define list_append_list(t, f) list_append_list_(t, f, \
+ __FILE__ ":" stringify(__LINE__))
+static inline void list_append_list_(struct list_head *to,
+ struct list_head *from,
+ const char *abortstr)
+{
+ struct list_node *from_tail = list_debug(from, abortstr)->n.prev;
+ struct list_node *to_tail = list_debug(to, abortstr)->n.prev;
+
+ /* Sew in head and entire list. */
+ to->n.prev = from_tail;
+ from_tail->next = &to->n;
+ to_tail->next = &from->n;
+ from->n.prev = to_tail;
+
+ /* Now remove head. */
+ list_del(&from->n);
+ list_head_init(from);
+}
+
+/**
+ * list_prepend_list - empty one list into the start of another.
+ * @to: the list to prepend into
+ * @from: the list to empty.
+ *
+ * This takes the entire contents of @from and moves it to the start
+ * of @to. After this @from will be empty.
+ *
+ * Example:
+ * list_prepend_list(&adopter, &parent->children);
+ * assert(list_empty(&parent->children));
+ * parent->num_children = 0;
+ */
+#define list_prepend_list(t, f) list_prepend_list_(t, f, LIST_LOC)
+static inline void list_prepend_list_(struct list_head *to,
+ struct list_head *from,
+ const char *abortstr)
+{
+ struct list_node *from_tail = list_debug(from, abortstr)->n.prev;
+ struct list_node *to_head = list_debug(to, abortstr)->n.next;
+
+ /* Sew in head and entire list. */
+ to->n.next = &from->n;
+ from->n.prev = &to->n;
+ to_head->prev = from_tail;
+ from_tail->next = to_head;
+
+ /* Now remove head. */
+ list_del(&from->n);
+ list_head_init(from);
+}
+
+/* internal macros, do not use directly */
+#define list_for_each_off_dir_(h, i, off, dir) \
+ for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.dir, \
+ (off)); \
+ list_node_from_off_((void *)i, (off)) != &(h)->n; \
+ i = list_node_to_off_(list_node_from_off_((void *)i, (off))->dir, \
+ (off)))
+
+#define list_for_each_safe_off_dir_(h, i, nxt, off, dir) \
+ for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.dir, \
+ (off)), \
+ nxt = list_node_to_off_(list_node_from_off_(i, (off))->dir, \
+ (off)); \
+ list_node_from_off_(i, (off)) != &(h)->n; \
+ i = nxt, \
+ nxt = list_node_to_off_(list_node_from_off_(i, (off))->dir, \
+ (off)))
+
+/**
* list_for_each_off - iterate through a list of memory regions.
* @h: the list_head
* @i: the pointer to a memory region which contains list node data.
@@ -454,9 +716,9 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
* so you can break and continue as normal.
*
* WARNING! Being the low-level macro that it is, this wrapper doesn't know
- * nor care about the type of @i. The only assumtion made is that @i points
+ * nor care about the type of @i. The only assumption made is that @i points
* to a chunk of memory that at some @offset, relative to @i, contains a
- * properly filled `struct node_list' which in turn contains pointers to
+ * properly filled `struct list_node' which in turn contains pointers to
* memory chunks and it's turtles all the way down. With all that in mind
* remember that given the wrong pointer/offset couple this macro will
* happily churn all you memory until SEGFAULT stops it, in other words
@@ -473,10 +735,18 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
* printf("Name: %s\n", child->name);
*/
#define list_for_each_off(h, i, off) \
- for (i = list_node_to_off_(list_debug(h)->n.next, (off)); \
- list_node_from_off_((void *)i, (off)) != &(h)->n; \
- i = list_node_to_off_(list_node_from_off_((void *)i, (off))->next, \
- (off)))
+ list_for_each_off_dir_((h),(i),(off),next)
+
+/**
+ * list_for_each_rev_off - iterate through a list of memory regions backwards
+ * @h: the list_head
+ * @i: the pointer to a memory region which contains list node data.
+ * @off: offset(relative to @i) at which list node data resides.
+ *
+ * See list_for_each_off for details
+ */
+#define list_for_each_rev_off(h, i, off) \
+ list_for_each_off_dir_((h),(i),(off),prev)
/**
* list_for_each_safe_off - iterate through a list of memory regions, maybe
@@ -495,14 +765,26 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
* printf("Name: %s\n", child->name);
*/
#define list_for_each_safe_off(h, i, nxt, off) \
- for (i = list_node_to_off_(list_debug(h)->n.next, (off)), \
- nxt = list_node_to_off_(list_node_from_off_(i, (off))->next, \
- (off)); \
- list_node_from_off_(i, (off)) != &(h)->n; \
- i = nxt, \
- nxt = list_node_to_off_(list_node_from_off_(i, (off))->next, \
- (off)))
+ list_for_each_safe_off_dir_((h),(i),(nxt),(off),next)
+/**
+ * list_for_each_rev_safe_off - iterate backwards through a list of
+ * memory regions, maybe during deletion
+ * @h: the list_head
+ * @i: the pointer to a memory region which contains list node data.
+ * @nxt: the structure containing the list_node
+ * @off: offset(relative to @i) at which list node data resides.
+ *
+ * For details see `list_for_each_rev_off' and `list_for_each_rev_safe'
+ * descriptions.
+ *
+ * Example:
+ * list_for_each_rev_safe_off(&parent->children, child,
+ * next, offsetof(struct child, list))
+ * printf("Name: %s\n", child->name);
+ */
+#define list_for_each_rev_safe_off(h, i, nxt, off) \
+ list_for_each_safe_off_dir_((h),(i),(nxt),(off),prev)
/* Other -off variants. */
#define list_entry_off(n, type, off) \
@@ -542,42 +824,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(const struct list_head *h,
- const struct list_node *n,
- size_t off)
+ const struct list_node *n,
+ size_t off)
{
- if (n == &h->n)
- return NULL;
- return (char *)n - off;
+ if (n == &h->n)
+ return NULL;
+ return (char *)n - off;
}
-
-/**
- * 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)))
-
-
#endif /* CCAN_LIST_H */
diff --git a/ccan/list/test/run-CCAN_LIST_DEBUG.c b/ccan/list/test/run-CCAN_LIST_DEBUG.c
new file mode 100644
index 0000000..b8e5165
--- /dev/null
+++ b/ccan/list/test/run-CCAN_LIST_DEBUG.c
@@ -0,0 +1,60 @@
+/* Check that CCAN_LIST_DEBUG works */
+#include <setjmp.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <err.h>
+
+/* We don't actually want it to exit... */
+static jmp_buf aborted;
+#define abort() longjmp(aborted, 1)
+
+#define fprintf my_fprintf
+static char printf_buffer[1000];
+
+static int my_fprintf(FILE *stream, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+ (void)stream;
+ va_start(ap, format);
+ ret = vsprintf(printf_buffer, format, ap);
+ va_end(ap);
+ return ret;
+}
+
+#define CCAN_LIST_DEBUG 1
+#include <ccan/list/list.h>
+#include <ccan/tap/tap.h>
+#include <ccan/list/list.c>
+
+int main(void)
+{
+ struct list_head list;
+ struct list_node n1;
+ char expect[100];
+
+ plan_tests(2);
+ /* Empty list. */
+ list.n.next = &list.n;
+ list.n.prev = &list.n;
+ ok1(list_check(&list, NULL) == &list);
+
+ /* Bad back ptr */
+ list.n.prev = &n1;
+
+ /* Aborting version. */
+ sprintf(expect, "run-CCAN_LIST_DEBUG.c:51: prev corrupt in node %p (0) of %p\n",
+ &list, &list);
+ if (setjmp(aborted) == 0) {
+ assert(list_empty(&list));
+ fail("list_empty on empty with bad back ptr didn't fail!");
+ } else {
+ /* __FILE__ might give full path. */
+ int prep = strlen(printf_buffer) - strlen(expect);
+ ok1(prep >= 0 && strcmp(printf_buffer + prep, expect) == 0);
+ }
+
+ return exit_status();
+}
diff --git a/ccan/list/test/run-check-corrupt.c b/ccan/list/test/run-check-corrupt.c
index da4578a..94c2e67 100644
--- a/ccan/list/test/run-check-corrupt.c
+++ b/ccan/list/test/run-check-corrupt.c
@@ -16,11 +16,9 @@ static int my_fprintf(FILE *stream, const char *format, ...)
{
va_list ap;
int ret;
-
(void)stream;
-
va_start(ap, format);
- ret = vsnprintf(printf_buffer, sizeof(printf_buffer), format, ap);
+ ret = vsprintf(printf_buffer, format, ap);
va_end(ap);
return ret;
}
@@ -29,15 +27,12 @@ static int my_fprintf(FILE *stream, const char *format, ...)
#include <ccan/tap/tap.h>
#include <ccan/list/list.c>
-int main(int argc, char *argv[])
+int main(void)
{
struct list_head list;
struct list_node n1;
char expect[100];
- (void)argc;
- (void)argv;
-
plan_tests(9);
/* Empty list. */
list.n.next = &list.n;
diff --git a/ccan/list/test/run-check-nonconst.c b/ccan/list/test/run-check-nonconst.c
new file mode 100644
index 0000000..d3cb652
--- /dev/null
+++ b/ccan/list/test/run-check-nonconst.c
@@ -0,0 +1,27 @@
+#include <ccan/list/list.h>
+#include <ccan/tap/tap.h>
+#include <ccan/list/list.c>
+#include "helper.h"
+
+struct child {
+ const char *name;
+ struct list_node list;
+};
+
+int main(void)
+{
+ struct child c1, c2;
+ struct list_head list = LIST_HEAD_INIT(list);
+
+ plan_tests(1);
+
+ list_add(&list, &c1.list);
+ list_add_tail(list_check(&list, "Bad list!"), &c2.list);
+ list_del_from(list_check(&list, "Bad list!"),
+ list_check_node(&c2.list, "Bad node!"));
+ list_del_from(list_check(&list, "Bad list!"),
+ list_check_node(&c1.list, "Bad node!"));
+ ok1(list_empty(list_check(&list, "Bad emptied list")));
+
+ return exit_status();
+}
diff --git a/ccan/list/test/run-list_del_from-assert.c b/ccan/list/test/run-list_del_from-assert.c
index 453dc0f..9404b71 100644
--- a/ccan/list/test/run-list_del_from-assert.c
+++ b/ccan/list/test/run-list_del_from-assert.c
@@ -7,16 +7,13 @@
#include <unistd.h>
#include <signal.h>
-int main(int argc, char *argv[])
+int main(void)
{
struct list_head list1, list2;
struct list_node n1, n2, n3;
pid_t child;
int status;
- (void)argc;
- (void)argv;
-
plan_tests(1);
list_head_init(&list1);
list_head_init(&list2);
@@ -28,7 +25,6 @@ int main(int argc, char *argv[])
if (child) {
wait(&status);
} else {
- close(2); /* Close stderr so we don't print confusing assert */
/* This should abort. */
list_del_from(&list1, &n3);
exit(0);
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 0000000..cc61e03
--- /dev/null
+++ b/ccan/list/test/run-list_prev-list_next.c
@@ -0,0 +1,65 @@
+#include <ccan/list/list.h>
+#include <ccan/tap/tap.h>
+#include <ccan/list/list.c>
+#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(void)
+{
+ struct parent parent;
+ struct child c1, c2, c3;
+ const struct parent *p;
+ const struct child *c;
+
+ plan_tests(20);
+ 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);
+
+ /* Const variants */
+ p = &parent;
+ c = &c2;
+ ok1(list_next(&p->children, &c1, list) == &c2);
+ ok1(list_prev(&p->children, &c1, list) == NULL);
+ ok1(list_next(&p->children, c, list) == &c3);
+ ok1(list_prev(&p->children, c, list) == &c1);
+ ok1(list_next(&parent.children, c, list) == &c3);
+ ok1(list_prev(&parent.children, c, list) == &c1);
+ ok1(list_next(&p->children, &c3, list) == NULL);
+ ok1(list_prev(&p->children, &c3, list) == &c2);
+
+ return exit_status();
+}
diff --git a/ccan/list/test/run-prepend_list.c b/ccan/list/test/run-prepend_list.c
new file mode 100644
index 0000000..fecd419
--- /dev/null
+++ b/ccan/list/test/run-prepend_list.c
@@ -0,0 +1,111 @@
+#include <ccan/list/list.h>
+#include <ccan/tap/tap.h>
+#include <ccan/list/list.c>
+#include <stdarg.h>
+
+static bool list_expect(struct list_head *h, ...)
+{
+ va_list ap;
+ struct list_node *n = &h->n, *expected;
+
+ va_start(ap, h);
+ while ((expected = va_arg(ap, struct list_node *)) != NULL) {
+ n = n->next;
+ if (n != expected)
+ return false;
+ }
+ return (n->next == &h->n);
+}
+
+int main(void)
+{
+ struct list_head h1, h2;
+ struct list_node n[4];
+
+ plan_tests(40);
+
+ list_head_init(&h1);
+ list_head_init(&h2);
+
+ /* Append an empty list to an empty list. */
+ list_append_list(&h1, &h2);
+ ok1(list_empty(&h1));
+ ok1(list_empty(&h2));
+ ok1(list_check(&h1, NULL));
+ ok1(list_check(&h2, NULL));
+
+ /* Prepend an empty list to an empty list. */
+ list_prepend_list(&h1, &h2);
+ ok1(list_empty(&h1));
+ ok1(list_empty(&h2));
+ ok1(list_check(&h1, NULL));
+ ok1(list_check(&h2, NULL));
+
+ /* Append an empty list to a non-empty list */
+ list_add(&h1, &n[0]);
+ list_append_list(&h1, &h2);
+ ok1(list_empty(&h2));
+ ok1(list_check(&h1, NULL));
+ ok1(list_check(&h2, NULL));
+ ok1(list_expect(&h1, &n[0], NULL));
+
+ /* Prepend an empty list to a non-empty list */
+ list_prepend_list(&h1, &h2);
+ ok1(list_empty(&h2));
+ ok1(list_check(&h1, NULL));
+ ok1(list_check(&h2, NULL));
+ ok1(list_expect(&h1, &n[0], NULL));
+
+ /* Append a non-empty list to an empty list. */
+ list_append_list(&h2, &h1);
+ ok1(list_empty(&h1));
+ ok1(list_check(&h1, NULL));
+ ok1(list_check(&h2, NULL));
+ ok1(list_expect(&h2, &n[0], NULL));
+
+ /* Prepend a non-empty list to an empty list. */
+ list_prepend_list(&h1, &h2);
+ ok1(list_empty(&h2));
+ ok1(list_check(&h1, NULL));
+ ok1(list_check(&h2, NULL));
+ ok1(list_expect(&h1, &n[0], NULL));
+
+ /* Prepend a non-empty list to non-empty list. */
+ list_add(&h2, &n[1]);
+ list_prepend_list(&h1, &h2);
+ ok1(list_empty(&h2));
+ ok1(list_check(&h1, NULL));
+ ok1(list_check(&h2, NULL));
+ ok1(list_expect(&h1, &n[1], &n[0], NULL));
+
+ /* Append a non-empty list to non-empty list. */
+ list_add(&h2, &n[2]);
+ list_append_list(&h1, &h2);
+ ok1(list_empty(&h2));
+ ok1(list_check(&h1, NULL));
+ ok1(list_check(&h2, NULL));
+ ok1(list_expect(&h1, &n[1], &n[0], &n[2], NULL));
+
+ /* Prepend a 2-entry list to a 2-entry list. */
+ list_del_from(&h1, &n[2]);
+ list_add(&h2, &n[2]);
+ list_add_tail(&h2, &n[3]);
+ list_prepend_list(&h1, &h2);
+ ok1(list_empty(&h2));
+ ok1(list_check(&h1, NULL));
+ ok1(list_check(&h2, NULL));
+ ok1(list_expect(&h1, &n[2], &n[3], &n[1], &n[0], NULL));
+
+ /* Append a 2-entry list to a 2-entry list. */
+ list_del_from(&h1, &n[2]);
+ list_del_from(&h1, &n[3]);
+ list_add(&h2, &n[2]);
+ list_add_tail(&h2, &n[3]);
+ list_append_list(&h1, &h2);
+ ok1(list_empty(&h2));
+ ok1(list_check(&h1, NULL));
+ ok1(list_check(&h2, NULL));
+ ok1(list_expect(&h1, &n[1], &n[0], &n[2], &n[3], NULL));
+
+ return exit_status();
+}
diff --git a/ccan/list/test/run-single-eval.c b/ccan/list/test/run-single-eval.c
index 3c17e03..db0bffd 100644
--- a/ccan/list/test/run-single-eval.c
+++ b/ccan/list/test/run-single-eval.c
@@ -19,7 +19,7 @@ static LIST_HEAD(static_list);
#define ref(obj, counter) ((counter)++, (obj))
-int main(int argc, char *argv[])
+int main(void)
{
struct parent parent;
struct child c1, c2, c3, *c, *n;
@@ -28,9 +28,6 @@ int main(int argc, char *argv[])
node_count = 0;
struct list_head list = LIST_HEAD_INIT(list);
- (void)argc;
- (void)argv;
-
plan_tests(74);
/* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */
ok1(list_empty(ref(&static_list, static_count)));
diff --git a/ccan/list/test/run.c b/ccan/list/test/run.c
index bc9d266..5354226 100644
--- a/ccan/list/test/run.c
+++ b/ccan/list/test/run.c
@@ -17,19 +17,17 @@ struct child {
static LIST_HEAD(static_list);
-int main(int argc, char *argv[])
+int main(void)
{
struct parent parent;
- struct child c1, c2, c3, *c, *n;
+ struct child c1, c2, c3, x1, *c, *n;
unsigned int i;
struct list_head list = LIST_HEAD_INIT(list);
opaque_t *q, *nq;
struct list_head opaque_list = LIST_HEAD_INIT(opaque_list);
+ LIST_HEAD(rev);
- (void)argc;
- (void)argv;
-
- plan_tests(65);
+ plan_tests(92);
/* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */
ok1(list_empty(&static_list));
ok1(list_check(&static_list, NULL));
@@ -89,6 +87,11 @@ int main(int argc, char *argv[])
/* Test list_top */
ok1(list_top(&parent.children, struct child, list) == &c1);
+ /* Test list_pop */
+ ok1(list_pop(&parent.children, struct child, list) == &c1);
+ ok1(list_top(&parent.children, struct child, list) == &c2);
+ list_add(&parent.children, &c1.list);
+
/* Test list_tail */
ok1(list_tail(&parent.children, struct child, list) == &c3);
@@ -147,6 +150,10 @@ int main(int argc, char *argv[])
list_del_from(&parent.children, &c->list);
break;
}
+
+ /* prepare for list_for_each_rev_safe test */
+ list_add(&rev, &c->list);
+
ok1(list_check(&parent.children, NULL));
if (i > 2)
break;
@@ -154,6 +161,43 @@ int main(int argc, char *argv[])
ok1(i == 3);
ok1(list_empty(&parent.children));
+ /* Test list_for_each_rev_safe, list_del and list_del_from. */
+ i = 0;
+ list_for_each_rev_safe(&rev, c, n, list) {
+ switch (i++) {
+ case 0:
+ ok1(c == &c1);
+ list_del(&c->list);
+ break;
+ case 1:
+ ok1(c == &c2);
+ list_del_from(&rev, &c->list);
+ break;
+ case 2:
+ ok1(c == &c3);
+ list_del_from(&rev, &c->list);
+ break;
+ }
+ ok1(list_check(&rev, NULL));
+ if (i > 2)
+ break;
+ }
+ ok1(i == 3);
+ ok1(list_empty(&rev));
+
+ /* Test list_node_init: safe to list_del after this. */
+ list_node_init(&c->list);
+ list_del(&c->list);
+
+ /* Test list_del_init */
+ list_add(&parent.children, &c->list);
+ ok1(!list_empty(&parent.children));
+ list_del_init(&c->list);
+ ok1(list_empty(&parent.children));
+ /* We can call this as many times as we like. */
+ list_del_init(&c->list);
+ list_del_init(&c->list);
+
/* Test list_for_each_off. */
list_add_tail(&opaque_list,
(struct list_node *)create_opaque_blob());
@@ -197,8 +241,66 @@ int main(int argc, char *argv[])
ok1(i == 3);
ok1(list_empty(&opaque_list));
- /* Test list_top/list_tail on empty list. */
+ /* Test list_top/list_tail/list_pop on empty list. */
ok1(list_top(&parent.children, struct child, list) == NULL);
ok1(list_tail(&parent.children, struct child, list) == NULL);
+ ok1(list_pop(&parent.children, struct child, list) == NULL);
+
+ /* Test list_add_before and list_add_after */
+ list_add(&parent.children, &c1.list);
+ list_add_after(&parent.children, &c1.list, &c2.list);
+ ok1(list_check(&parent.children, "list_add_after"));
+
+ i = 0;
+ list_for_each(&parent.children, c, list) {
+ switch (i++) {
+ case 0:
+ ok1(c == &c1);
+ break;
+ case 1:
+ ok1(c == &c2);
+ break;
+ }
+ }
+ ok1(i == 2);
+
+ list_add_before(&parent.children, &c2.list, &c3.list);
+ ok1(list_check(&parent.children, "list_add_before"));
+
+ i = 0;
+ list_for_each(&parent.children, c, list) {
+ switch (i++) {
+ case 0:
+ ok1(c == &c1);
+ break;
+ case 1:
+ ok1(c == &c3);
+ break;
+ case 2:
+ ok1(c == &c2);
+ break;
+ }
+ }
+ ok1(i == 3);
+
+ /* test list_swap */
+ list_swap(&c3.list, &x1.list);
+ ok1(list_check(&parent.children, "list_swap"));
+ i = 0;
+ list_for_each(&parent.children, c, list) {
+ switch (i++) {
+ case 0:
+ ok1(c == &c1);
+ break;
+ case 1:
+ ok1(c == &x1);
+ break;
+ case 2:
+ ok1(c == &c2);
+ break;
+ }
+ }
+ ok1(i == 3);
+
return exit_status();
}