summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Smirnov <andrew.smirnov@gmail.com>2011-12-12 13:52:44 +1030
committerRusty Russell <rusty@rustcorp.com.au>2011-12-12 13:52:44 +1030
commite59b7388d8b2b56dc759be7c2dbf63100d2e131f (patch)
treee139f831b76d8beee67da5cf6820931d8b52c26a
parent87f505a56eae7ef2a03a76b5b412d6bc46c58aae (diff)
list.h: opaque list iteration functionality
See https://github.com/rustyrussell/ccan/pull/1#issuecomment-2856561 for details. (Reworked with minor cleanups -- Rusty)
-rw-r--r--ccan/list/list.h103
-rw-r--r--ccan/list/test/helper.c56
-rw-r--r--ccan/list/test/helper.h9
-rw-r--r--ccan/list/test/run.c48
4 files changed, 208 insertions, 8 deletions
diff --git a/ccan/list/list.h b/ccan/list/list.h
index 5c9aa2a6..0091ea4b 100644
--- a/ccan/list/list.h
+++ b/ccan/list/list.h
@@ -326,9 +326,7 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
* printf("Name: %s\n", child->name);
*/
#define list_for_each(h, i, member) \
- for (i = container_of_var(list_debug(h)->n.next, i, member); \
- &i->member != &(h)->n; \
- i = container_of_var(i->member.next, i, member))
+ list_for_each_off(h, i, list_off_var_(i, member))
/**
* list_for_each_rev - iterate through a list backwards.
@@ -367,14 +365,105 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
* }
*/
#define list_for_each_safe(h, i, nxt, member) \
- for (i = container_of_var(list_debug(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))
+ list_for_each_safe_off(h, i, nxt, list_off_var_(i, member))
+
+/**
+ * list_for_each_off - iterate through a list of memory regions.
+ * @h: the list_head
+ * @i: the pointer to a memory region wich contains list node data.
+ * @off: offset(relative to @i) at which list node data resides.
+ *
+ * This is a low-level wrapper to iterate @i over the entire list, used to
+ * implement all oher, more high-level, for-each constructs. It's a for loop,
+ * 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
+ * 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
+ * memory chunks and it's turtles all the way down. Whith all that in mind
+ * remember that given the wrong pointer/offset couple this macro will
+ * happilly churn all you memory untill SEGFAULT stops it, in other words
+ * caveat emptor.
+ *
+ * It is worth mentioning that one of legitimate use-cases for that wrapper
+ * is operation on opaque types with known offset for `struct list_node'
+ * member(preferably 0), because it allows you not to disclose the type of
+ * @i.
+ *
+ * Example:
+ * list_for_each_off(&parent->children, child,
+ * offsetof(struct child, list))
+ * 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_safe_off - iterate through a list of memory regions, maybe
+ * during deletion
+ * @h: the list_head
+ * @i: the pointer to a memory region wich 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_off' and `list_for_each_safe'
+ * descriptions.
+ *
+ * Example:
+ * list_for_each_safe_off(&parent->children, child,
+ * next, offsetof(struct child, list))
+ * 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)))
+
+
+/* Other -off variants. */
+#define list_entry_off(n, type, off) \
+ ((type *)list_node_from_off_((n), (off)))
+
+#define list_head_off(h, type, off) \
+ ((type *)list_head_off((h), (off)))
+
+#define list_tail_off(h, type, off) \
+ ((type *)list_tail_((h), (off)))
+
+#define list_add_off(h, n, off) \
+ list_add((h), list_node_from_off_((n), (off)))
+
+#define list_del_off(n, off) \
+ list_del(list_node_from_off_((n), (off)))
+
+#define list_del_from_off(h, n, off) \
+ list_del_from(h, list_node_from_off_((n), (off)))
+
+/* Offset helper functions so we only single-evaluate. */
+static inline void *list_node_to_off_(struct list_node *node, size_t off)
+{
+ return (void *)((char *)node - off);
+}
+static inline struct list_node *list_node_from_off_(void *ptr, size_t off)
+{
+ return (struct list_node *)((char *)ptr + off);
+}
/* Get the offset of the member, but make sure it's a list_node. */
#define list_off_(type, member) \
(container_off(type, member) + \
check_type(((type *)0)->member, struct list_node))
+#define list_off_var_(var, member) \
+ (container_off_var(var, member) + \
+ check_type(var->member, struct list_node))
+
#endif /* CCAN_LIST_H */
diff --git a/ccan/list/test/helper.c b/ccan/list/test/helper.c
new file mode 100644
index 00000000..4fb1c5ac
--- /dev/null
+++ b/ccan/list/test/helper.c
@@ -0,0 +1,56 @@
+#include <stdlib.h>
+#include <stdbool.h>
+#include <time.h>
+
+#include <ccan/list/list.h>
+#include "helper.h"
+
+#define ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING \
+ (42)
+
+struct opaque {
+ struct list_node list;
+ size_t secret_offset;
+ char secret_drawer[42];
+};
+
+static bool not_randomized = true;
+
+struct opaque *create_opaque_blob(void)
+{
+ struct opaque *blob = calloc(1, sizeof(struct opaque));
+
+ if (not_randomized) {
+ srandom((int)time(NULL));
+ not_randomized = false;
+ }
+
+ blob->secret_offset = random() % (sizeof(blob->secret_drawer));
+ blob->secret_drawer[blob->secret_offset] =
+ ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING;
+
+ return blob;
+}
+
+bool if_blobs_know_the_secret(struct opaque *blob)
+{
+ bool answer = true;
+ int i;
+ for (i = 0; i < sizeof(blob->secret_drawer) /
+ sizeof(blob->secret_drawer[0]); i++)
+ if (i != blob->secret_offset)
+ answer = answer && (blob->secret_drawer[i] == 0);
+ else
+ answer = answer &&
+ (blob->secret_drawer[blob->secret_offset] ==
+ ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING);
+
+ return answer;
+}
+
+void destroy_opaque_blob(struct opaque *blob)
+{
+ free(blob);
+}
+
+
diff --git a/ccan/list/test/helper.h b/ccan/list/test/helper.h
new file mode 100644
index 00000000..4b64a7d6
--- /dev/null
+++ b/ccan/list/test/helper.h
@@ -0,0 +1,9 @@
+/* These are in a separate C file so we can test undefined structures. */
+struct opaque;
+typedef struct opaque opaque_t;
+
+opaque_t *create_opaque_blob(void);
+bool if_blobs_know_the_secret(opaque_t *blob);
+void destroy_opaque_blob(opaque_t *blob);
+
+
diff --git a/ccan/list/test/run.c b/ccan/list/test/run.c
index 4ac2dcda..1d02acd5 100644
--- a/ccan/list/test/run.c
+++ b/ccan/list/test/run.c
@@ -1,6 +1,7 @@
#include <ccan/list/list.h>
#include <ccan/tap/tap.h>
#include <ccan/list/list.c>
+#include "helper.h"
struct parent {
const char *name;
@@ -21,8 +22,10 @@ int main(int argc, char *argv[])
struct child c1, c2, c3, *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);
- plan_tests(53);
+ plan_tests(65);
/* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */
ok1(list_empty(&static_list));
ok1(list_check(&static_list, NULL));
@@ -147,6 +150,49 @@ int main(int argc, char *argv[])
ok1(i == 3);
ok1(list_empty(&parent.children));
+ /* Test list_for_each_off. */
+ list_add_tail(&opaque_list,
+ (struct list_node *)create_opaque_blob());
+ list_add_tail(&opaque_list,
+ (struct list_node *)create_opaque_blob());
+ list_add_tail(&opaque_list,
+ (struct list_node *)create_opaque_blob());
+
+ i = 0;
+
+ list_for_each_off(&opaque_list, q, 0) {
+ i++;
+ ok1(if_blobs_know_the_secret(q));
+ }
+ ok1(i == 3);
+
+ /* Test list_for_each_safe_off, list_del_off and list_del_from_off. */
+ i = 0;
+ list_for_each_safe_off(&opaque_list, q, nq, 0) {
+ switch (i++) {
+ case 0:
+ ok1(if_blobs_know_the_secret(q));
+ list_del_off(q, 0);
+ destroy_opaque_blob(q);
+ break;
+ case 1:
+ ok1(if_blobs_know_the_secret(q));
+ list_del_from_off(&opaque_list, q, 0);
+ destroy_opaque_blob(q);
+ break;
+ case 2:
+ ok1(c == &c3);
+ list_del_from_off(&opaque_list, q, 0);
+ destroy_opaque_blob(q);
+ break;
+ }
+ ok1(list_check(&opaque_list, NULL));
+ if (i > 2)
+ break;
+ }
+ ok1(i == 3);
+ ok1(list_empty(&opaque_list));
+
/* Test list_top/list_tail on empty list. */
ok1(list_top(&parent.children, struct child, list) == NULL);
ok1(list_tail(&parent.children, struct child, list) == NULL);