Simple data structures / objects in plain C Snapshot
|
00001 /* Copyright (c) Michael Moser (2011) . 3-clause BSD License applies */ 00002 00003 #ifndef _VBUCKETHASH_H_ 00004 #define _VBUCKETHASH_H_ 00005 00006 #ifdef __cplusplus 00007 extern "C" { 00008 #endif 00009 00010 #include <cutils/sring.h> 00011 #include <cutils/hashfunction.h> 00012 00013 /** 00014 * @defgroup HASH_entry 00015 * @brief Entry in bucket hash table, add to structure as member in order to make structure storable in hash table. 00016 * Each hash table entry has to embed a VBUCKETHASH_Entry as part of its structure. 00017 * The user of this hash thing can please key/value wherever he likes. 00018 * @{ 00019 */ 00020 typedef struct { 00021 SRING entry; 00022 HASH_VALUE hash; 00023 } 00024 HASH_Entry; 00025 00026 00027 00028 /** 00029 * compute hash value 00030 */ 00031 typedef HASH_VALUE (*HASH_FUNCTION) (void *data, ssize_t length); 00032 00033 00034 /** 00035 * Compare argument key with hash key embedded in entry. 00036 */ 00037 typedef int (*HASH_COMPARE_KEY) (HASH_Entry *, void *key, ssize_t key_size); 00038 00039 /** 00040 * Visit a hash entry. 00041 */ 00042 typedef int (*HASH_VISITOR) (HASH_Entry *, void *context); 00043 00044 00045 /* 00046 * @} 00047 */ 00048 00049 /** 00050 * @defgroup HASH 00051 * @brief Hash table that is implemented as bucket hash table. 00052 00053 * Element of has is an user defined entry which embeds HASH_Entry structure. 00054 * 00055 * @{ 00056 */ 00057 typedef struct { 00058 00059 SRING *buckets; 00060 00061 int ismultimap; 00062 size_t buckets_size; 00063 size_t elmcount; 00064 size_t resize_threshold; 00065 00066 int mult_load_factor,div_load_factor; 00067 00068 00069 HASH_FUNCTION hash_func; 00070 HASH_COMPARE_KEY compare_key; 00071 } 00072 HASH; 00073 00074 00075 00076 00077 /** 00078 * @brief Object constructor; initialise the hash that allows one entry for a given key. 00079 00080 * @param hash (out) the object. 00081 * @param buckets (in) number of buckets - will be rounded to closes power of two (i.e. number of arrays of linked lists structures, each separate list holds elements with same hash number). 00082 * @param hash_func (in) pointer to hash function 00083 * @param compare (in) pointer to compare function; compares if two keys are equal, (called when hash value of two entries is equal) 00084 00085 * @returns 0 if ok 00086 */ 00087 int HASH_init( 00088 HASH *hash, 00089 00090 size_t buckets, 00091 int ismultimap, 00092 00093 HASH_COMPARE_KEY compare_key, 00094 HASH_FUNCTION hash_func 00095 ); 00096 00097 00098 00099 00100 00101 /** 00102 * @brief resize the hash table 00103 00104 * Change number of buckets in hash table. 00105 * The function pass over each element in the hash table and insert it into the resized table. 00106 00107 * @param hash (in out) the object 00108 * @param buckets (in) new number of buckets 00109 */ 00110 int HASH_resize(HASH *hash, size_t buckets); 00111 00112 /** 00113 * @brief Object destructor; frees a hash table. 00114 00115 * All elements currently contained in hash table will remain in memory. 00116 00117 * @param hash (in out) the object 00118 */ 00119 M_INLINE void HASH_free(HASH *hash) 00120 { 00121 if (hash->buckets) { 00122 free(hash->buckets); 00123 hash->buckets = 0; 00124 } 00125 } 00126 00127 00128 00129 /** 00130 * @brief returns number of elements in hash table 00131 */ 00132 M_INLINE size_t HASH_size( HASH *hash) 00133 { 00134 return hash->elmcount; 00135 } 00136 00137 00138 /** 00139 * @brief find entry with given key in hash table; for multimaps this will return the first occurence of the key. 00140 00141 * @param phash (in) the object 00142 * @param key (in) pointer to key 00143 * @param key_size (in) size of key 00144 00145 * @return pointer to entry if found; NULL if not found 00146 */ 00147 HASH_Entry *HASH_find( HASH *phash, void *key, ssize_t key_size ); 00148 00149 00150 /** 00151 * @brief for multimaps - find next occurence of key 00152 * 00153 * Usage to retrive all occurences of a key 00154 * 00155 * 00156 * HASH_Entry * entry; 00157 * for( entry = HASH_find(hash, key, sizeof(key) ) ; entry; 00158 * entry = HASH_find_next(hash, entry, key, sizeof(key) ) { 00159 * 00160 * } 00161 * 00162 * 00163 * @param phash (in) the object 00164 * @param prev (in) value returned by first invocation of VBUCKETHASH_find or previous invocation of VBUCKETHASH_find_next 00165 * @param key (in) pointer to key 00166 * @param key_size (in) size of key. If key is a null terminated string then pass VHASH_STRING as value. 00167 * @return pointer to entry if found; NULL if not found 00168 */ 00169 HASH_Entry *HASH_find_next( HASH *phash, HASH_Entry *prev, void *key, ssize_t key_size ); 00170 00171 /** 00172 * @brief insert new entry in hash table 00173 * @param hash (in) the object 00174 * @param key (in) key 00175 * @param size (in) size of key. If key is a null terminated string then pass VHASH_STRING as value. 00176 */ 00177 int HASH_insert( HASH *phash, HASH_Entry *entry, void *key, ssize_t key_size ); 00178 00179 /** 00180 * @brief find first hash table entry and unlink it from its bucket. The caller of this function has to free memory held by hash table entry. 00181 * @param hash 00182 * @param key 00183 * @param size 00184 * @returns if entry with given keys exists - pointer to hash table entry. 00185 */ 00186 HASH_Entry * HASH_unlink( HASH *hash, void *key, ssize_t key_size ); 00187 00188 00189 /* internal macro not directly used by user */ 00190 #define HASH_BUCKET_FOREACH(cur,bucket) \ 00191 for((cur) = (HASH_Entry *) ((bucket)->next);\ 00192 (SRING *) (cur) != (bucket);\ 00193 (cur) = (HASH_Entry *) (cur)->entry.next ) 00194 00195 00196 /** 00197 * @brief Macro: iterate over all elements that match a given key (multimap) 00198 * 00199 * Usage: 00200 * HASH_Entry *cur; 00201 * 00202 * HASH_FOREACH_KEY(cur,hash) { 00203 * =do something with cur - don't delete it= 00204 * } 00205 * 00206 * @param cur (type HASH_Entry *) current entry 00207 * @param hash (type HASH *) the object 00208 * @param key (type void *) the key 00209 * @param key_size (type size_t) length of key 00210 */ 00211 #define HASH_FOREACH_KEY(cur,hash,key,key_size) \ 00212 for( (cur) = HASH_find( (hash), (key), (key_size ) ) ; \ 00213 (cur); \ 00214 (cur) = HASH_find_next( (hash), (cur), (key), (key_size) ) ) 00215 00216 00217 /** 00218 * @brief Macro: unlink al entries that match a given key (for multimap); the user has to free the memory in loop code. 00219 00220 * @param cur (type HASH_Entry *) current entry 00221 * @param hash (type HASH *) the object 00222 * @param key (type void *) the key 00223 * @param key_size (type size_t) length of key 00224 00225 */ 00226 #define HASH_DELETEALL_KEY(cur,hash,key,key_size) \ 00227 { \ 00228 HASH_Entry * HASH_DELETEALL_KEY##next = (HASH_Entry *) 0xFFFFFFFF; \ 00229 for( (cur) = HASH_unlink( (hash), (key), (key_size ) ) ; \ 00230 (cur) && (HASH_DELETEALL_KEY##next); \ 00231 (HASH_DELETEALL_KEY##next) = HASH_find_next( (hash), (cur), (key), (key_size) ) ) { \ 00232 if ((HASH_DELETEALL_KEY##next) != (HASH_Entry *) 0xFFFFFFFF) { \ 00233 SRING_unlink_after( (SRING *) (cur) ); \ 00234 (hash)->elmcount--; \ 00235 (cur) = (HASH_DELETEALL_KEY##next); \ 00236 } 00237 00238 #define HASH_DELETEALL_KEY_END \ 00239 } \ 00240 } 00241 00242 00243 /** 00244 * @brief Macro: iterate over all elements in hash table 00245 * @param cur (type HASH_Entry *) 00246 * @param hash (type HASH *) 00247 */ 00248 #define HASH_FOREACH(cur,hash) \ 00249 { \ 00250 size_t HASH_foreach_i##cur = 0; \ 00251 for(HASH_foreach_i##cur = 0; HASH_foreach_i##cur < (hash)->buckets_size; HASH_foreach_i##cur ++) \ 00252 HASH_BUCKET_FOREACH( cur, &(hash)->buckets[ HASH_foreach_i##cur ] ) { 00253 00254 00255 00256 /** @brief Macro: close block opened by HASH_FOREACH 00257 */ 00258 #define HASH_FOREACH_END \ 00259 }\ 00260 } 00261 00262 /** 00263 * @brief Macro: iterate over all elements in hash table and delete them from the table; allow the user to free the memory of each element. 00264 * @param cur (type HASH_Entry *) 00265 * @param hash (type HASH *) 00266 */ 00267 #define HASH_DELETEALL(cur,hash) \ 00268 { \ 00269 size_t HASH_delallforeach_i##cur = 0; \ 00270 for(HASH_delallforeach_i##cur = 0; HASH_delallforeach_i##cur < (hash)->buckets_size; HASH_delallforeach_i##cur ++) \ 00271 while( !SRING_isempty( &(hash)->buckets[ HASH_delallforeach_i##cur ]) ) { \ 00272 (cur) = (HASH_Entry *) SRING_unlink_after( &(hash)->buckets[ HASH_delallforeach_i##cur ] );\ 00273 (hash)->elmcount--; 00274 00275 /** 00276 * @brief Macro: close block opened by HASH_DELETEALL 00277 */ 00278 #define HASH_DELETEALL_END \ 00279 }\ 00280 } 00281 00282 00283 /** 00284 * @brief iterate over all entries of the hash table that match a given key and invoke callback with those elements. 00285 * 00286 * @param hash (in) the object 00287 00288 * @param key (in) key 00289 * @param key_size (in) size of key 00290 00291 * @param eval_func (in) function invoked for every element in hash table. 00292 * @param context (in) data pointer passed to every invocation of eval_func 00293 */ 00294 00295 M_INLINE void HASH_foreach_key( HASH *hash, void *key, ssize_t key_size, HASH_VISITOR eval_func, void *context ) 00296 { 00297 HASH_Entry *cur; 00298 00299 if (!eval_func) { 00300 return; 00301 } 00302 HASH_FOREACH_KEY(cur,hash,key,key_size) { 00303 eval_func( cur, context); 00304 } 00305 } 00306 00307 00308 /** 00309 * @brief iterate over all entries of the hash table and invoke callback with each element. 00310 * Equivalent of Lisp foldl,mapcar and friends. 00311 * @param hash 00312 * @param eval_func function invoked for every element in hash table. 00313 * @param context data pointer passed to every invocation of eval_func 00314 */ 00315 M_INLINE void HASH_foreach( HASH *hash, HASH_VISITOR eval_func, void *context ) 00316 { 00317 HASH_Entry *cur; 00318 00319 if (!eval_func) { 00320 return; 00321 } 00322 00323 HASH_FOREACH(cur,hash) 00324 eval_func( cur, context); 00325 HASH_FOREACH_END 00326 } 00327 00328 00329 /** 00330 * @brief find an element within the hash. callback is invoked for each element of the list, when the callback returns non zero value the iteration stops as we have found what we searched for. 00331 * 00332 * @param hash (in) the object. 00333 * @param eval_func (in) callback that is called to visit each set (or cleared) bit. 00334 * @param context (in) pointer that is passed as context on each invocation of callback. 00335 * @param retval (out) optional - the first non zero value returned by eval callback, if NULL then ignored. 00336 * @return the list element that has matched (i.e. element that has been found). 00337 * 00338 */ 00339 M_INLINE HASH_Entry * HASH_findif( HASH *hash, HASH_VISITOR eval_func, void *context, int *retval) 00340 { 00341 size_t i; 00342 SRING *cur; 00343 int ret; 00344 00345 if (!eval_func) { 00346 return 0; 00347 } 00348 00349 for(i=0; i<hash->buckets_size; i++) { 00350 SRING_FOREACH( cur, &hash->buckets[i] ) { 00351 ret = eval_func( (HASH_Entry*) cur, context); 00352 if (ret) { 00353 if (retval) { 00354 *retval = ret; 00355 } 00356 return (HASH_Entry *) cur; 00357 } 00358 } 00359 } 00360 00361 return 0; 00362 } 00363 00364 /** 00365 * @brief iterate over all entries of the hash table and delete them. 00366 * @param hash (in) the object 00367 * @param on_delete (in) optional callback, call prior to freing object. 00368 * @param offset_of_link (in) offset of HASH_Entry in embedded structure. 00369 */ 00370 M_INLINE void HASH_deleteall( HASH *hash, int offset_of_link, HASH_VISITOR on_delete, void *context ) 00371 { 00372 HASH_Entry *cur; 00373 00374 HASH_DELETEALL(cur,hash); 00375 if (on_delete) { 00376 on_delete(cur,context); 00377 } 00378 free( _MEMBEROF(cur,offset_of_link) ); 00379 HASH_DELETEALL_END 00380 } 00381 00382 00383 /** 00384 * @brief check consistency of bucket hash structure. 00385 */ 00386 M_INLINE int HASH_check(HASH *hash) 00387 { 00388 size_t i,nbuck = hash->buckets_size; 00389 size_t count_of_elem = 0; 00390 HASH_Entry *cur; 00391 00392 for(i=0; i < nbuck; i++) { 00393 if (!SRING_check(hash->buckets + i)) { 00394 return 0; 00395 } 00396 } 00397 00398 HASH_FOREACH(cur,hash) 00399 count_of_elem++; 00400 HASH_FOREACH_END 00401 00402 if (count_of_elem != hash->elmcount) { 00403 return 0; 00404 } 00405 00406 return 1; 00407 } 00408 00409 /** 00410 @} 00411 */ 00412 00413 #ifdef __cplusplus 00414 } 00415 #endif 00416 00417 #endif