Simple data structures / objects in plain C Snapshot
bhash.h
Go to the documentation of this file.
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