Simple data structures / objects in plain C Snapshot
dlist.h
Go to the documentation of this file.
00001 /* Copyright (c) Michael Moser (2011) . 3-clause BSD License applies */
00002 
00003 #ifndef _VBASE_DLIST_H_
00004 #define _VBASE_DLIST_H_
00005 
00006 #ifdef  __cplusplus
00007 extern "C" {
00008 #endif
00009 
00010 #include <cutils/base.h>
00011 
00012 
00013 
00014 /**
00015  * @defgroup DLIST_entry
00016  * @brief an entry in double linked list, add to structure as member in order to make structure storable in hash table.
00017  * If the user wants to link his struct(ure) into a DLIST linked list, then he must embed a DLIST_entry into his structure.
00018  * Access to user defined structure is via embedded SLIST_entry.
00019  * @{
00020  */
00021 typedef struct tagDLIST_entry 
00022 {
00023   struct tagDLIST_entry *next,*prev;
00024 }  
00025   DLIST_entry;
00026 
00027 /*
00028  * @}
00029  */
00030 
00031 
00032 /**
00033  * @defgroup DLIST
00034  * @brief Double linked 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.
00035  *
00036  * This double linked list has a list header (DLIST).
00037  * The list header points to the first and last element in the list.
00038  * first elements previous pointer is NULL; last elements next pointer is NULL
00039  *
00040  * Usage: If the user wants to link his struct(ure) into a list, then he must embed a DLIST_entry into his structure.
00041  * Access to user defined structure is via embedded DLIST_entry.
00042  *
00043  * The list header contains a counter of elements (feature can be surpressed by defining #define DLIST_NO_ELMCOUNT
00044  *
00045  * @{
00046  */
00047 typedef struct {
00048         size_t elmcount;   /** number of elements in list */ 
00049         DLIST_entry root; /** root.next - pointer to first element, root.last pointer to last element */
00050 } DLIST;
00051 
00052 
00053 typedef void     (*DLIST_VISITOR_V)(DLIST *list, DLIST_entry *entry, void *context);
00054 typedef int32_t  (*DLIST_VISITOR)  (DLIST *list, DLIST_entry *entry, void *context);
00055 typedef int32_t  (*DLIST_COMPARE)  (DLIST_entry *,  DLIST_entry * );
00056 
00057 
00058 /**
00059  * @brief initialises an empty list head
00060  * @param head (in) list head 
00061  */
00062 M_INLINE void DLIST_init( DLIST *head ) 
00063 { 
00064   head->root.next = head->root.prev = 0; 
00065   head->elmcount = 0;
00066 }
00067 
00068 /**
00069  * @brief checks if argument list is empty
00070  * @param list pointer to list.
00071  * @returns not zero for non empty list.
00072  */
00073 M_INLINE int DLIST_isempty( DLIST *head ) 
00074 {
00075   return head->root.next == 0;
00076 }
00077 
00078 /**
00079  * @brief insert new entry before a given entry.
00080  * @param list     (in|out) pointer to list head
00081  * @param pos      (in) current position (newentry inserted after this one).
00082  * @param newentry (in) pointer to new list entry (to be inserted).
00083  */
00084 M_INLINE int DLIST_insert_before( DLIST *list, DLIST_entry *pos, DLIST_entry *newentry) 
00085 {
00086         if (list->root.next) {
00087         
00088                 if (!pos) {
00089                         return -1;
00090                 }
00091 
00092                 newentry->prev = pos->prev;
00093                 pos->prev = newentry;
00094                 newentry->next = pos;
00095 
00096                 if (newentry->prev) {
00097                         newentry->prev->next = newentry;
00098                 }
00099 
00100                 if (list->root.prev == pos) {
00101                         list->root.prev = newentry;
00102                 }
00103 
00104 
00105         } else {
00106 
00107                 list->root.next = list->root.prev = newentry;
00108                 newentry->next = newentry->prev = 0;
00109 
00110         }       
00111 
00112         list->elmcount ++;
00113         return 0;
00114 }
00115 
00116 
00117 /**
00118  * @brief insert new entry after a given entry.
00119  * @param list     (in) pointer to list head
00120  * @param pos      (in) current position (newentry inserted after this one).
00121  * @param newentry (in) pointer to new list entry (to be inserted).
00122  */
00123 M_INLINE void DLIST_insert_after( DLIST *list, DLIST_entry *pos, DLIST_entry *newentry) 
00124 {
00125         if (list->root.next) {
00126                 newentry->next = pos->next;
00127                 pos->next = newentry;
00128                 newentry->prev = pos;
00129 
00130                 if (newentry->next) {
00131                         newentry->next->prev = newentry;
00132                 }
00133 
00134                 if (list->root.next == pos) {
00135                         list->root.next = newentry;
00136                 }
00137 
00138 
00139         } else {
00140 
00141                 list->root.next = list->root.prev = newentry;
00142                 newentry->next = newentry->prev = 0;            
00143 
00144         }
00145 
00146 
00147         list->elmcount ++;
00148 }
00149 
00150 
00151 /**
00152  * @brief delete an element from a list.
00153  * @param list (in|out) the object
00154  * @param link (in) deletes this list element
00155  */
00156 M_INLINE DLIST_entry *DLIST_unlink( DLIST *list, DLIST_entry *link ) 
00157 {
00158         DLIST_entry *next,*prev;
00159 
00160         next = link->next;
00161         prev = link->prev;
00162 
00163         if (next) {
00164                 next->prev = link->prev;
00165         }
00166         if (prev) {
00167                 prev->next = link->next;
00168         }
00169 
00170         if (list->root.next == link) {
00171                 list->root.next = prev; 
00172         }
00173         
00174         if (list->root.prev == link) {
00175                 list->root.prev = next;
00176         }
00177 
00178         link->prev = link->next = 0;
00179 
00180         list->elmcount --;
00181         return link;
00182 }
00183 
00184 
00185 /**
00186  * @brief insert element as last in list (used to maintain queue)
00187  * @param list list head.
00188  * @param newentry entry to be inserted into list
00189  */
00190 M_INLINE void DLIST_push_back( DLIST *list, DLIST_entry *newentry)
00191 {
00192   DLIST_insert_after( list, list->root.next, newentry );
00193 }
00194 
00195 /**
00196  * @brief insert element as first in list (used to maintain queue)
00197  * @param list (in) list head.
00198  * @param newentry (in) entry to be inserted into list
00199  */
00200 M_INLINE void DLIST_push_front( DLIST *list, DLIST_entry *newentry)
00201 {
00202   DLIST_insert_before( list, list->root.prev, newentry );
00203 }
00204 
00205 /**
00206  * @brief remove the first element from list (used to maintain double ended queue)
00207  * @param list (in) the object
00208  * @return the first element of list, NULL if list empty
00209  */
00210 M_INLINE DLIST_entry * DLIST_pop_front( DLIST *list )
00211 {
00212   DLIST_entry *ret;
00213 
00214   if (DLIST_isempty( list )) {
00215     return 0;
00216   }
00217   ret = list->root.prev;
00218   DLIST_unlink(list, ret);
00219   return ret;  
00220 }
00221 
00222 
00223 /**
00224  * @brief remove the last element from list (used to maintain double ended queue)
00225  * @param list (in) the object
00226  * @return the first element of list, NULL if list empty
00227  */
00228 M_INLINE DLIST_entry * DLIST_pop_back( DLIST *list)
00229 { 
00230   DLIST_entry *ret;
00231 
00232   if (DLIST_isempty( list )) {
00233     return 0;
00234   }
00235   ret = list->root.next;
00236   DLIST_unlink(list, ret);
00237   return ret;
00238 }
00239 
00240 
00241 M_INLINE DLIST_entry *DLIST_get_first(DLIST *list)
00242 {
00243   if (DLIST_isempty(list)) {
00244     return 0;
00245   }  
00246   return list->root.prev;
00247 }
00248 
00249 M_INLINE DLIST_entry *DLIST_get_last(DLIST *list)
00250 {
00251   if (DLIST_isempty(list)) {
00252     return 0;
00253   }
00254   return list->root.next;
00255 }
00256 
00257 M_INLINE DLIST_entry *DLIST_get_next( DLIST_entry *cur) 
00258 {
00259   return cur->next;
00260 }
00261 
00262 
00263 M_INLINE DLIST_entry *DLIST_get_prev( DLIST_entry *cur ) 
00264 {
00265   return cur->prev;
00266 }
00267 
00268 /**
00269  * @brief Macro for iterate over all elements of a list, the list is traversed in forward direction from first element to the last element.
00270  * @param loopvarname (type DLIST_entry *) pointer to the current element
00271  * @param list (type DLIST *) pointer to the list that is traversed
00272  */
00273 #define DLIST_FOREACH( loopvarname, list )\
00274   for((loopvarname) = (list)->root.prev;\
00275       (loopvarname) != 0;\
00276       (loopvarname) = (loopvarname)->next )
00277 
00278 /**
00279  * @brief Macro for iterate over all elements of a list, the list is traversed in reverse direction from last element to the first element.
00280  * @param loopvarname (type DLIST_entry *) pointer to the current element
00281  * @param list (type DLIST *) pointer to the list that is traversed
00282  */
00283 #define DLIST_FOREACH_REVERSE( loopvarname, list )\
00284   for((loopvarname) = (list)->root.next;\
00285       (loopvarname) != 0;\
00286       (loopvarname) = (loopvarname)->prev )
00287 
00288 /**
00289  * @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.
00290  * @param loopvarname (type DLIST_entry *) pointer to the current element
00291  * @param loopvarnamenext (type DLIST_entry *) do not modify! pointer to next element after current element (used for iteration).
00292  * @param list (type DLIST *) pointer to the list that is traversed
00293  */
00294 #define DLIST_FOREACH_SAVE( loopvarname, loopvarnext, list )\
00295   for((loopvarname) = (list)->root.prev, (loopvarnext) = (loopvarname) ? (loopvarname)->next : 0;\
00296       (loopvarname) != 0;\
00297       (loopvarname) = (loopvarnext), (loopvarnext) = (loopvarname) ? (loopvarname)->next : 0)
00298 
00299 /**
00300  * @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.
00301  * @param loopvarname (type DLIST_entry *) pointer to the current element
00302  * @param loopvarnamenext (type DLIST_entry *) do not modify! pointer to next element after current element (used for iteration).
00303  * @param list (type DLIST *) pointer to the list that is traversed
00304  */
00305 #define DLIST_FOREACH_REVERSE_SAVE( loopvarname, loopvarnext, list )\
00306   for((loopvarname) = (list)->root.next, (loopvarnext) = (loopvarname) ? (loopvarname)->prev : 0;\
00307       (loopvarname) != 0;\
00308       (loopvarname) = (loopvarnext), (loopvarnext) = (loopvarname) ? (loopvarname)->prev : 0)
00309 
00310 
00311 
00312 /*
00313  * @brief: return number of elements in list
00314  * if we don't have element count in list (DLIST_NO_ELMCOUNT defined), then the whole list structure is traversed.
00315  */
00316 M_INLINE size_t DLIST_size( DLIST *list )
00317 {
00318         return list->elmcount;
00319 }
00320 
00321 /*
00322  * @brief get the nth element of a list as counted from the beginning of the list.
00323  * @brief list (IN) the object
00324  * @brief nth  (IN) index of element to retrieve (null based).
00325  */
00326 M_INLINE DLIST_entry *DLIST_get_nth( DLIST *list, size_t nth)
00327 {
00328         DLIST_entry *elm;
00329 
00330         if (nth >= list->elmcount) {
00331                 return 0;
00332         }
00333 
00334         /* if less then half of count - iterate from start of list, otherwise from tail. */
00335         if (nth < (list->elmcount >> 1)) {
00336                 for(elm = DLIST_get_first(list); nth > 0; nth -= 1, elm = DLIST_get_next(elm)); 
00337         } else {
00338                 nth = list->elmcount - nth - 1;
00339                 for(elm = DLIST_get_last(list);  nth > 0; nth -= 1, elm = DLIST_get_prev(elm)); 
00340         }
00341 
00342         return elm;
00343 }
00344 
00345 /*
00346  * @brief get the nth element of a list as counted from the end of the list.
00347  * @brief list (IN) the object
00348  * @brief nth  (IN) index of element to retrieve (null based).
00349  */
00350 M_INLINE DLIST_entry *DLIST_get_nth_reverse( DLIST *list, size_t nth)
00351 {       
00352         return DLIST_get_nth( list, DLIST_size( list ) - nth );
00353 }
00354 
00355 /**
00356  * @brief iterate over all elements of a list, callback is invoked for either element of the list. list is traversed from first element to the last element.
00357  * @param lst (in) the object.
00358  * @param eval (in) callback that is called to visit each set (or cleared) bit.
00359  * @param context (in) pointer that is passed as context on each invocation of callback.
00360  * @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.
00361  */
00362 M_INLINE void DLIST_foreach( DLIST *lst, DLIST_VISITOR_V eval, void *context, int save_from_del)
00363 {
00364     DLIST_entry *cur, *next;
00365 
00366         if (!eval) {
00367           return;
00368         }
00369 
00370         if (save_from_del) {
00371                 DLIST_FOREACH_SAVE( cur, next, lst) {
00372                         eval( lst, cur, context );
00373                 }
00374         } else {
00375                 DLIST_FOREACH(  cur, lst ) {
00376                         eval( lst, cur, context );
00377                 }
00378         }               
00379 }
00380 
00381 /**
00382  * @brief iterate over all elements of a list, callback is invoked for either element of the list. list is traversed backword from last element to the first element.
00383  *
00384  * @param lst (in) the object.
00385  * @param eval (in) callback that is called to visit each set (or cleared) bit.
00386  * @param context (in) pointer that is passed as context on each invocation of callback.
00387  * @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.
00388  */
00389 M_INLINE void DLIST_foreach_reverse( DLIST *lst, DLIST_VISITOR_V eval, void *context, int save_from_delete)
00390 {
00391         DLIST_entry *cur, *next;
00392 
00393         if (!eval) {
00394           return ;
00395         }
00396 
00397         if ( save_from_delete ) {
00398                 DLIST_FOREACH_REVERSE_SAVE( cur, next, lst ) {
00399                         eval( lst, cur, context );
00400                 }
00401         } else {
00402                 DLIST_FOREACH_REVERSE( cur, lst ) {
00403                         eval( lst, cur, context );
00404                 }
00405         }
00406 }
00407 
00408 
00409 
00410 /**
00411  * @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.
00412  * 
00413  * @param lst (in) the object.
00414  * @param eval (in) callback that is called to visit each set (or cleared) bit.
00415  * @param context (in) pointer that is passed as context on each invocation of callback.
00416  * @param retval (out) optional - the first non zero value returned by eval callback, if NULL then ignored.
00417  * @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.
00418  * @return the list element that has matched (i.e. element that has been found).
00419  *
00420  */
00421 M_INLINE DLIST_entry * DLIST_findif( DLIST *lst, DLIST_VISITOR eval, void *context, int32_t *retval, int save_from_del)
00422 {
00423         int ret;
00424     DLIST_entry *cur, *next;
00425 
00426         if (retval) {
00427                 *retval = 0;
00428         }
00429 
00430         if (!eval) {
00431                 return 0;
00432         }
00433         
00434         if (save_from_del) {
00435                 DLIST_FOREACH_SAVE( cur, next, lst) {
00436                         ret = eval( lst, cur, context );
00437                         if (ret) {
00438                                 if (retval) {
00439                                         *retval = ret;
00440                                 }
00441                                 return cur;
00442                         }
00443                 }
00444         } else {
00445                 DLIST_FOREACH(  cur, lst ) {
00446                         ret = eval( lst, cur, context );
00447                         if (ret) {
00448                                 if (retval) {
00449                                         *retval = ret;
00450                                 }
00451                                 return cur;
00452                         }
00453                 }
00454         }               
00455 
00456         return 0;
00457 }
00458 
00459 
00460 
00461 /**
00462  * @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. 
00463  * 
00464  * @param lst (in) the object.
00465  * @param eval (in) callback that is called to visit each set (or cleared) bit.
00466  * @param context (in) pointer that is passed as context on each invocation of callback.
00467  * @param retval (out) optional - the first non zero value returned by eval callback, if NULL then ignored.
00468  * @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.
00469  * @return the list element that has matched (i.e. element that has been found).
00470  *
00471  */
00472 M_INLINE DLIST_entry * DLIST_findif_reverse( DLIST *lst, DLIST_VISITOR eval, void *context, int32_t *retval, int save_from_delete)
00473 {
00474         int ret;
00475         DLIST_entry *cur, *next;
00476 
00477         if (retval) {
00478                 *retval = 0;
00479         }
00480 
00481         if (!eval) {
00482           return 0;
00483         }
00484 
00485         if ( save_from_delete ) {
00486                 DLIST_FOREACH_REVERSE_SAVE( cur, next, lst ) {
00487 
00488                         ret = eval( lst, cur, context );
00489                         if (ret) {
00490                                 if (retval) {
00491                                         *retval = ret;
00492                                 }
00493                                 return cur;
00494                         }
00495                 }
00496         } else {
00497                 DLIST_FOREACH_REVERSE( cur, lst ) {
00498 
00499                         ret = eval( lst, cur, context );
00500                         if (ret) {
00501                                 if (retval) {
00502                                         *retval = ret;
00503                                 }                               
00504                                 return cur;
00505                         }
00506                 }
00507         }
00508 
00509         return 0;
00510 }
00511 
00512 
00513 /** 
00514  * @brief iterate over all entries of the list and delete entries that match predicate from the list, and frees the memory (optionally)
00515  * @param list (in) the object.
00516  * @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. 
00517  * @param context (in) data pointer passed to every invocation of check_if
00518  * @param free_ctx (in) interface used to free data of entry, is argument pointer is NULL then nothing is freed.
00519  * @param offset_of_link (in) offset of DLIST_entry in embedded structure.
00520  */
00521 M_INLINE void DLIST_deleteif( DLIST *list, DLIST_VISITOR check_if, void *context,  int offset_of_link)
00522 {
00523     DLIST_entry *cur,*next,*del;
00524 
00525     DLIST_FOREACH_SAVE(cur,next,list) {
00526                 if (!check_if || check_if( list, cur, context))  {
00527                         del = DLIST_unlink( list, cur );
00528                         if (offset_of_link != -1) {
00529                           free(  _MEMBEROF( del, offset_of_link ) );
00530                         }
00531                 }
00532         }
00533 }
00534 
00535 /** 
00536  * @brief iterate over all entries of the list and delete them.
00537  * @param list (in) the object
00538 
00539  * @param on_delete(in) if not NULL then this callback is called prior to deleting each list element.
00540  * @param context (in) if on_delete is not NULL then this pointer is passed as argument to on_delete.
00541  
00542  * @param free_ctx allocation interface used to free data of entry.
00543  * @param offset_of_link offset of DLIST_entry in embedded structure.
00544  */
00545 M_INLINE void DLIST_deleteall( DLIST *list, DLIST_VISITOR_V on_delete, void *context, int offset_of_link)
00546 {
00547     DLIST_entry *cur,*next,*del;
00548 
00549     DLIST_FOREACH_SAVE(cur,next,list) {
00550                 
00551                 if (on_delete)  {                       
00552                         on_delete( list, cur, context);
00553                 }
00554 
00555                 del = DLIST_unlink( list, cur );
00556                 if (offset_of_link != -1) {
00557                   free( _MEMBEROF(del,offset_of_link) );
00558                 }
00559         }
00560 }
00561 
00562 
00563 /**
00564  * @brief insert new element into sorted list; Maintains ordering relationship between elements of linked list (sort by ascending order)
00565  * 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.
00566  * @param list (in) the object
00567  * @param compare (in) comparison function.
00568  * @param newentry (in) element that is to be inserted into list.
00569  */
00570 M_INLINE void DLIST_insert_sorted( DLIST *list, DLIST_COMPARE compare, DLIST_entry *newentry) 
00571 {
00572         DLIST_entry *cur;
00573         
00574         DLIST_FOREACH(  cur, list ) {
00575                 if (compare(cur,newentry) > 0) {
00576 
00577                         DLIST_insert_before(list, cur,newentry);
00578                         return;
00579                 }
00580         }
00581 
00582         DLIST_push_back( list, newentry );
00583 }
00584 
00585 /**
00586  * @brief Reverse a list
00587  * This function turns the first element into the last element, the second element
00588  * into the one before the last, and so on and on.
00589  * @param header (in) the object
00590  */
00591 M_INLINE void DLIST_reverse( DLIST *lst )
00592 {
00593         DLIST_entry *cur = lst->root.prev, *next;
00594         DLIST_entry *tmp;
00595         if (cur) {
00596                 next = cur->next;
00597                 cur->next = 0;
00598 
00599                 while(next) {
00600                         tmp = next->next;
00601                         
00602                         next->next = cur;
00603                         cur->prev = next;
00604 
00605                         cur = next;
00606                         next = tmp;
00607                 }
00608 
00609                 tmp = lst->root.prev;
00610                 lst->root.prev = lst->root.next;
00611                 lst->root.next = tmp;
00612         }
00613 }
00614 
00615 /**
00616  * @brief check consistency of list
00617  * @param lst list header.
00618  * @return zero if list contains errors.
00619  */
00620 M_INLINE int DLIST_check(DLIST *header)
00621 {
00622         DLIST_entry *cur,*next,*prev = 0;
00623         size_t count = 0;
00624 
00625         if (header->root.prev && !header->root.next) {
00626                 return 0;
00627         }
00628 
00629         if (header->root.prev && header->root.prev->prev) {
00630                 return 0;
00631         }
00632 
00633         if (header->root.next && header->root.next->next) {
00634                 return 0;
00635         }
00636 
00637         DLIST_FOREACH( cur, header ) {
00638 
00639                 next = cur->next;
00640                 if (next && next->prev != cur) {                
00641                         return 0;
00642                 }
00643                 if (cur->prev != prev) {
00644                         return 0;
00645                 }
00646                 count += 1;
00647                 prev = cur;
00648         }
00649 
00650         if (header->root.next != prev) {
00651                 return 0;
00652         }
00653 
00654         if (header->elmcount != count) {
00655                 return 0;
00656         }
00657         return 1;
00658 }
00659 
00660 /**
00661  @}
00662  */
00663 
00664 #ifdef  __cplusplus
00665 }
00666 #endif
00667 
00668 #endif