Added silc_list_pop
[runtime.git] / lib / silcutil / silclist.h
index 2bf94112cf9bbb420177dc15ae19da22c43a1634..52454c30d7f1dd2b2ec1ab575d6d46a7b7520a2e 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silclist.h 
+  silclist.h
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2002 - 2003 Pekka Riikonen
+  Copyright (C) 2002 - 2008 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 
 */
 
-/****h* silcutil/SILC List Interface
+/****h* silcutil/List Interface
  *
  * DESCRIPTION
  *
- * Implementation of the SilcList interface.  This interface provides
- * simple linked list.
+ * Generic list interface that can turn any structure with list pointers
+ * into a SilcList.  The interface can provide both singly and doubly linked
+ * lists.  The interface does not allocate any memory.
+ *
+ * SILC List is not thread-safe.  If the same list context must be used
+ * in multithreaded environment concurrency control must be employed.
+ *
+ * EXAMPLE
+ *
+ * struct EntryStruct {
+ *   char *dummy;
+ *   struct EntryStruct *next;        // The list member pointer
+ * };
+ *
+ * SilcList list;
+ *
+ * // Initialize list
+ * silc_list_init(list, struct EntryStruct, next);
  *
  ***/
 
 #ifndef SILCLIST_H
 #define SILCLIST_H
 
-/****s* silcutil/SilcList/SilcList
+/****s* silcutil/SilcList
  *
  * NAME
- * 
+ *
  *    typedef struct { ... } SilcList;
  *
  * DESCRIPTION
  *    function silc_list_init.
  *
  ***/
