Simple data structures / objects in plain C Snapshot
|
00001 #ifndef _DLISTUNR_H_ 00002 #define _DLISTUNR_H_ 00003 00004 #ifdef __cplusplus 00005 extern "C" { 00006 #endif 00007 00008 #include <cutils/base.h> 00009 #include <cutils/dring.h> 00010 #include <stdlib.h> 00011 #include <string.h> 00012 00013 00014 typedef struct tagDLISTUNR_entry { 00015 DRING ring; 00016 size_t elmcount; 00017 uint8_t buffer[]; 00018 } DLISTUNR_entry; 00019 00020 00021 00022 /** 00023 * @defgroup DLISTUNR 00024 * @brief unrolled list where each list node contains an array of nodes. 00025 * Unlike other lists here, each element in a list is of the same size. 00026 00027 * an unrolled linked list is a variation on the linked list which stores multiple 00028 * elements in each node. It can drastically increase cache performance, 00029 * while decreasing the memory overhead associated with storing links to other list elements.. 00030 * 00031 * Each node holds up to a certain maximum number of elements, typically just large 00032 * enough so that the node fills a single cache line or a small multiple thereof. 00033 * A position in the list is indicated by both a reference to the node and a position 00034 * in the elements array. It's also possible to include a previous pointer for an unrolled 00035 * doubly-linked linked list. 00036 * Quoted from http://en.wikipedia.org/wiki/Unrolled_linked_list 00037 * 00038 * @{ 00039 */ 00040 typedef struct { 00041 00042 size_t elmsize; /* size of element */ 00043 size_t elmmaxcount; /* number of elements in one entry */ 00044 00045 size_t elmcount; /* size of elements in list (global) */ 00046 size_t entrycount; /* number of entries */ 00047 00048 DLISTUNR_entry root; 00049 00050 } 00051 DLISTUNR; 00052 00053 typedef void (*DLISTUNR_VISITOR_V)(DLISTUNR *list, void *entry, void *context); 00054 typedef int (*DLISTUNR_VISITOR) (DLISTUNR *list, void *entry, void *context); 00055 00056 /** 00057 * Current position of iteration through an unrolled linked list structure. 00058 */ 00059 typedef struct { 00060 DLISTUNR_entry *entry; 00061 size_t index; 00062 } DLISTUNR_position; 00063 00064 00065 /** 00066 * @brief initialises a new unrolled list. List entries are all of a fixed size. 00067 * @param ctx (in) allocator interface. (if null we are using the default allocator) 00068 * @param list (in) object that is initialised 00069 * @param elmsize (in) size of one element in list. 00070 * @param elmmaxcount (in) number of element stored in one list entry 00071 */ 00072 M_INLINE int DLISTUNR_init( DLISTUNR *list, size_t elmsize, size_t elmmaxcount) 00073 { 00074 list->elmmaxcount = elmmaxcount; 00075 list->elmsize = elmsize; 00076 00077 list->elmcount = list->entrycount = 0; 00078 00079 DRING_init( &list->root.ring ); 00080 list->root.elmcount = (size_t) -1; 00081 00082 return 0; 00083 } 00084 00085 /** 00086 * @brief check validity of unrolled list instance 00087 * @param list the object 00088 * @return 0 if list is invalid. 00089 */ 00090 M_INLINE int DLISTUNR_check(DLISTUNR *list) 00091 { 00092 size_t elmcount, size = 0; 00093 size_t cursize = 0; 00094 DRING *cur,*next; 00095 00096 if (list->root.elmcount != (size_t) -1) { 00097 return 0; 00098 } 00099 00100 DRING_FOREACH( cur, &list->root.ring) { 00101 00102 next = cur->next; 00103 if (!next || next->prev != cur) { 00104 return 0; 00105 } 00106 elmcount = ((DLISTUNR_entry *) cur)->elmcount; 00107 if ( elmcount > list->elmmaxcount) { 00108 return 0; 00109 } 00110 if (elmcount == 0) { 00111 return 0; 00112 } 00113 size++; 00114 cursize += elmcount; 00115 } 00116 00117 if (size != list->entrycount) { 00118 return 0; 00119 } 00120 if (cursize != list->elmcount) { 00121 return 0; 00122 } 00123 00124 return 1; 00125 } 00126 00127 00128 /** 00129 * @brief checks if argument list is empty 00130 * @param list pointer to list. 00131 * @returns not zero for non empty list. 00132 */ 00133 M_INLINE int DLISTUNR_isempty( DLISTUNR *list ) 00134 { 00135 return list->elmcount == 0; 00136 } 00137 00138 00139 M_INLINE size_t DLISTUNR_size( DLISTUNR *list) 00140 { 00141 return list->elmcount; 00142 } 00143 00144 00145 M_INLINE size_t DLISTUNR_maxsize( DLISTUNR *list ) 00146 { 00147 return list->elmmaxcount; 00148 } 00149 00150 /** 00151 * @brief Returns position structure of first element in unrolled linked list. 00152 * @param list (in) the object 00153 * @return position structure of first element 00154 */ 00155 M_INLINE DLISTUNR_position DLISTUNR_get_first( DLISTUNR *list ) 00156 { 00157 DLISTUNR_position ret; 00158 00159 ret.entry = (DLISTUNR_entry *) list->root.ring.next; 00160 ret.index = 0; 00161 00162 return ret; 00163 } 00164 00165 00166 /** 00167 * @brief Returns position structure of last element in unrolled linked list. 00168 * @param list (in) the object 00169 * @return position structure of first element 00170 */ 00171 M_INLINE DLISTUNR_position DLISTUNR_get_last( DLISTUNR *list ) 00172 { 00173 DLISTUNR_position ret; 00174 00175 if (list->elmcount) { 00176 ret.entry = (DLISTUNR_entry *) list->root.ring.prev; 00177 ret.index = ret.entry->elmcount - 1; 00178 } else { 00179 ret = DLISTUNR_get_first( list); 00180 } 00181 00182 return ret; 00183 } 00184 00185 /** 00186 * @brief Returns position to the next element in unrolled linked list. 00187 * @param list (in) the object 00188 * @param pos (in) iteration position in list. 00189 * @return position structure of the element that follows the element identified by pos structure. 00190 */ 00191 M_INLINE DLISTUNR_position DLISTUNR_next(DLISTUNR_position pos) 00192 { 00193 DLISTUNR_position ret; 00194 DLISTUNR_entry *entry; 00195 00196 ret = pos; 00197 entry = pos.entry; 00198 00199 ret.index++; 00200 if (ret.index >= entry->elmcount) { 00201 ret.entry = (DLISTUNR_entry *) entry->ring.next; 00202 ret.index = 0; 00203 } 00204 return ret; 00205 } 00206 00207 M_INLINE int DLISTUNR_is_eof(DLISTUNR *list, DLISTUNR_position pos) 00208 { 00209 return pos.entry == (DLISTUNR_entry *) &list->root; 00210 } 00211 00212 00213 /** 00214 * @brief Returns position to the previous element in unrolled linked list. 00215 * @param list (in) the object 00216 * @param pos (in) iteration position in list. 00217 * @return position structure of the element that precedes the element identified by pos structure. 00218 */ 00219 M_INLINE DLISTUNR_position DLISTUNR_prev(DLISTUNR_position pos) 00220 { 00221 DLISTUNR_position ret; 00222 DLISTUNR_entry *entry; 00223 00224 ret = pos; 00225 entry = pos.entry; 00226 if (ret.index) { 00227 ret.index--; 00228 } else { 00229 ret.entry = (DLISTUNR_entry *) entry->ring.prev; 00230 ret.index = 0; 00231 if (ret.entry && ret.entry->elmcount != (size_t) -1) { 00232 ret.index = ret.entry->elmcount - 1; 00233 } 00234 } 00235 return ret; 00236 } 00237 00238 00239 /** 00240 * @brief verify a position structure. 00241 */ 00242 M_INLINE int DLISTUNR_check_position( DLISTUNR_position pos) 00243 { 00244 if (!pos.entry) { 00245 return -1; 00246 } 00247 if (pos.index >= pos.entry->elmcount) { 00248 return -1; 00249 } 00250 return 0; 00251 } 00252 00253 /** 00254 * @brief copy list entry identified by position structure (pos) into user supplied memory area 00255 * @param list (in) the object 00256 * @param pos (in) position pointer 00257 * @param data (in|out) user supplied buffer 00258 * @param size (in) size of user supplied buffer (must be equal to the size of one element). 00259 * @return 0 on success -1 on failure 00260 */ 00261 M_INLINE int DLISTUNR_copy_at(DLISTUNR *list, DLISTUNR_position pos, void *data, size_t size) 00262 { 00263 void *ptr; 00264 if (size != list->elmsize) { 00265 return -1; 00266 } 00267 if (DLISTUNR_check_position(pos)) { 00268 return -1; 00269 } 00270 00271 ptr = pos.entry->buffer + pos.index * size; 00272 memcpy(data, ptr, size); 00273 00274 return 0; 00275 } 00276 00277 /** 00278 * @brief return pointer to list entry identified by position structure (pos). 00279 * @param list (in) the object 00280 * @param pos (in) position pointer 00281 * @return pointer to data entry in linked list. 00282 */ 00283 00284 M_INLINE uint8_t * DLISTUNR_at(DLISTUNR *list, DLISTUNR_position pos) 00285 { 00286 if (DLISTUNR_check_position(pos)) { 00287 return 0; 00288 } 00289 00290 return pos.entry->buffer + pos.index * list->elmsize; 00291 } 00292 00293 /** 00294 * @brief insert new entry after a given entry into this unrolled linked list 00295 * @param list (in) pointer to list head 00296 * @param pos (in) current position (newentry inserted after this one). 00297 * @param data (in) pointer to element that is to be inserted into this list. 00298 * @param size (in) size of area identified by data pointer. 00299 */ 00300 int DLISTUNR_insert_after(DLISTUNR *list, DLISTUNR_position pos, void *data, size_t size); 00301 00302 /** 00303 * @brief insert new entry before a given entry into this unrolled linked list 00304 * @param list (in) pointer to list head 00305 * @param pos (in) current position (newentry inserted before this one). 00306 * @param data (in) pointer to element that is to be inserted into this list. 00307 * @param size (in) size of area identified by data pointer. 00308 */ 00309 M_INLINE int DLISTUNR_insert_before(DLISTUNR *list, DLISTUNR_position pos, void *data, size_t size) 00310 { 00311 DLISTUNR_position insert_pos = DLISTUNR_prev(pos); 00312 00313 return DLISTUNR_insert_after( list, insert_pos, data, size); 00314 } 00315 00316 00317 /** 00318 * @brief delete an element from a unrolled list. 00319 * @param list (in) the object 00320 * @param pos (in) deletes element identified by this position structure 00321 * return -1 on failure 0 on success. 00322 */ 00323 int DLISTUNR_unlink(DLISTUNR *list, DLISTUNR_position pos); 00324 00325 00326 /** 00327 * @brief insert element as last in list (used to maintain queue) 00328 * @param list (in) the object 00329 * @param data (in) pointer to element that is to be inserted into this list. 00330 * @param size (in) size of area identified by data pointer. 00331 */ 00332 M_INLINE int DLISTUNR_push_back(DLISTUNR *list, void *data, size_t size) 00333 { 00334 DLISTUNR_position last = DLISTUNR_get_last( list ); 00335 return DLISTUNR_insert_after(list, last, data, size); 00336 00337 } 00338 00339 /** 00340 * @brief insert element as last in list (used to maintain queue) 00341 * @param list (in) the object 00342 * @param data (in) pointer to element that is to be inserted into this list. 00343 * @param size (in) size of area identified by data pointer. 00344 */ 00345 M_INLINE int DLISTUNR_push_front(DLISTUNR *list, void *data, size_t size) 00346 { 00347 DLISTUNR_position last = DLISTUNR_get_first( list ); 00348 return DLISTUNR_insert_before(list, last, data, size); 00349 } 00350 00351 00352 /** 00353 * @brief copy data of last element into user supplied buffer and remove the last element from list (used to maintain double ended queue) 00354 * @param list (in) the object 00355 * @param data (in) pointer to element that is to receive data of list entry 00356 * @param size (in) size of area identified by data pointer. 00357 * @return return 0 on success. 00358 */ 00359 M_INLINE int DLISTUNR_pop_back(DLISTUNR *list, void *data, size_t size) 00360 { 00361 DLISTUNR_position pos; 00362 00363 00364 if (DLISTUNR_isempty(list)) { 00365 return -1; 00366 } 00367 00368 pos = DLISTUNR_get_last( list ); 00369 00370 if (DLISTUNR_copy_at(list, pos, data, size)) { 00371 return -1; 00372 } 00373 00374 if (DLISTUNR_unlink(list,pos)) { 00375 return -1; 00376 } 00377 return 0; 00378 } 00379 00380 /** 00381 * @brief copy data of first element into user supplied buffer and remove the first element from list (used to maintain double ended queue) 00382 * @param list (in) the object 00383 * @param data (in) pointer to element that is to receive data of list entry 00384 * @param size (in) size of area identified by data pointer. 00385 * @return return 0 on success. 00386 */ 00387 M_INLINE int DLISTUNR_pop_front(DLISTUNR *list, void *data, size_t size) 00388 { 00389 DLISTUNR_position pos; 00390 00391 00392 if (DLISTUNR_isempty(list)) { 00393 return -1; 00394 } 00395 00396 pos = DLISTUNR_get_first( list ); 00397 00398 if (DLISTUNR_copy_at(list, pos, data, size)) { 00399 return -1; 00400 } 00401 if (DLISTUNR_unlink(list,pos)) { 00402 return -1; 00403 } 00404 return 0; 00405 } 00406 00407 /** 00408 * @brief Macro for iterate over all elements of a list, the list is traversed in forward direction from first element to the last element. 00409 * @param loopvarname (type DLISTUNR_position) struct that identifies current element 00410 * @param list (type DLISTUNR *) pointer to the list that is traversed 00411 */ 00412 #define DLISTUNR_FOREACH( loopvarname, list )\ 00413 for((loopvarname) = DLISTUNR_get_first( list );\ 00414 (loopvarname).entry != (DLISTUNR_entry *) &(list)->root;\ 00415 (loopvarname) = DLISTUNR_next( loopvarname ) ) 00416 00417 /** 00418 * @brief Macro for iterate over all elements of a list, the list is traversed in reverse direction from last element to the first element. 00419 * @param loopvarname (type DLISTUNR_position) pointer to the current element 00420 * @param list (type DLISTUNR *) pointer to the list that is traversed 00421 */ 00422 #define DLISTUNR_FOREACH_REVERSE( loopvarname, list )\ 00423 for((loopvarname) = DLISTUNR_get_last( list );\ 00424 (loopvarname).entry != (DLISTUNR_entry *) &(list)->root;\ 00425 (loopvarname) = DLISTUNR_prev( loopvarname ) ) 00426 00427 /** 00428 * @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. 00429 * @param loopvarname (type DLISTUNR_position) pointer to the current element 00430 * @param loopvarnamenext (type DLISTUNR_position) do not modify! pointer to next element after current element (used for iteration). 00431 * @param list (type DLISTUNR *) pointer to the list that is traversed 00432 */ 00433 #define DLISTUNR_FOREACH_SAVE( loopvarname, loopvarnext, list )\ 00434 for((loopvarname) = DLISTUNR_get_first( list ), (loopvarnext) = DLISTUNR_next( loopvarname );\ 00435 (loopvarname).entry != (DLISTUNR_entry *) &(list)->root;\ 00436 (loopvarname) = (loopvarnext), (loopvarnext) = DLISTUNR_next(loopvarname) ) 00437 00438 /** 00439 * @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. 00440 * @param loopvarname (type DLISTUNR_position) pointer to the current element 00441 * @param loopvarnamenext (type DLISTUNR_position) do not modify! pointer to next element after current element (used for iteration). 00442 * @param list (type DLISTUNR *) pointer to the list that is traversed 00443 */ 00444 #define DLISTUNR_FOREACH_REVERSE_SAVE( loopvarname, loopvarnext, list )\ 00445 for((loopvarname) = DLISTUNR_get_last(list);\ 00446 (loopvarname).entry != (DLISTUNR_entry *) &(list)->root;\ 00447 (loopvarname) = (loopvarnext), (loopvarnext) = DLISTUNR_prev(loopvarname) ) 00448 00449 00450 /** 00451 * @brief list free all entries of the list. The list will then be an empty list. 00452 */ 00453 M_INLINE void DLISTUNR_free( DLISTUNR *list, DLISTUNR_VISITOR_V free_func, void *context) 00454 { 00455 DRING *next,*cur; 00456 DLISTUNR_entry *curr; 00457 size_t pos,elmsize,i; 00458 00459 elmsize = list->elmsize; 00460 00461 DRING_FOREACH_SAVE( cur, next, &list->root.ring) { 00462 00463 curr = (DLISTUNR_entry *) cur; 00464 if (free_func) { 00465 for(pos = 0,i=0;i<curr->elmcount;i++) { 00466 free_func(list, (void *) (curr->buffer +pos), context); 00467 pos += elmsize; 00468 } 00469 } 00470 /*list->ctx->free( DRING_unlink(cur) );*/ 00471 free( cur ); 00472 00473 } 00474 DRING_init( &list->root.ring ); 00475 list->entrycount = 0; 00476 list->elmcount = 0; 00477 } 00478 00479 #if 0 00480 M_INLINE void DLISTUNR_deleteif( DLISTUNR *list, DLISTUNR_VISITOR free_func, void *context) 00481 { 00482 DRING *next,*cur; 00483 DLISTUNR_entry *curr; 00484 size_t pos,elmsize,i; 00485 00486 elmsize = list->elmsize; 00487 00488 DRING_FOREACH_SAVE( cur, next, &list->root.ring) { 00489 00490 curr = (DLISTUNR_entry *) cur; 00491 if (free_func) { 00492 for(pos = 0,i=0;i<curr->elmcount;i++) { 00493 free_func(list, (void *) curr->buffer[ pos ], context); 00494 pos += elmsize; 00495 } 00496 } 00497 /*list->ctx->free( DRING_unlink(cur) );*/ 00498 list->ctx->free( cur ); 00499 00500 } 00501 DRING_init( &list->root.ring ); 00502 list->entrycount = 0; 00503 list->elmcount = 0; 00504 } 00505 #endif 00506 00507 /* 00508 * @} 00509 */ 00510 #ifdef __cplusplus 00511 } 00512 #endif 00513 00514 #endif