Simple data structures / objects in plain C Snapshot
dring.h
Go to the documentation of this file.
00001 /* Copyright (c) Michael Moser (2011) . 3-clause BSD License applies */
00002 
00003 #ifndef _VBASE_DRING_H_
00004 #define _VBASE_DRING_H_
00005 
00006 #ifdef  __cplusplus
00007 extern "C" {
00008 #endif
00009 
00010 #include <cutils/base.h>
00011 
00012 
00013 /**
00014  * @defgroup DRING
00015  * @brief Double linked circular list data structure; where each list element can be of different length. Each element has a pointer to the next and previous element of the list.
00016  *
00017  * In a circularly-linked list, the first and final nodes are linked together. 
00018  * To traverse a circular linked list, you begin at any node and follow the list 
00019  * in either direction until you return to the original node. Viewed another way, 
00020  * circularly-linked lists can be seen as having no beginning or end. This type of 
00021  * list is most useful for managing buffers for data ingest, and in cases where you 
00022  * have one object in a list and wish to see all other objects in the list.
00023  * The pointer pointing to the whole list is usually called the end pointer.
00024  * (quoted from http://en.wikipedia.org/wiki/Linked_list )
00025  *
00026  * Usage: If the user wants to link his struct(ure) into a list, then he must embed a SLIST_entry into his structure.
00027  * Access to user defined structure is via embedded DRING.
00028  * @{
00029  */
00030 typedef struct tagDRING 
00031 {
00032   struct tagDRING *next,*prev;
00033 }  
00034   DRING;
00035 
00036 
00037 typedef void     (*DRING_VISITOR_V) (DRING *entry, void *context);
00038 typedef int32_t  (*DRING_VISITOR) (DRING *entry, void *context);
00039 typedef int32_t  (*DRING_COMPARE) (DRING *, DRING *);
00040 
00041 
00042 /**
00043  * @brief initialises an empty list head
00044  * @param head list head 
00045  */
00046 M_INLINE void DRING_init( DRING *head ) 
00047 { 
00048   head->next = head->prev = head; 
00049 }
00050 
00051 
00052 /**
00053  * @brief checks if argument list is empty
00054  * @param list pointer to list.
00055  * @returns not zero for non empty list.
00056  */
00057 M_INLINE int DRING_isempty( DRING *list ) 
00058 {
00059   return list->next == list;
00060 }
00061 
00062 /**
00063  * @brief insert new entry before a given entry.
00064  * @param newentry pointer to new list entry (to be inserted).
00065  * @param list current position (newentry inserted before this one).
00066  */
00067 M_INLINE void DRING_insert_before( DRING *list, DRING *newentry) 
00068 {
00069    DRING *prev = list->prev;
00070 
00071    prev->next = newentry;    
00072    newentry->next = list;
00073 
00074    newentry->prev = prev; 
00075    list->prev = newentry; 
00076 }
00077 
00078 /**
00079  * @brief insert new entry after a given entry.
00080  * @param newentry pointer to new list entry (to be inserted).
00081  * @param list current posision (newentry inserted after this one).
00082  */
00083 M_INLINE void DRING_insert_after( DRING *list, DRING *newentry) 
00084 {
00085    DRING_insert_before( list->next, newentry );
00086 }
00087 
00088 /**
00089  * @brief insert new element into sorted list; Maintains ordering relationship between elements of linked list (sort by ascending order)
00090  * 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.
00091  * @param list (in) the object
00092  * @param compare (in) comparison function.
00093  * @param newentry (in) element that is to be inserted into list.
00094  */
00095  void DRING_insert_sorted( DRING *list, DRING_COMPARE compare, DRING *newentry); 
00096 
00097 /**
00098  * @brief delete an element from a list.
00099  * @param list deletes this list element
00100  */
00101 M_INLINE DRING *DRING_unlink( DRING *list ) 
00102 {
00103    list->next->prev = list->prev;
00104    list->prev->next = list->next;
00105    return list;
00106 }
00107 
00108 /**
00109  * @brief insert element as last in list (used to maintain double ended queue)
00110  * @param list list head.
00111  * @param newentry entry to be inserted into list
00112  */
00113 M_INLINE void DRING_push_back( DRING *list, DRING *newentry)
00114 {
00115   DRING_insert_after( list->prev, newentry );
00116 }
00117 
00118 /**
00119  * @brief insert element as first in list (used to maintain double ended queue)
00120  * @param list list head.s
00121  * @param newentry entry to be inserted into list
00122  */
00123 M_INLINE void DRING_push_front( DRING *list, DRING *newentry)
00124 {
00125   DRING_insert_after( list, newentry );
00126 }
00127 
00128 /**
00129  * @brief remove first element from list (used to maintain double ended queue)
00130  * @param elem (in) list head
00131  * @return the first element of list, NULL if list empty
00132  */
00133 M_INLINE DRING * DRING_pop_front( DRING *elem )
00134 {
00135   DRING *ret;
00136   if (DRING_isempty( elem )) {
00137     return 0;
00138   }
00139   ret = elem->next;
00140   DRING_unlink(elem->next);
00141   return ret;  
00142 }
00143 
00144 
00145 /**
00146  * @brief remove last element from list (used to maintain double ended queue)
00147  * @param elem (in) list head.
00148  * @return the last element of list, NULL if list empty
00149  */
00150 M_INLINE DRING * DRING_pop_back( DRING *elem )
00151 { 
00152   DRING *ret;
00153 
00154   if (DRING_isempty( elem )) {
00155     return 0;
00156   }
00157   ret = elem->prev;
00158   DRING_unlink(elem->prev);
00159   return ret;
00160 }
00161 
00162 
00163 M_INLINE DRING *DRING_get_first(DRING *list)
00164 {
00165   if (DRING_isempty(list)) {
00166     return 0;
00167   }  
00168   return list->next;
00169 }
00170 
00171 M_INLINE DRING *DRING_get_last(DRING *list)
00172 {
00173   if (DRING_isempty(list)) {
00174     return 0;
00175   }
00176   return list->prev;
00177 }
00178 
00179 
00180 M_INLINE DRING *DRING_get_next( DRING *end, DRING *cur ) 
00181 {
00182   if (cur->next == end) { 
00183     return 0;
00184   }
00185   return cur->next;
00186 
00187 }
00188 
00189 
00190 M_INLINE DRING *DRING_get_prev( DRING *end, DRING *cur ) 
00191 {
00192   if (cur->prev == end) { 
00193     return 0;
00194   }
00195   return cur->prev;
00196 
00197 }
00198 
00199 /**
00200  * @brief Macro for iterate over all elements of a list, the list is traversed in forward direction from first element to the last element.
00201  * @param loopvarname (type DRING *) pointer to the current element
00202  * @param list (type DRING *) pointer to the list that is traversed
00203  */
00204 #define DRING_FOREACH( loopvarname, list )\
00205   for((loopvarname) = (list)->next;\
00206       (loopvarname) != (list);\
00207       (loopvarname) = (loopvarname )->next )
00208 
00209 /**
00210  * @brief Macro for iterate over all elements of a list, the list is traversed in reverse direction from last element to the first element.
00211  * @param loopvarname (type DRING *) pointer to the current element
00212  * @param list (type DRING *) pointer to the list that is traversed
00213  */
00214 #define DRING_FOREACH_REVERSE( loopvarname, list )\
00215   for((loopvarname) = (list)->prev;\
00216       (loopvarname) != (list);\
00217       (loopvarname) = (loopvarname)->prev )
00218 
00219 /**
00220  * @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.
00221  * @param loopvarname (type DRING *) pointer to the current element
00222  * @param loopvarnamenext (type DRING *) do not modify! pointer to next element after current element (used for iteration).
00223  * @param list (type DRING *) pointer to the list that is traversed
00224  */
00225 #define DRING_FOREACH_SAVE( loopvarname, loopvarnext, list )\
00226   for((loopvarname) = (list)->next, (loopvarnext) = (loopvarname)->next;\
00227       (loopvarname) != (list);\
00228       (loopvarname) = (loopvarnext), (loopvarnext) = (loopvarname)->next )
00229 
00230 /**
00231  * @brief Macro for iterate over all elements of a list, You may delete the current element; the list is traversed in reverse direction from last element to the first element.
00232  * @param loopvarname (type DRING *) pointer to the current element
00233  * @param loopvarnamenext (type DRING *) do not modify! pointer to next element after current element (used for iteration).
00234  * @param list (type DRING *) pointer to the list that is traversed
00235  */
00236 #define DRING_FOREACH_REVERSE_SAVE( loopvarname, loopvarnext, list )\
00237   for((loopvarname) = (list)->prev, (loopvarnext) = (loopvarname)->prev;\
00238       (loopvarname) != (list);\
00239       (loopvarname) = (loopvarnext), (loopvarnext) = (loopvarname)->prev )
00240 
00241 /*
00242  * @brief: return number of elements in list
00243  * the whole list structure is traversed.
00244  */
00245 M_INLINE size_t DRING_size( DRING *list )
00246 {
00247         size_t sz;
00248         DRING * cur;
00249         
00250         DRING_FOREACH( cur, list ) {
00251                 sz += 1;
00252         }
00253         return sz;
00254 }
00255 
00256 /*
00257  * @brief get the nth element of a list as counted from the beginning of the list.
00258  * @brief list (IN) the object
00259  * @brief nth  (IN) index of element to retrieve (null based).
00260  */
00261 M_INLINE DRING *DRING_get_nth( DRING *list, size_t nth)
00262 {
00263 
00264         DRING *elm;
00265 
00266         for(elm = DRING_get_first(list); elm != 0 && nth > 0; nth -= 1, elm = DRING_get_next(list, elm)); 
00267 
00268         return elm;
00269 }
00270 
00271 /*
00272  * @brief get the nth element of a list as counted from the end of the list.
00273  * @brief list (IN) the object
00274  * @brief nth  (IN) index of element to retrieve (null based).
00275  * traverses the list twice: once to get size of list, then to get the element.
00276  */
00277 M_INLINE DRING *DRING_get_nth_reverse( DRING *list, size_t nth)
00278 {       
00279         return DRING_get_nth( list, DRING_size( list ) - nth );
00280 }
00281 
00282 /**
00283  * @brief iterate over all elements of a list, callback is invoked for either element of the list. list is traversed forward from first element to the last element.
00284  * @param lst (in) the object.
00285  * @param eval (in) callback that is called to visit each set (or cleared) bit.
00286  * @param context (in) pointer that is passed as context on each invocation of callback.
00287  * @param save_from_del (in) if non zero then callback can remove the current link from the list (a bit slower); if null then not; if zero then can't remove the current link from the list.
00288  */
00289  void DRING_foreach( DRING *lst, DRING_VISITOR_V eval, void *context, int save_from_del);
00290 
00291 /**
00292  * @brief iterate over all elements of a list, callback is invoked for each element of the list. list is traversed backword from last element to the first element.
00293  * @param lst (in) the object.
00294  * @param eval (in) callback that is called to visit each set (or cleared) bit.
00295  * @param context (in) pointer that is passed as context on each invocation of callback.
00296  * @param save_from_del (in) if non zero then callback can remove the current link from the list (a bit slower); if null then not; if zero then can't remove the current link from the list.
00297  */
00298  void DRING_foreach_reverse( DRING *lst, DRING_VISITOR_V eval, void *context, int save_from_delete);
00299 
00300 /**
00301  * @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.
00302  * 
00303  * @param lst (in) the object.
00304  * @param eval (in) callback that is called to visit each set (or cleared) bit.
00305  * @param context (in) pointer that is passed as context on each invocation of callback.
00306  * @param retval (out) optional - the first non zero value returned by eval callback, if NULL then ignored.
00307  * @param save_from_del (in) if non zero then callback can remove the current link from the list (a bit slower); if null then not; if zero then can't remove the current link from the list.
00308  * @return the list element that has matched (i.e. element that has been found).
00309  */
00310 M_INLINE DRING *DRING_findif( DRING *lst, DRING_VISITOR eval, void *context, int32_t *retval, int save_from_del)
00311 {
00312         int ret;
00313     DRING *cur, *next;
00314 
00315         if (retval) {
00316                 *retval = 0;
00317         }
00318 
00319         if (!eval) {
00320                 return 0;
00321         }
00322 
00323         if (save_from_del) {
00324                 DRING_FOREACH_SAVE( cur, next, lst) {
00325                         ret = eval( cur, context );
00326                         if (ret) {
00327                                 if (retval) {
00328                                         *retval = ret;
00329                                 }
00330                                 return cur;
00331                         }
00332                 }
00333         } else {
00334                 DRING_FOREACH(  cur, lst ) {
00335                         ret = eval( cur, context );
00336                         if (ret) {
00337                                 if (retval) {
00338                                         *retval = ret;
00339                                 }
00340                                 return cur;
00341                         }
00342                 }
00343         }
00344                 
00345 
00346         return 0;
00347 }
00348 
00349 
00350 /**
00351  * @brief find an element within the linked list. callback is invoked for each element of the list, in reverse direction from last element to first element; when the callback returns non zero value the iteration stops as we have found what we searched for. 
00352  * 
00353  * @param lst (in) the object.
00354  * @param eval (in) callback that is called to visit each set (or cleared) bit.
00355  * @param context (in) pointer that is passed as context on each invocation of callback.
00356  * @param lastpos (out) position in the list that has been found.
00357  * @param save_from_del (in) if non zero then callback can remove the current link from the list (a bit slower); if null then not; if zero then can't remove the current link from the list.
00358  * @return the list element that has matched (i.e. element that has been found).
00359  */
00360 M_INLINE DRING *DRING_find_reverse( DRING *lst, DRING_VISITOR eval, void *context, int32_t *retval, int save_from_delete)
00361 {
00362         int ret;
00363         DRING *cur, *next;
00364 
00365         if (retval) {
00366                 *retval = 0;
00367         }
00368 
00369         if (!eval) {
00370           return 0;
00371         }
00372 
00373         if ( save_from_delete ) {
00374                 DRING_FOREACH_REVERSE_SAVE( cur, next, lst ) {
00375 
00376                         ret = eval( cur, context );
00377                         if (ret) {
00378                                 if (retval) {
00379                                         *retval = ret;
00380                                 }
00381                                 return cur;
00382                         }
00383                 }
00384         } else {
00385                 DRING_FOREACH_REVERSE( cur, lst ) {
00386 
00387                         ret = eval( cur, context );
00388                         if (ret) {
00389                                 if (retval) {
00390                                         *retval = ret;
00391                                 }
00392                                 return cur;
00393                         }
00394                 }
00395         }
00396 
00397         return 0;
00398 }
00399 
00400 /** 
00401  * @brief iterate over all entries of the list and delete entries that match predicate from the list, and frees the memory (optionally)
00402  * @param list (in) the object.
00403  * @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. 
00404  * @param context (in) data pointer passed to every invocation of check_if
00405  * @param free_ctx (in) interface used to free data of entry, is argument pointer is NULL then nothing is freed.
00406  * @param offset_of_link (in) offset of DRING in embedded structure.
00407  * @return nunmber of elements deleted.
00408  */
00409 M_INLINE size_t DRING_deleteif( DRING *list, DRING_VISITOR check_if, void *context, int offset_of_link)
00410 {
00411     DRING *cur,*next,*del;
00412         size_t  ret = 0;
00413 
00414     DRING_FOREACH_SAVE(cur,next,list) {
00415                 if (!check_if || check_if( cur, context))  {
00416                         del = DRING_unlink( cur );
00417                         if (offset_of_link != -1) {
00418                                 free( _MEMBEROF(del,offset_of_link) );
00419                         }
00420                         ret ++;
00421                 }
00422         }
00423         return ret;
00424 }
00425 
00426 /** 
00427  * @brief iterate over all entries of the list and delete them.
00428  * @param list (in) the object
00429 
00430  * @param on_delete(in) if not NULL then this callback is called prior to deleting each list element.
00431  * @param context (in) if on_delete is not NULL then this pointer is passed as argument to on_delete.
00432 
00433  * @param free_ctx allocation interface used to free data of entry.
00434  * @param offset_of_link offset of DRING in embedded structure.
00435  */
00436 M_INLINE void DRING_deleteall( DRING *list, DRING_VISITOR_V on_delete, void *context, int offset_of_link)
00437 {
00438     DRING *cur,*next,*del;
00439 
00440     DRING_FOREACH_SAVE(cur,next,list) {
00441                 if (on_delete) {
00442                         on_delete( cur, context);
00443                 }
00444                 del = DRING_unlink( cur );
00445                 if (offset_of_link != -1) {
00446                         free( _MEMBEROF(del,offset_of_link) );
00447                 }
00448         }
00449 }
00450 
00451 /**
00452  * @brief Reverse a list
00453  * This function turns the first element into the last element, the second element
00454  * into the one before the last, and so on and on.
00455  * @param header (in) the object
00456  */
00457 M_INLINE void DRING_reverse( DRING *lst )
00458 {
00459         DRING *cur = lst->prev, *next;
00460         DRING *tmp;
00461 
00462         if (cur) {
00463                 next = cur->next;
00464                 cur->next = lst;
00465 
00466                 while(next) {
00467                         tmp = next->next;
00468                         
00469                         next->next = cur;
00470                         cur->prev = next;
00471 
00472                         cur = next;
00473                         next = tmp;
00474                 }
00475 
00476                 tmp = lst->prev;
00477                 lst->prev = lst->next;
00478                 lst->next = tmp;
00479         }
00480 }
00481 
00482 /**
00483  * @brief check consistency of list
00484  * @param lst list header.
00485  * @return zero if list contains errors.
00486  */
00487 M_INLINE int DRING_check(DRING *header)
00488 {
00489         DRING *cur,*next;
00490 
00491         DRING_FOREACH( cur, header ) {
00492 
00493                 next = cur->next;
00494                 if (!next || next->prev != cur) {               
00495                         return 0;
00496                 }
00497         }
00498         return 1;
00499 }
00500 
00501 /**
00502  * @}
00503  */
00504 
00505 #ifdef  __cplusplus
00506 }
00507 #endif
00508 
00509 #endif