-typedef struct {
+typedef struct SilcListStruct {
   void *head;                       /* Start of the list */
   void *tail;                       /* End of the list */
   void *current;                    /* Current pointer in list */
-  unsigned int next_offset : 16;     /* Offset to 'next' pointer */
-  unsigned int prev_offset : 16;     /* Offset to 'prev' pointer */
+  SilcUInt16 next_offset;           /* Offset to 'next' pointer */
+  SilcUInt16 prev_offset;           /* Offset to 'prev' pointer */
   unsigned int prev_set    : 1;             /* Set if 'prev' exists */
   unsigned int end_set     : 1;             /* Set if silc_list_end was called */
   unsigned int count       : 30;     /* Number of entries in the list */
 } SilcList;
 
-/****d* silcutil/SilcList/SILC_LIST_END
+/****d* silcutil/SILC_LIST_END
  *
  * NAME
- * 
- *    #define SILC_LIST_END ...
+ *
+ *    #define SILC_LIST_END NULL
  *
  * DESCRIPTION
  *
@@ -68,7 +84,7 @@ typedef struct {
 #define SILC_LIST_END NULL
 /***/
 
-/****f* silcutil/SilcList/silc_list_init
+/****f* silcutil/silc_list_init
  *
  * SYNOPSIS
  *
@@ -101,10 +117,11 @@ do {                                                      \
   (list).next_offset = silc_offsetof(type, nextfield); \
   (list).prev_set = 0;                                 \
   (list).prev_offset = 0;                              \
+  (list).end_set = 0;                                  \
   (list).head = (list).tail = (list).current = NULL;   \
 } while(0)
 
-/****f* silcutil/SilcList/silc_list_init_prev
+/****f* silcutil/silc_list_init_prev
  *
  * SYNOPSIS
  *
@@ -140,14 +157,15 @@ do {                                                              \
   (list).next_offset = silc_offsetof(type, nextfield);         \
   (list).prev_offset = silc_offsetof(type, prevfield);         \
   (list).prev_set = 1;                                         \
+  (list).end_set = 0;                                          \
   (list).head = (list).tail = (list).current = NULL;           \
 } while(0)
 
-/****f* silcutil/SilcList/silc_list_count
+/****f* silcutil/silc_list_count
  *
  * SYNOPSIS
  *
- *    #define silc_list_count(list) ...
+ *    SilcUInt32 silc_list_count(SilcList list);
  *
  * DESCRIPTION
  *
@@ -156,11 +174,11 @@ do {                                                              \
  ***/
 #define silc_list_count(list) (list).count
 
-/****f* silcutil/SilcList/silc_list_start
+/****f* silcutil/silc_list_start
  *
  * SYNOPSIS
  *
- *    #define silc_list_start(list) ...
+ *    void silc_list_start(SilcList list);
  *
  * DESCRIPTION
  *
@@ -171,11 +189,11 @@ do {                                                              \
 #define silc_list_start(list)                          \
   ((list).current = (list).head, (list).end_set = 0)
 
-/****f* silcutil/SilcList/silc_list_end
+/****f* silcutil/silc_list_end
  *
  * SYNOPSIS
  *
- *    #define silc_list_end(list) ...
+ *    void silc_list_end(SilcList list);
  *
  * DESCRIPTION
  *
@@ -197,15 +215,15 @@ do {                                                              \
 #define __silc_list_prev(list, pos)                            \
   ((void **)((unsigned char *)(pos) + (list).prev_offset))
 
-/****f* silcutil/SilcList/silc_list_add
+/****f* silcutil/silc_list_add
  *
  * SYNOPSIS
  *
- *    #define silc_list_add(list, entry) ...
+ *    void silc_list_add(SilcList list, void *entry);
  *
  * DESCRIPTION
  *
- *    Adds new entry indicated by `entry' to the end of the list indicated 
+ *    Adds new entry indicated by `entry' to the end of the list indicated
  *    by `list'.
  *
  ***/
@@ -222,11 +240,53 @@ do {                                                      \
   (list).count++;                                      \
 } while(0)
 
-/****f* silcutil/SilcList/silc_list_del
+/****f* silcutil/silc_list_insert
+ *
+ * SYNOPSIS
+ *
+ *    void silc_list_insert(SilcList list, void *current, void *entry);
+ *
+ * DESCRIPTION
+ *
+ *    Insert new entry indicated by `entry' after the entry `current'
+ *    to the list indicated by `list'.  If `current' is NULL, then the
+ *    `entry' is added at the head of the list.  Use the silc_list_add
+ *    to add at the end of the list.
+ *
+ ***/
+#define silc_list_insert(list, current, entry)                          \
+do {                                                                    \
+  if (!(current)) {                                                     \
+    if ((list).head)                                                    \
+      *__silc_list_next(list, entry) = (list).head;                     \
+    else                                                                \
+      *__silc_list_next(list, entry) = NULL;                            \
+    if ((list).prev_set && (list).head)                                         \
+      *__silc_list_prev(list, (list).head) = entry;                     \
+    if (!(list).tail)                                                   \
+      (list).tail = (entry);                                            \
+    (list).head = (entry);                                              \
+    if ((list).prev_set)                                                \
+      *__silc_list_prev(list, entry) = NULL;                            \
+  } else {                                                              \
+    *__silc_list_next(list, entry) = *__silc_list_next(list, current);  \
+    *__silc_list_next(list, current) = entry;                           \
+    if ((list).prev_set) {                                              \
+      *__silc_list_prev(list, entry) = current;                                 \
+      if (*__silc_list_next(list, entry))                               \
+        *__silc_list_prev(list, *__silc_list_next(list, entry)) = entry; \
+    }                                                                   \
+    if ((list).tail == (current))                                       \
+      (list).tail = (entry);                                            \
+  }                                                                     \
+  (list).count++;                                                       \
+} while(0)
+
+/****f* silcutil/silc_list_del
  *
  * SYNOPSIS
  *
- *    #define silc_list_del(list, entry) ...
+ *    void silc_list_del(SilcListlist, void *entry);
  *
  * DESCRIPTION
  *
@@ -240,7 +300,7 @@ do {                                                                        \
   for (p = &(list).head; *p; p = __silc_list_next(list, *p)) {         \
     if (*p == (entry)) {                                               \
       *p = *__silc_list_next(list, entry);                             \
-      if (*p && (list).prev_set)                                       \
+      if ((list).prev_set && *p)                                       \
         *__silc_list_prev(list, *p) = *__silc_list_prev(list, entry);  \
       if ((list).current == (entry))                                   \
         (list).current = *p;                                           \
@@ -253,11 +313,11 @@ do {                                                                      \
     (list).tail = prev;                                                        \
 } while(0)
 
-/****f* silcutil/SilcList/silc_list_get
+/****f* silcutil/silc_list_get
  *
  * SYNOPSIS
  *
- *    #define silc_list_get(list) ...
+ *    void *silc_list_get(SilcList list);
  *
  * DESCRIPTION
  *
@@ -271,7 +331,7 @@ do {                                                                        \
  *
  * EXAMPLE
  *
- *    // Traverse the list from the beginning to the end 
+ *    // Traverse the list from the beginning to the end
  *    silc_list_start(list);
  *    while ((entry = silc_list_get(list)) != SILC_LIST_END) {
  *      ...
@@ -295,4 +355,43 @@ void *__silc_list_get(SilcList *list)
   return pos;
 }
 
-#endif
+/****f* silcutil/silc_list_pop
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_list_pop(SilcList list);
+ *
+ * DESCRIPTION
+ *
+ *    Pops the head of the list.  Removes the head of the list and returns
+ *    the removed head.  This will always remove the head of the list even
+ *    if silc_list_end was called.  Calling silc_list_start is not necessary.
+ *    Returns SILC_LIST_END if the list is empty.
+ *
+ ***/
+#define silc_list_pop(x) __silc_list_pop(&(x))
+static inline
+void *__silc_list_pop(SilcList *list)
+{
+  void *head, **p;
+
+  if (!list->head)
+    return NULL;
+
+  head = list->head;
+  p = &list->head;
+  *p = *__silc_list_next(*list, head);
+  if (list->prev_set && *p)
+    *__silc_list_prev(*list, *p) = *__silc_list_prev(*list, head);
+
+  if (list->current == head)
+    list->current = *p;
+  if (head == list->tail)
+    list->tail = NULL;
+
+  list->count--;
+
+  return head;
+}
+
+#endif /* SILCLIST_H */