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