Simple data structures / objects in plain C Snapshot
sring.h
Go to the documentation of this file.
00001 /* Copyright (c) Michael Moser (2011) . 3-clause BSD License applies */
00002 
00003 #ifndef _VBASE_SRING_H_
00004 #define _VBASE_SRING_H_
00005 
00006 #ifdef  __cplusplus
00007 extern "C" {
00008 #endif
00009 
00010 #include <cutils/base.h>
00011 
00012 /**
00013  * @defgroup SRING
00014  * @brief Single linked circular list data structure; where each list element can be of different length. Each element has a pointer to the next element of the list.
00015  *
00016  * In a circularly-linked list, the first and final nodes are linked together. 
00017  * To traverse a circular linked list, you begin at any node and follow the list 
00018  * in either direction until you return to the original node. Viewed another way, 
00019  * circularly-linked lists can be seen as having no beginning or end. This type of 
00020  * list is most useful for managing buffers for data ingest, and in cases where you 
00021  * have one object in a list and wish to see all other objects in the list.
00022  * The pointer pointing to the whole list is usually called the end pointer.
00023  * (quoted from http://en.wikipedia.org/wiki/Linked_list )
00024  *
00025  * Usage: If the user wants to link his struct(ure) into a list, then he must embed a SLIST_entry into his structure.
00026  * Access to user defined structure is via embedded DRING.
00027  *
00028  * Note: unlike SLIST it is not easy to get the last element in the list; in SLIST we have a pointer to the last element
00029  * int the list header; with SRING we have to traverse the whole list in order to get to the last element.
00030  * @{
00031  */
00032 typedef struct tagSRING 
00033 {
00034   struct tagSRING *next;
00035 }  
00036   SRING;
00037 
00038 typedef void     (*SRING_VISITOR_V)(SRING *entry, void *context);
00039 typedef int32_t  (*SRING_VISITOR)       (SRING *entry, void *context);
00040 typedef int32_t  (*SRING_COMPARE)       (SRING *, SRING *);
00041 
00042 /**
00043  * @brief initialises an empty list head
00044  * @param head list head 
00045  */
00046 M_INLINE void SRING_init( SRING *list ) 
00047 { 
00048   list->next = list; 
00049 }
00050 
00051 /**
00052  * @brief checks if argument list is empty
00053  * @param list pointer to list.
00054  * @returns not zero for non empty list.
00055  */
00056 M_INLINE int SRING_isempty( SRING *list ) 
00057 {
00058   return list->next == list;
00059 }
00060 
00061 /**
00062  * @brief insert new entry after a given entry.
00063  * @param newentry pointer to new list entry (to be inserted).
00064  * @param list current position (newentry inserted after this one).
00065  */
00066 M_INLINE void SRING_insert_after( SRING *list, SRING *newentry) 
00067 {
00068    newentry->next = list->next;
00069    list->next = newentry;
00070 }
00071 
00072 
00073 M_INLINE SRING * SRING_unlink_after( SRING *list ) 
00074 {
00075    SRING *ret;
00076    
00077    if (SRING_isempty(list)) {
00078                 return 0;
00079    }
00080 
00081    ret = list->next;
00082    list->next = list->next->next;
00083    return ret;
00084 }
00085 
00086 
00087 M_INLINE void SRING_push_front( SRING *list, SRING *newentry)
00088 {
00089   SRING_insert_after( list, newentry );
00090 }
00091 
00092 /**
00093  * @brief remove the first element from list (used to maintain double ended queue)
00094  * @param elem (in) list head
00095  * @return the first element of list, NULL if list empty
00096  */
00097 M_INLINE SRING * SRING_pop_front( SRING *elem )
00098 {
00099   SRING *ret;
00100   if (SRING_isempty( elem )) {
00101     return 0;
00102   }
00103   ret = elem->next;
00104   SRING_unlink_after(elem);
00105   return ret;  
00106 }
00107 
00108 
00109 M_INLINE SRING *SRING_get_first(SRING *list)
00110 {
00111 
00112   if (SRING_isempty(list)) {
00113     return 0;
00114   }  
00115   return list->next;
00116 }
00117 
00118 
00119 M_INLINE SRING *SRING_get_last(SRING *list)
00120 {
00121   SRING *cur;
00122 
00123   if (SRING_isempty(list)) {
00124     return 0;
00125   }  
00126   
00127   for(cur = list;cur->next != list; cur = cur->next);
00128 
00129   return cur;
00130 }
00131 
00132 M_INLINE SRING *SRING_get_next( SRING *end, SRING *cur ) 
00133 {
00134   if (cur->next == end) { 
00135     return 0;
00136   }
00137   return cur->next;
00138 
00139 }
00140 
00141 /**
00142  * @brief Macro for iterate over all elements of a list, the list is traversed in forward direction from first element to the last element.
00143  * @param loopvarname (type SRING *) pointer to the current element
00144  * @param list (type SRING *) pointer to the list that is traversed
00145  */
00146 #define SRING_FOREACH( loopvarname, list )\
00147   for((loopvarname) = (list)->next;\
00148       (loopvarname) != (list);\
00149       (loopvarname) = (loopvarname )->next )
00150 
00151 
00152 
00153 /**
00154  * @brief Macro for iterate over all elements of a list, You may delete the current element; the list is traversed in forward direction from first element to the last element.
00155  * @param loopvarname (type DRING *) pointer to the current element
00156  * @param loopvarnamenext (type DRING *) do not modify! pointer to next element after current element (used for iteration).
00157  * @param list (type DRING *) pointer to the list that is traversed
00158  */
00159 #define SRING_FOREACH_SAVE( loopvarname, loopvarnext, list ) \
00160   for((loopvarname) = (list)->next, (loopvarnext) = (loopvarname)->next;  \
00161       (loopvarname) != (list);\
00162       (loopvarname) = (loopvarnext), (loopvarnext) = (loopvarname)->next   )
00163 
00164 
00165 #define SRING_FOREACH_SAVE_END( loopvarname, loopvarnext, list )\
00166    }
00167 
00168 /*
00169  * @brief: return number of elements in list
00170  * the whole list structure is traversed.
00171  */
00172 M_INLINE size_t SRING_size( SRING *list )
00173 {
00174         size_t sz;
00175         SRING * cur;
00176         
00177         SRING_FOREACH( cur, list ) {
00178                 sz += 1;
00179         }
00180         return sz;
00181 }
00182 
00183 /*
00184  * @brief get the nth element of a list as counted from the beginning of the list.
00185  * @brief list (IN) the object
00186  * @brief nth  (IN) index of element to retrieve (null based).
00187  */
00188 M_INLINE SRING *SRING_get_nth( SRING *list, size_t nth)
00189 {
00190 
00191         SRING *elm;
00192 
00193         for(elm = SRING_get_first(list); elm != 0 && nth > 0; nth -= 1, elm = SRING_get_next(list, elm)); 
00194 
00195         return elm;
00196 }
00197 
00198 /*
00199  * @brief get the nth element of a list as counted from the end of the list.
00200  * @brief list (IN) the object
00201  * @brief nth  (IN) index of element to retrieve (null based).
00202  * traverses the list twice: once to get size of list, then to get the element.
00203  */
00204 M_INLINE SRING *SRING_get_nth_reverse( SRING *list, size_t nth)
00205 {       
00206         return SRING_get_nth( list, SRING_size( list ) - nth );
00207 }
00208 
00209 
00210 /**
00211  * @brief iterate over all elements of a list, callback is invoked for either element of the list.
00212  *
00213  * @param lst (in) the object.
00214  * @param eval (in) callback that is called to visit each set (or cleared) bit.
00215  * @param context (in) pointer that is passed as context on each invocation of callback.
00216  */
00217 M_INLINE void SRING_foreach( SRING *lst, SRING_VISITOR_V eval, void *context)
00218 {
00219         SRING *cur;
00220 
00221         if (!eval) {
00222           return ;
00223         }
00224 
00225         SRING_FOREACH(  cur, lst ) {
00226                 eval( cur, context );
00227 
00228         }               
00229 }
00230 
00231 /**
00232  * @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.
00233  * 
00234  * @param lst (in) the object.
00235  * @param eval (in) callback that is called to visit each set (or cleared) bit.
00236  * @param context (in) pointer that is passed as context on each invocation of callback.
00237  * @param retval (out) optional - the first non zero value returned by eval callback, if NULL then ignored.
00238  * @return the list element that has matched (i.e. element that has been found).
00239  */
00240 M_INLINE SRING *SRING_findif( SRING *lst, SRING_VISITOR eval, void *context, int32_t *retval)
00241 {
00242         int ret;
00243         SRING *cur;
00244 
00245         if (retval) {
00246                 *retval = 0;
00247         }
00248 
00249         if (!eval) {
00250           return 0;
00251         }
00252 
00253         SRING_FOREACH(  cur, lst ) {
00254                 ret = eval( cur, context );
00255                 if (ret) {
00256                         if (retval) {
00257                                 *retval = ret;
00258                         }
00259                         return cur;
00260                 }
00261         }
00262 
00263         return 0;
00264 }
00265 
00266 /** 
00267  * @brief iterate over all entries of the list and delete entries that match predicate from the list, and frees the memory (optionally)
00268  * @param list (in) the object.
00269  * @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. 
00270  * @param context (in) data pointer passed to every invocation of check_if
00271  * @param free_ctx (in) interface used to free data of entry, is argument pointer is NULL then nothing is freed.
00272  * @param offset_of_link (in) offset of SRING in embedded structure.
00273  */
00274 M_INLINE size_t SRING_deleteif( SRING *list, SRING_VISITOR check_if, void *context,  int offset_of_link)
00275 {
00276     SRING *cur,*prev,*del;
00277         size_t  ret;
00278 
00279         prev = list;
00280         ret = 0;
00281 
00282         for(cur = prev->next; cur != list;) {
00283     
00284                 if (!check_if || check_if( cur, context))  {
00285 
00286                         cur = cur->next;
00287                         del = SRING_unlink_after( prev );
00288                         if (offset_of_link != -1) {
00289                                 free( _MEMBEROF(del, offset_of_link) );
00290                         }
00291                         ret++;
00292 
00293                 } else {
00294 
00295                         prev = cur;
00296                         cur = cur->next;
00297                 }
00298         }
00299         return ret;
00300 }
00301 
00302 /** 
00303  * @brief iterate over all entries of the list and delete them.
00304  * @param list (in) the object
00305 
00306  * @param on_delete(in) if not NULL then this callback is called prior to deleting each list element.
00307  * @param context (in) if on_delete is not NULL then this pointer is passed as argument to on_delete.
00308 
00309  * @param free_ctx (in) allocation interface used to free data of entry.
00310  * @param offset_of_link (in) offset of SRING in embedded structure.
00311  * @return nunmber of elements deleted.
00312  */
00313 M_INLINE void SRING_deleteall( SRING *list, SRING_VISITOR_V on_delete, void *context, int offset_of_link)
00314 {
00315     SRING *cur,*prev,*del;
00316 
00317         prev = list;
00318 
00319         for(cur = prev->next; cur != list;) {
00320     
00321                 if (on_delete) {
00322                         on_delete( cur, context);
00323                 }
00324 
00325                 cur = cur->next;
00326                 del = SRING_unlink_after( prev );
00327                 if (offset_of_link != -1) {
00328                         free( _MEMBEROF(del, offset_of_link) );
00329                 }
00330         }
00331 }
00332 
00333 
00334 /**
00335  * @brief insert new element into sorted list; Maintains ordering relationship between elements of linked list (sort by ascending order)
00336  * 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.
00337  * @param list (in) the object
00338  * @param compare (in) comparison function.
00339  * @param newentry (in) element that is to be inserted into list.
00340  */
00341 M_INLINE void SRING_insert_sorted( SRING *list, SRING_COMPARE compare, SRING *newentry) 
00342 {
00343         SRING *cur,*prev;
00344         
00345         prev = list;
00346         SRING_FOREACH(  cur, list ) {
00347                 if (compare(cur,newentry) > 0) {
00348 
00349                         SRING_insert_after(prev,newentry);
00350                         return;
00351                 }
00352                 prev = cur;
00353         }
00354 
00355         SRING_insert_after(prev,newentry);
00356 }
00357 
00358 /**
00359  * @brief Reverse a list
00360  * This function turns the first element into the last element, the second element
00361  * into the one before the last, and so on and on.
00362  * @param header (in) the object
00363  */
00364 M_INLINE void SRING_reverse( SRING *lst )
00365 {
00366         SRING *cur = lst->next, *next, *tmp;
00367 
00368         if (cur) {
00369                 next = cur->next;
00370                 cur->next = lst;
00371 
00372                 while(next != lst) {
00373                         tmp = next->next;
00374                         
00375                         next->next = cur;
00376                         cur = next;
00377                         next = tmp;
00378                 }
00379                 lst->next = cur;
00380         }
00381 }
00382 
00383 
00384 /**
00385  * verify list for consistency, i.e. that it does not have loops.
00386  * Uses Floyds cycle finding algorithm.
00387  * http://en.literateprograms.org/Floyd%27s_cycle-finding_algorithm_%28C%29
00388  * @return 0 if list is inconsistent.
00389  */
00390 M_INLINE size_t SRING_check( SRING *head )
00391 {
00392         SRING *slow, *fast = head->next;
00393 
00394         SRING_FOREACH( slow, head ) {
00395                 if (!slow) {
00396                         return 0;
00397                 }
00398                 fast = fast->next;
00399                 if (!fast) {
00400                         return 0;
00401                 }
00402                 if (fast == head) {
00403                         return 1;
00404                 }
00405                 if (fast == slow) {
00406                         return 0;
00407                 }
00408 
00409                 fast = fast->next;
00410                 if (!fast) {
00411                         return 0;
00412                 }
00413                 if (fast == head) {
00414                         return 1;
00415                 }
00416                 if (fast == slow) {
00417                         return 0;
00418                 }
00419         }
00420         return 1;
00421 }
00422 
00423 /**
00424  * @}
00425  */
00426  
00427 
00428 #ifdef  __cplusplus
00429 }
00430 #endif
00431 
00432 #endif