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