Simple data structures / objects in plain C Snapshot
slist.h
Go to the documentation of this file.
00001 /* Copyright (c) Michael Moser (2011) . 3-clause BSD License applies */
00002 
00003 #ifndef _VBASE_SLIST_H_
00004 #define _VBASE_SLIST_H_
00005 
00006 #ifdef  __cplusplus
00007 extern "C" {
00008 #endif
00009 
00010 #include <cutils/base.h>
00011 
00012 
00013 /**
00014  * @defgroup SLIST_entry
00015  * @brief an entry in single linked list, add to structure as member in order to make structure storable in hash table.
00016  * If the user wants to link his struct(ure) into a SLIST linked list, then he must embed a DLIST_entry into his structure.
00017  * Access to user defined structure is via embedded SLIST_entry.
00018  * @{
00019  */
00020 typedef struct tagSLIST_entry 
00021 {
00022   struct tagSLIST_entry *next;
00023 }  
00024   SLIST_entry;
00025 
00026 /**
00027  * @}
00028  */
00029 
00030 
00031 /**
00032  * @defgroup SLIST
00033  * @brief Single linked list data structure; where each list element can be of different length.
00034  *
00035  * This single linked list has a list header (SLIST).
00036  * The list header points to the first and last element in the list.
00037  * last elements next pointer is NULL.
00038  *
00039  * Usage: If the user wants to link his struct(ure) into a list, then he must embed a SLIST_entry into his structure.
00040  * Access to user defined structure is via embedded SLIST_entry.
00041  *
00042  * The list header contains a counter of elements (feature can be surpressed by defining #define SLIST_NO_ELMCOUNT
00043  * @{
00044  */
00045 typedef struct {
00046 #ifndef SLIST_NO_ELMCOUNT
00047         size_t elmcount;   /** number of elements in list */ 
00048 #endif
00049         SLIST_entry *prev, *next; /** root.prev - pointer to first element,  root.next - pointer to last element */
00050 } SLIST;
00051 
00052 
00053 
00054 typedef void    (*SLIST_VISITOR_V)(SLIST *list, SLIST_entry *entry, void *context);
00055 typedef int32_t (*SLIST_VISITOR)  (SLIST *list, SLIST_entry *entry, void *context);
00056 typedef int32_t (*SLIST_COMPARE)  (SLIST_entry *,  SLIST_entry * );
00057 
00058 
00059 /**
00060  * @brief initialises an empty list head
00061  * @param head list head 
00062  */
00063 M_INLINE void SLIST_init( SLIST *head ) 
00064 { 
00065   head->next = head->prev = 0; 
00066 #ifndef SLIST_NO_ELMCOUNT
00067   head->elmcount = 0;
00068 #endif
00069 }
00070 
00071 /**
00072  * @brief checks if argument list is empty
00073  * @param list pointer to list.
00074  * @returns not zero for non empty list.
00075  */
00076 M_INLINE int SLIST_isempty( SLIST *head ) 
00077 {
00078   return head->next == 0;
00079 }
00080 
00081 
00082 /**
00083  * @brief insert new entry after a given entry.
00084  * @param list pointer to list head
00085  * @param pos current posision (newentry inserted after this one).
00086  * @param newentry pointer to new list entry (to be inserted).
00087  */
00088 M_INLINE void SLIST_insert_after( SLIST *list, SLIST_entry *pos, SLIST_entry *newentry) 
00089 {
00090         if (list->next) {
00091 
00092                 if (pos){
00093                 
00094                         newentry->next = pos->next;
00095                         pos->next = newentry;
00096 
00097                         if (!newentry->next) {
00098                                 list->next = newentry;
00099                         } 
00100 
00101                 } else  {
00102                         newentry->next = list->prev;
00103                         list->prev = newentry;
00104                 } 
00105 
00106 
00107         } else {
00108 
00109                 list->next = list->prev = newentry;
00110                 newentry->next =  0;            
00111 
00112         }
00113 
00114 
00115 #ifndef SLIST_NO_ELMCOUNT
00116         list->elmcount ++;
00117 #endif
00118 }
00119 
00120 
00121 /**
00122  * @brief delete an element from a list.
00123  * @param list deletes this list element
00124  */
00125 M_INLINE SLIST_entry *SLIST_unlink_after( SLIST *list, SLIST_entry *link ) 
00126 {
00127         SLIST_entry *ret;
00128 
00129         if (!link) {
00130                 ret = list->prev;
00131                 list->prev = ret->next;
00132                 if (!list->prev) {
00133                         list->next = 0;
00134                 }
00135                 
00136         } else {
00137                 ret = link->next;       
00138                 if (!ret) {
00139                         return 0;
00140                 }
00141 
00142                 link->next = ret ? ret->next : 0;
00143 
00144                 if (link->next == 0) {
00145                         list->next = link;
00146                 }
00147 
00148         }
00149 #ifndef SLIST_NO_ELMCOUNT
00150                 list->elmcount --;
00151 #endif
00152 
00153         return ret;
00154 }
00155 
00156 
00157 /**
00158  * @brief insert element as last in list.
00159  * @param list list head.
00160  * @param newentry entry to be inserted into list
00161  */
00162 M_INLINE void SLIST_push_back( SLIST *list, SLIST_entry *newentry)
00163 {
00164   SLIST_insert_after( list, list->next, newentry );
00165 }
00166 
00167 /**
00168  * @brief insert element as first in list (used to maintain queue)
00169  * @param list list head
00170  * @param newentry entry to be inserted into list
00171  */
00172 M_INLINE void SLIST_push_front( SLIST *list, SLIST_entry *newentry)
00173 {
00174   SLIST_insert_after( list, 0, newentry );
00175 }
00176 
00177 /**
00178  * @brief append contents of one list onto the other
00179  * @param dest 
00180  * @param src  content of this list is appended onto dest list. postcondition: src list is empty
00181  */
00182 
00183 M_INLINE void SLIST_append( SLIST *dest, SLIST *src) 
00184 {
00185         if (SLIST_isempty(dest)) {
00186                 
00187                 if (!SLIST_isempty(src)) {
00188 
00189                         dest->prev = src->prev;
00190                         dest->next = src->next;
00191 #ifndef SLIST_NO_ELMCOUNT
00192                         dest->elmcount = src->elmcount;
00193 #endif  
00194 
00195                         //SLIST_init(src);
00196                 } 
00197 
00198         } else {
00199 
00200                 if (!SLIST_isempty(src)) {
00201                         dest->next->next = src->prev;
00202                         dest->next = src->next;
00203 #ifndef SLIST_NO_ELMCOUNT
00204                         dest->elmcount += src->elmcount;
00205 #endif  
00206                         //SLIST_init(src);
00207 
00208                 }
00209         }
00210 
00211 
00212 }
00213 
00214 
00215 /**
00216  * @brief remove the first element from list (used to maintain queue)
00217  * @param list (in) the object
00218  * @return the first element of list, NULL if list empty
00219  */
00220 M_INLINE SLIST_entry * SLIST_pop_front( SLIST *list )
00221 {
00222   return SLIST_unlink_after(list, 0);
00223 }
00224 
00225 
00226 M_INLINE SLIST_entry *SLIST_get_first(SLIST *list)
00227 {
00228   return list->prev;
00229 }
00230 
00231 M_INLINE SLIST_entry *SLIST_get_last(SLIST *list)
00232 {
00233   return list->next;
00234 }
00235 
00236 
00237 M_INLINE SLIST_entry *SLIST_get_next( SLIST_entry *cur) 
00238 {
00239   return cur->next;
00240 }
00241 
00242 
00243 
00244 
00245 /**
00246  * @brief Macro for iterate over all elements of a list, the list is traversed in forward direction from first element to the last element.
00247  * @param loopvarname (type SLIST_entry *) pointer to the current element
00248  * @param list (type SLIST *) pointer to the list that is traversed
00249  */
00250 #define SLIST_FOREACH( loopvarname, list )\
00251   for((loopvarname) = (list)->prev;\
00252       (loopvarname) != 0;\
00253       (loopvarname) = (loopvarname)->next )
00254 
00255 /**
00256  * @brief Macro for iterate over all elements of a list, the list is traversed in forward direction from first element to the last element.
00257  * @param loopvsarname (type SLIST_entry *) pointer to the current element
00258  * @param list (type SLIST *) pointer to the list that is traversed
00259  */
00260 #define SLIST_FOREACH_SAVE( loopvarname, loopvarnamenext, list )\
00261   for((loopvarname) = (list)->prev, (loopvarnamenext) = (loopvarname) ? (loopvarname)->next : 0;\
00262       (loopvarname) != 0;\
00263       (loopvarname) = (loopvarnamenext),  (loopvarnamenext) = (loopvarname) ? (loopvarname)->next : 0 )
00264 
00265 
00266 /**
00267  * @brief: return number of elements in list
00268  * if we don't have element count in list (SLIST_NO_ELMCOUNT defined), then the whole list structure is traversed.
00269  */
00270 M_INLINE size_t SLIST_size( SLIST *list )
00271 {
00272 #ifndef SLIST_NO_ELMCOUNT
00273         return list->elmcount;
00274 #else
00275         size_t sz;
00276         SLIST_entry * cur;
00277         
00278         SLIST_FOREACH( cur, list ) {
00279                 sz += 1;
00280         }
00281         return sz;
00282 #endif
00283 }
00284 
00285 /*
00286  * @brief get the nth element of a list as counted from the beginning of the list.
00287  * @brief list (IN) the object
00288  * @brief nth  (IN) index of element to retrieve (null based).
00289  */
00290 M_INLINE SLIST_entry *SLIST_get_nth( SLIST *list, size_t nth)
00291 {
00292 
00293         SLIST_entry *elm;
00294 
00295 #ifndef SLIST_NO_ELMCOUNT
00296         if (nth >= list->elmcount) {
00297                 return 0;
00298         }
00299 
00300         for(elm = SLIST_get_first(list); nth > 0; nth -= 1, elm = SLIST_get_next(elm)); 
00301 #else
00302         for(elm = SLIST_get_first(list); elm != 0 && nth > 0; nth -= 1, elm = SLIST_get_next(elm)); 
00303 #endif
00304 
00305         return elm;
00306 }
00307 
00308 /**
00309  * @brief get the nth element of a list as counted from the end of the list.
00310  * @brief list (IN) the object
00311  * @brief nth  (IN) index of element to retrieve (null based).
00312  */
00313 M_INLINE SLIST_entry *SLIST_get_nth_reverse( SLIST *list, size_t nth)
00314 {       
00315         return SLIST_get_nth( list, SLIST_size( list ) - nth );
00316 }
00317 
00318 
00319 /**
00320  * @brief iterate over all elements of a list, callback is invoked for either element of the list.
00321  *
00322  * @param lst (in) the object.
00323  * @param eval (in) callback that is called to visit each set (or cleared) bit.
00324  * @param context (in) pointer that is passed as context on each invocation of callback.
00325  */
00326 M_INLINE void SLIST_foreach( SLIST *lst, SLIST_VISITOR_V eval, void *context)
00327 {
00328     SLIST_entry *cur;
00329 
00330         if (!eval) {
00331           return;
00332         }
00333 
00334         SLIST_FOREACH( cur, lst) {
00335                 eval( lst, cur, context);
00336         }
00337         
00338 }
00339 
00340 /**
00341  * @brief find an element within the linked list. callback is invoked for each element of the list, in forward direction from first element to last element; when the callback returns non zero value the iteration stops as we have found what we searched for.
00342  * 
00343  * @param lst (in) the object.
00344  * @param eval (in) callback that is called to visit each set (or cleared) bit.
00345  * @param context (in) pointer that is passed as context on each invocation of callback.
00346  * @param retval (out) optional - the first non zero value returned by eval callback, if NULL then ignored.
00347  * @return the list element that has matched (i.e. element that has been found).
00348  */
00349 M_INLINE SLIST_entry *SLIST_findif( SLIST *lst, SLIST_VISITOR eval, void *context, int32_t *retval)
00350 {
00351         int ret;
00352     SLIST_entry  *cur;
00353 
00354         if (retval) {
00355                 *retval = 0;
00356         }
00357 
00358         if (!eval) {
00359           return 0;
00360         }
00361 
00362         SLIST_FOREACH(  cur, lst ) {
00363                 ret = eval( lst, cur, context);
00364                 if (ret) {
00365                         if (retval) {
00366                                 *retval = ret;
00367                         }
00368 
00369                         return cur;
00370                 }
00371 
00372         }
00373 
00374         return 0;
00375 }
00376 
00377 
00378 /**
00379  * @brief insert new element into sorted list; Maintains ordering relationship between elements of linked list (sort by ascending order)
00380  * A sorting algorithm based on this function is not very efficient; it is of complexity O(n^2); nevertheless usefull if a list is modified and has to be always maintained in sorted order.
00381  * @param list (in) the object
00382  * @param compare (in) comparison function.
00383  * @param newentry (in) element that is to be inserted into list.
00384  */
00385 M_INLINE void SLIST_insert_sorted( SLIST *list, SLIST_COMPARE compare, SLIST_entry *newentry) 
00386 {
00387         SLIST_entry *cur,*prev;
00388         
00389         prev = 0;
00390         SLIST_FOREACH(  cur, list ) {
00391                 if (compare(cur,newentry) > 0) {
00392 
00393                         SLIST_insert_after(list, prev,newentry);
00394                         return;
00395                 }
00396                 prev = cur;
00397         }
00398 
00399         SLIST_push_back( list, newentry );
00400 }
00401 
00402 /** 
00403  * @brief iterate over all entries of the list and delete entries that match predicate from the list, and frees the memory (optionally)
00404  * @param list (in) the object.
00405  * @param check_if (in) predicate function; the function returns 1 then inspected argument element will be deleted; if argument pointer is NULL then all entries will be deleted. 
00406  * @param context (in) data pointer passed to every invocation of check_if
00407  * @param free_ctx (in) interface used to free data of entry, is argument pointer is NULL then nothing is freed.
00408  * @param offset_of_link (in) offset of DLIST_entry in embedded structure.
00409  */
00410 M_INLINE void SLIST_deleteif( SLIST *list, SLIST_VISITOR check_if, void *context,  int offset_of_link)
00411 {
00412     SLIST_entry *cur,*prev,*del;
00413 
00414         prev = 0;
00415 
00416         for(cur = list->prev; cur;) {
00417     
00418                 if (!check_if || check_if( list, cur, context))  {
00419 
00420                         cur = cur->next;
00421                         del = SLIST_unlink_after( list, prev );
00422                         if (offset_of_link != -1) {
00423                                 free( _MEMBEROF(del, offset_of_link) );
00424                         }
00425                         
00426 
00427                 } else {
00428 
00429                         prev = cur;
00430                         cur = cur->next;
00431                 }
00432         }
00433 }
00434 
00435 /** 
00436  * @brief iterate over all entries of the list and delete them.
00437  * @param list (in) the object
00438 
00439  * @param on_delete(in) if not NULL then this callback is called prior to deleting each list element.
00440  * @param context (in) if on_delete is not NULL then this pointer is passed as argument to on_delete.
00441 
00442  * @param free_ctx (in) allocation interface used to free data of entry.
00443  * @param offset_of_link (in) offset of SRING in embedded structure.
00444  */
00445 M_INLINE void SLIST_deleteall( SLIST *list, SLIST_VISITOR_V on_delete, void *context, int offset_of_link)
00446 {
00447     SLIST_entry *cur,*del;
00448 
00449 
00450         for(cur = list->prev; cur;) {
00451     
00452                 if (on_delete)  {
00453                         on_delete( list, cur, context );
00454                 }
00455 
00456                 cur = cur->next;
00457                 del = SLIST_unlink_after( list, 0 );
00458                 if (offset_of_link != -1) {
00459                         free( _MEMBEROF(del, offset_of_link) );
00460                 }
00461         }
00462 }
00463 
00464 
00465 /**
00466  * @brief Reverse a list
00467  * This function turns the first element into the last element, the second element
00468  * into the one before the last, and so on and on.
00469  * @param header (in) the object
00470  */
00471 M_INLINE void SLIST_reverse( SLIST *lst )
00472 {
00473         SLIST_entry *cur = lst->prev, *next;
00474         SLIST_entry *tmp;
00475         if (cur) {
00476                 next = cur->next;
00477                 cur->next = 0;
00478 
00479                 while(next) {
00480                         tmp = next->next;
00481                         
00482                         next->next = cur;
00483                         cur = next;
00484                         next = tmp;
00485                 }
00486 
00487                 tmp = lst->prev;
00488                 lst->prev = lst->next;
00489                 lst->next = tmp;
00490         }
00491 }
00492 
00493 /**
00494  * @brief check consistency of list
00495  * This function checks for loops in the list, and if count of elements in list is the same as counter of elements in list header.
00496  * @param header (in) the object
00497  * @return zero if list contains errors.
00498  */
00499 M_INLINE int SLIST_check(SLIST *header)
00500 {
00501         SLIST_entry *cur,*prev = 0,*fast= 0;
00502 #ifndef SLIST_NO_ELMCOUNT
00503         size_t count = 0;
00504 #endif
00505 
00506         if (!header->prev || !header->next) {
00507                 if (header->prev || header->next) {
00508                         return 0;
00509                 }
00510                 return 1;
00511         }
00512         
00513         fast = header->prev;
00514         SLIST_FOREACH( cur, header ) {
00515 
00516 #ifndef SLIST_NO_ELMCOUNT
00517                 count += 1;
00518 #endif      
00519                 prev = fast;
00520                 fast = fast->next;
00521                 if (!fast) {
00522                         break;
00523                 }
00524                 if (fast == cur) {
00525                         return 0;
00526                 }
00527 
00528 #ifndef SLIST_NO_ELMCOUNT
00529                 count += 1;
00530 #endif
00531             prev = fast;
00532                 fast = fast->next;
00533                 if (!fast) {
00534                         break;
00535                 }
00536                 if (fast == cur) {
00537                         return 0;
00538                 }
00539         }
00540 
00541         if (header->next != prev) {
00542                 return 0;
00543         }
00544 
00545 
00546 #ifndef SLIST_NO_ELMCOUNT
00547         if (header->elmcount != count) {
00548                 return 0;
00549         }
00550 #endif  
00551         return 1;
00552 }
00553 
00554 /**
00555  * @}
00556  */
00557 
00558 #ifdef  __cplusplus
00559 }
00560 #endif
00561 
00562 #endif