]> git.ozlabs.org Git - ccan/blobdiff - ccan/list/list.h
list: list_swap to exchange elements
[ccan] / ccan / list / list.h
index 4d1d34e327090f8bc568a9ea8cf5e4eb79006fa4..983675b81f9d53877fd5d0dfaa56abc1d742a641 100644 (file)
@@ -145,6 +145,48 @@ static inline void list_head_init(struct list_head *h)
        h->n.next = h->n.prev = &h->n;
 }
 
+/**
+ * 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
@@ -163,10 +205,34 @@ 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;
+       list_add_after_(h, &h->n, n, abortstr);
+}
+
+/**
+ * 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 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);
+ */
+#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;
        (void)list_debug(h, abortstr);
 }
 
@@ -185,11 +251,7 @@ 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, abortstr);
+       list_add_before_(h, &h->n, n, abortstr);
 }
 
 /**
@@ -237,7 +299,7 @@ static inline bool list_empty_nodebug(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);
@@ -255,6 +317,27 @@ static inline void list_del_(struct list_node *n, const char* abortstr)
 #endif
 }
 
+/**
+ * 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.
@@ -285,6 +368,39 @@ static inline void list_del_from(struct list_head *h, struct list_node *n)
        list_del(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