Rewrote SilcList, new version includes support for backwards
authorPekka Riikonen <priikone@silcnet.org>
Sat, 15 Mar 2003 14:46:20 +0000 (14:46 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sat, 15 Mar 2003 14:46:20 +0000 (14:46 +0000)
list traversing as well.

lib/silcutil/silcdlist.h
lib/silcutil/silclist.h

index d9f6d55267bfd19cbe45526237c4d5381b28a1c6..2e62a9077c1e91d6447b52b13d6c2f1dd0bc5b0d 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  silcdlist.h
+  silcdlist.h 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 Pekka Riikonen
+  Copyright (C) 2000 - 2003 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
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *
  * DESCRIPTION
  *
- *    SILC Dynamic List API can be used to add opaque contexts to list that
- *    will automatically allocate list entries.  Normal SILC List API cannot
- *    be used for this purpose because in that case the context passed to the
- *    list must be defined as list structure already.  This is not the case in
- *    SilcDList.
+ * SILC Dynamic List API can be used to add opaque contexts to list that
+ * will automatically allocate list entries.  Normal SILC List API cannot
+ * be used for this purpose because in that case the context passed to the
+ * list must be defined as list structure already.  This is not the case in
+ * SilcDList.
  *
- *    This is slower than SilcList because this requires one extra memory
- *    allocation when adding new entries to the list.  The context is probably
- *    allocated already and the new list entry requires one additional memory
- *    allocation.  The memory allocation and freeing is done automatically in
- *    the API and does not show to the caller.
+ * This is slower than SilcList because this requires one extra memory
+ * allocation when adding new entries to the list.  The context is probably
+ * allocated already and the new list entry requires one additional memory
+ * allocation.  The memory allocation and freeing is done automatically in
+ * the API and does not show to the caller.
  *
  ***/
 
@@ -65,6 +64,7 @@ typedef struct {
 typedef struct SilcDListEntryStruct {
   void *context;
   struct SilcDListEntryStruct *next;
+  struct SilcDListEntryStruct *prev;
 } *SilcDListEntry;
 
 /****f* silcutil/SilcDListAPI/silc_dlist_init
@@ -85,8 +85,10 @@ SilcDList silc_dlist_init()
 {
   SilcDList list;
 
-  list = (SilcDList)silc_calloc(1, sizeof(*list));
-  silc_list_init(list->list, struct SilcDListEntryStruct, next);
+  list = (SilcDList)silc_malloc(sizeof(*list));
+  if (!list)
+    return NULL;
+  silc_list_init_prev(list->list, struct SilcDListEntryStruct, next, prev);
 
   return list;
 }
@@ -148,7 +150,7 @@ int silc_dlist_count(SilcDList list)
  * DESCRIPTION
  *
  *    Set the start of the list. This prepares the list for traversing entries
- *    from the start of the list.
+ *    from the start of the list towards end of the list.
  *
  ***/
 
@@ -158,6 +160,26 @@ void silc_dlist_start(SilcDList list)
   silc_list_start(list->list);
 }
 
+/****f* silcutil/SilcDListAPI/silc_dlist_end
+ *
+ * SYNOPSIS
+ *
+ *    static inline
+ *    void silc_dlist_end(SilcDList list);
+ *
+ * DESCRIPTION
+ *
+ *    Set the end of the list. This prepares the list for traversing entries
+ *    from the end of the list towards start of the list.
+ *
+ ***/
+
+static inline
+void silc_dlist_end(SilcDList list)
+{
+  silc_list_end(list->list);
+}
+
 /****f* silcutil/SilcDListAPI/silc_dlist_add
  *
  * SYNOPSIS
@@ -175,7 +197,9 @@ void silc_dlist_start(SilcDList list)
 static inline
 void silc_dlist_add(SilcDList list, void *context)
 {
-  SilcDListEntry e = (SilcDListEntry)silc_calloc(1, sizeof(*e));
+  SilcDListEntry e = (SilcDListEntry)silc_malloc(sizeof(*e));
+  if (!e)
+    return NULL;
   e->context = context;
   silc_list_add(list->list, e);
 }
@@ -223,8 +247,8 @@ void silc_dlist_del(SilcDList list, void *context)
  *    Returns current entry from the list and moves the list pointer forward
  *    so that calling this next time returns the next entry from the list.
  *    This can be used to traverse the list. Return SILC_LIST_END when the
- *    entire list has been traversed. Later, silc_list_start must be called
- *    again when re-starting list traversing.
+ *    entire list has been traversed. Later, silc_list_start (or
+ *    silc_dlist_end) must be called again when re-starting list traversing.
  *
  * EXAMPLE
  *
index 76dcf69239ed1f92e490336ab2a05225671a2be6..60e1bcc4f31e3ef32b44564ad5ebca45a77a62b8 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  silclist.h
+  silclist.h 
 
-  Author: Timo Sirainen <tss@iki.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2002 Timo Sirainen
+  Copyright (C) 2002 - 2003 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
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *    This is the SilcList context, and is initialized by calling the
  *    function silc_list_init.
  *
- * EXAMPLE
- *
- *     SilcList list;
- *     silc_list_init(list, struct SilcInternalEntryStruct, next);
- *
  ***/
 typedef struct {
-  void *head, *tail;
-  void *current;
-  int offset;
+  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 */
+  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
@@ -69,41 +68,21 @@ typedef struct {
 #define SILC_LIST_END NULL
 /***/
 
-/* Initializes SilcList object. Example:
-
-   SilcList list;
-
-   silc_list_init(list, struct SilcInternalEntryStruct, next);
-
-   Where `list' is the defined list, and second argument is the structure
-   of the entries in the list and last argument is the pointer in the entry
-   structure that is used as list member. SilcInternalEntry might be as 
-   follows:
-
-   struct SilcInternalEntryStruct {
-     char *dummy;
-     struct SilcInternalEntryStruct *next; // The list member pointer
-   };
-
-   The `next' pointer in the structure (or some other pointer for that matter)
-   is given for the silc_list_init as the last argument. This pointer is used
-   by the list routines to link the entries together in the list. Your code
-   should not touch the member pointer manually.
-*/
-
 /****f* silcutil/SilcList/silc_list_init
  *
  * SYNOPSIS
  *
- *    #define silc_list_init(list, type, field) ...
+ *    #define silc_list_init(list, type, nextfield) ...
  *
  * DESCRIPTION
  *
  *    This macro initializes the SilcList list.  The `list' is the defined
  *    list, second argument is the structure of the entries in the list,
- *    and last argument is the pointer in the entry structure that is used
- *    as list member.  When using SilcList, you should not touch the
- *    structure member pointer (the `next' for example) manually.
+ *    and last argument is the pointer in the structure that is used
+ *    as next list members.  When using SilcList you must not touch the
+ *    structure member pointers manually.  If your list has also a prev
+ *    pointer should use silc_list_init_prev instead of this call if
+ *    you need to be able traverse the list backwards as well.
  *
  * EXAMPLE
  *
@@ -116,16 +95,53 @@ typedef struct {
  *    silc_list_init(list, struct SilcInternalEntryStruct, next);
  *
  ***/
-#define silc_list_init(list, type, field) \
-  __silc_list_init(&(list), silc_offsetof(type, field))
-
-static inline void __silc_list_init(SilcList *list, int offset)
-{
-  list->head = list->tail = list->current = SILC_LIST_END;
-  list->offset = offset;
-}
+#define silc_list_init(list, type, nextfield)          \
+do {                                                   \
+  (list).count = 0;                                    \
+  (list).next_offset = silc_offsetof(type, nextfield); \
+  (list).prev_set = 0;                                 \
+  (list).prev_offset = 0;                              \
+  (list).head = (list).tail = (list).current = NULL;   \
+} while(0)
 
-#define list_next(list, pos) ((void **) ((char *) (pos) + (list)->offset))
+/****f* silcutil/SilcList/silc_list_init_prev
+ *
+ * SYNOPSIS
+ *
+ *    #define silc_list_init_prev(list, type, nextfield, prevfield) ...
+ *
+ * DESCRIPTION
+ *
+ *    This macro initializes the SilcList list.  The `list' is the defined
+ *    list, second argument is the structure of the entries in the list,
+ *    and last two arguments are the pointers in the structure that is used
+ *    as next and prev list members.  When using SilcList you must not
+ *    touch the structure member pointers manually.
+ *
+ *    Having both next and prev pointers makes it possible to traverse
+ *    list from both ends of the list (from start to end, and from end
+ *    to start).
+ *
+ * EXAMPLE
+ *
+ *    struct SilcInternalEntryStruct {
+ *      char *dummy;
+ *      struct SilcInternalEntryStruct *next; // The list member pointer
+ *      struct SilcInternalEntryStruct *prev; // The list member pointer
+ *    };
+ *
+ *    SilcList list;
+ *    silc_list_init(list, struct SilcInternalEntryStruct, next, prev);
+ *
+ ***/
+#define silc_list_init_prev(list, type, nextfield, prevfield)  \
+do {                                                           \
+  (list).count = 0;                                            \
+  (list).next_offset = silc_offsetof(type, nextfield);         \
+  (list).prev_offset = silc_offsetof(type, prevfield);         \
+  (list).prev_set = 1;                                         \
+  (list).head = (list).tail = (list).current = NULL;           \
+} while(0)
 
 /****f* silcutil/SilcList/silc_list_count
  *
@@ -138,17 +154,7 @@ static inline void __silc_list_init(SilcList *list, int offset)
  *    Returns the number of entries in the list indicated by `list'.
  *
  ***/
-#define silc_list_count(list) __silc_list_count(&(list))
-static inline int __silc_list_count(SilcList *list)
-{
-  int count = 0;
-  void *pos;
-
-  for (pos = list->head; pos != NULL; pos = *list_next(list, pos))
-    count++;
-
-  return count;
-}
+#define silc_list_count(list) (list).count
 
 /****f* silcutil/SilcList/silc_list_start
  *
@@ -159,10 +165,37 @@ static inline int __silc_list_count(SilcList *list)
  * DESCRIPTION
  *
  *    Sets the start of the list.  This prepares the list for traversing
- *    the entries from the start of the list.
+ *    the entries from the start of the list towards end of the list.
+ *
+ ***/
+#define silc_list_start(list)                          \
+  ((list).current = (list).head, (list).end_set = 0)
+
+/****f* silcutil/SilcList/silc_list_end
+ *
+ * SYNOPSIS
+ *
+ *    #define silc_list_end(list) ...
+ *
+ * DESCRIPTION
+ *
+ *    Sets the end of the list.  This prepares the list for traversing
+ *    the entries from the end of the list towards start of the list.
+ *
+ * NOTES
+ *
+ *    You can use this call only if you initialized the list with
+ *    silc_list_init_prev.
  *
  ***/
-#define silc_list_start(list) (list).current = (list).head;
+#define silc_list_end(list)                            \
+  ((list).current = (list).tail, (list).end_set = 1)
+
+/* Macros to get position to next and prev list pointers */
+#define __silc_list_next(list, pos)                            \
+  ((void **)((unsigned char *)(pos) + (list).next_offset))
+#define __silc_list_prev(list, pos)                            \
+  ((void **)((unsigned char *)(pos) + (list).prev_offset))
 
 /****f* silcutil/SilcList/silc_list_add
  *
@@ -176,17 +209,18 @@ static inline int __silc_list_count(SilcList *list)
  *    by `list'.
  *
  ***/
-#define silc_list_add(list, entry) __silc_list_add(&(list), entry)
-static inline void __silc_list_add(SilcList *list, void *data)
-{
-  if (list->head == NULL)
-    list->head = data;
-  else
-    *list_next(list, list->tail) = data;
-
-  list->tail = data;
-  *list_next(list, data) = NULL;
-}
+#define silc_list_add(list, entry)                     \
+do {                                                   \
+  if (!(list).head)                                    \
+    (list).head = (entry);                             \
+  else                                                 \
+    *__silc_list_next(list, (list).tail) = (entry);    \
+  if ((list).prev_set)                                 \
+    *__silc_list_prev(list, entry) = (list).tail;      \
+  (list).tail = (entry);                               \
+  *__silc_list_next(list, entry) = NULL;               \
+  (list).count++;                                      \
+} while(0)
 
 /****f* silcutil/SilcList/silc_list_del
  *
@@ -199,32 +233,31 @@ static inline void __silc_list_add(SilcList *list, void *data)
  *    Remove entry indicated by `entry' from the list indicated by `list'.
  *
  ***/
-#define silc_list_del(list, data) __silc_list_del(&(list), data)
-static inline void __silc_list_del(SilcList *list, void *data)
-{
-  void **pos, *prev;
-
-  prev = NULL;
-  for (pos = &list->head; *pos != NULL; pos = list_next(list, *pos)) {
-    if (*pos == data) {
-      *pos = *list_next(list, data);
-      if (list->current == data)
-        list->current = *pos;
-      break;
-    }
-
-    prev = *pos;
-  }
-
-  if (data == list->tail)
-    list->tail = prev;
-}
+#define silc_list_del(list, entry)                                     \
+do {                                                                   \
+  void **p, *prev;                                                     \
+  prev = NULL;                                                         \
+  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)                                       \
+        *__silc_list_prev(list, *p) = *__silc_list_prev(list, entry);  \
+      if ((list).current == (entry))                                   \
+        (list).current = *p;                                           \
+      (list).count--;                                                  \
+      break;                                                           \
+    }                                                                  \
+    prev = *p;                                                         \
+  }                                                                    \
+  if (entry == (list).tail)                                            \
+    (list).tail = prev;                                                        \
+} while(0)
 
 /****f* silcutil/SilcList/silc_list_get
  *
  * SYNOPSIS
  *
- *    #define silc_list_get(list, entry) ...
+ *    #define silc_list_get(list) ...
  *
  * DESCRIPTION
  *
@@ -233,28 +266,32 @@ static inline void __silc_list_del(SilcList *list, void *data)
  *    return the next entry from the list.  This can be used to traverse
  *    the list.  Returns SILC_LIST_END when the entire list has been
  *    tarversed and no additional entries exist in the list. Later,
- *    silc_list_start must be called again when re-starting the list
- *    tarversing.
+ *    silc_list_start (or silc_list_end) must be called again when
+ *    re-starting the list tarversing.
  *
  * EXAMPLE
  *
  *    // Traverse the list from the beginning to the end 
- *    silc_list_start(list)
+ *    silc_list_start(list);
+ *    while ((entry = silc_list_get(list)) != SILC_LIST_END) {
+ *      ...
+ *    }
+ *
+ *    // Traverse the list from the end to the beginning
+ *    silc_list_end(list);
  *    while ((entry = silc_list_get(list)) != SILC_LIST_END) {
  *      ...
  *    }
  *
  ***/
 #define silc_list_get(x) __silc_list_get(&(x))
-
 static inline
 void *__silc_list_get(SilcList *list)
 {
-  void *pos;
-
-  pos = list->current;
-  if (pos != NULL)
-    list->current = *list_next(list, pos);
+  void *pos = list->current;
+  if (pos)
+    list->current = (list->end_set ? *__silc_list_prev(*list, pos) :
+                    *__silc_list_next(*list, pos));
   return pos;
 }