Simple data structures / objects in plain C Snapshot
|
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