]> git.ozlabs.org Git - ccan/commitdiff
list.h: opaque list iteration functionality
authorAndrey Smirnov <andrew.smirnov@gmail.com>
Mon, 12 Dec 2011 03:22:44 +0000 (13:52 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Mon, 12 Dec 2011 03:22:44 +0000 (13:52 +1030)
See https://github.com/rustyrussell/ccan/pull/1#issuecomment-2856561
for details.
(Reworked with minor cleanups -- Rusty)

ccan/list/list.h
ccan/list/test/helper.c [new file with mode: 0644]
ccan/list/test/helper.h [new file with mode: 0644]
ccan/list/test/run.c

index 5c9aa2a689ca38af5a03c5c7c928381bf7b3c443..0091ea4b655216754a347cb4ccb655aa8823177e 100644 (file)
@@ -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 (file)
index 0000000..4fb1c5a
--- /dev/null
@@ -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 (file)
index 0000000..4b64a7d
--- /dev/null
@@ -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);
+
+
index 4ac2dcda34e3eb5ebdc50aca20ea8ac1e379505a..1d02acd5b763123ae365f4e185c676dd6c35453d 100644 (file)
@@ -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);