]> git.ozlabs.org Git - ccan/blobdiff - ccan/list/list.h
ttxml: removed cruft from tests
[ccan] / ccan / list / list.h
index 18f077aec20418c65711230ff051bc85670a2cbb..0091ea4b655216754a347cb4ccb655aa8823177e 100644 (file)
@@ -4,6 +4,7 @@
 #include <stdbool.h>
 #include <assert.h>
 #include <ccan/container_of/container_of.h>
+#include <ccan/check_type/check_type.h>
 
 /**
  * struct list_node - an entry in a doubly-linked list
@@ -279,8 +280,15 @@ static inline void list_del_from(struct list_head *h, struct list_node *n)
  *     struct child *first;
  *     first = list_top(&parent->children, struct child, list);
  */
-#define list_top(h, type, member) \
-       (list_empty(h) ? NULL : list_entry((h)->n.next, type, member))
+#define list_top(h, type, member)                                      \
+       ((type *)list_top_((h), list_off_(type, member)))
+
+static inline const void *list_top_(const struct list_head *h, size_t off)
+{
+       if (list_empty(h))
+               return NULL;
+       return (const char *)h->n.next - off;
+}
 
 /**
  * list_tail - get the last entry in a list
@@ -295,11 +303,18 @@ static inline void list_del_from(struct list_head *h, struct list_node *n)
  *     last = list_tail(&parent->children, struct child, list);
  */
 #define list_tail(h, type, member) \
-       (list_empty(h) ? NULL : list_entry((h)->n.prev, type, member))
+       ((type *)list_tail_((h), list_off_(type, member)))
+
+static inline const void *list_tail_(const struct list_head *h, size_t off)
+{
+       if (list_empty(h))
+               return NULL;
+       return (const char *)h->n.prev - off;
+}
 
 /**
  * list_for_each - iterate through a list.
- * @h: the list_head
+ * @h: the list_head (warning: evaluated multiple times!)
  * @i: the structure containing the list_node
  * @member: the list_node member of the structure
  *
@@ -311,9 +326,25 @@ static inline void list_del_from(struct list_head *h, struct list_node *n)
  *             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);    \
+       list_for_each_off(h, i, list_off_var_(i, member))
+
+/**
+ * list_for_each_rev - iterate through a list backwards.
+ * @h: the list_head
+ * @i: 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.  It's
+ * a for loop, so you can break and continue as normal.
+ *
+ * Example:
+ *     list_for_each_rev(&parent->children, child, list)
+ *             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.next, i, member))
+            i = container_of_var(i->member.prev, i, member))
 
 /**
  * list_for_each_safe - iterate through a list, maybe during deletion
@@ -334,8 +365,105 @@ static inline void list_del_from(struct list_head *h, struct list_node *n)
  *     }
  */
 #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 */