Main Page | Modules | Class List | Directories | File List | Class Members | File Members | Related Pages

sfxhash.c

Go to the documentation of this file.
00001 /*!
00002  *
00003  *  \f sfxhash.c
00004  *
00005  *  A Customized hash table library for storing and accessing key + data pairs.
00006  *
00007  *  This table incorporates a memory manager (memcap.c) to provide a memory cap,  
00008  *  and an automatic node recovery system for out of memory management. Keys and
00009  *  Data are copied into the hash table during the add operation. The data may
00010  *  be allocated and free'd by the user (by setting the datasize to zero ). A 
00011  *  user callback is provided to allow the user to do cleanup whenever a node
00012  *  is released, by either the ANR system or the relase() function.
00013  *
00014  *  Users can and should delete nodes when they know they are not needed anymore,
00015  *  but this custom table is designed for the case where nodes are allocated 
00016  *  permanently, we have to limit memory, and we wish to recycle old nodes.  
00017  *  Many problems have a natural node ageing paradigm working in our favor, 
00018  *  so automated node aging makes sense. i.e. thresholding, tcp state.
00019  *
00020  *  This hash table maps keys to data.  All keys must be unique.
00021  *  Uniqueness is enforcedby the code.
00022  *
00023  *  Features:
00024  *
00025  *    1) Keys must be fixed length (per table) binary byte sequences.
00026  *         keys are copied during the add function
00027  *    2) Data must be fixed length (per table) binary byte sequences.
00028  *         data is copied during the add function - if datasize > 0
00029  *       Data may be managed by the user as well.
00030  *    3) Table row sizes can be automatically adjusted to
00031  *       the nearest prime number size during table initialization/creation.
00032  *    4) Memory management includes tracking the size of each allocation, 
00033  *       number of allocations, enforcing a memory cap, and automatic node 
00034  *       recovery - when  memory is low the oldest untouched node
00035  *       is unlinked and recycled for use as a new node.
00036  *
00037  *  Per Node Memory Usage:
00038  *  ----------------------
00039  *     SFXHASH_NODE bytes
00040  *     KEYSIZE bytes
00041  *     [DATASIZE bytes] if datasize > 0 during call to sfxhash_new.
00042  *
00043  *  The hash node memory (sfxhash_node,key,and data) is allocated with 
00044  *  one call to s_malloc/memcap_alloc.
00045  *
00046  *  Copyright (C) 2001 Marc A Norton.
00047  *  Copyright (C) 2003 Sourcefire,Inc.
00048  *
00049  *  2003-06-03: cmg - added sfxhash_{l,m}ru to return {least,most}
00050  *              recently used node from the global list
00051  *
00052  *              - added _anrcount function
00053  *              - changed count function to return unsigned to match structure
00054  *
00055  *  2003-06-11: cmg added
00056  *              overhead_bytes + blocks to separate out the
00057  *              memcap constraints from the hash table itself
00058  *              find success v fail
00059  *
00060  *  2003-06-19: cmg added
00061  *
00062  *              ability to set own hash function
00063  *              ability to set own key cmp function
00064  *
00065  *  2003-06-30: rdempster
00066  *              fixed bug in that would anr from the freelist
00067  *              
00068  *  2005-11-15: modified sfxhash_add to check if 'data' is zero before memcpy'ing.
00069  *              this allows user to pass null for data, and set up the data area
00070  *              themselves after the call - this is much more flexible.
00071  */
00072 #include <stdio.h>
00073 #include <stdlib.h>
00074 #include <string.h>
00075 
00076 #include "sfxhash.h"
00077 
00078 /*
00079  * Private Malloc - abstract the memory system
00080  */
00081 static 
00082 void * s_malloc( SFXHASH * t, int n )
00083 {
00084     return sfmemcap_alloc( &t->mc, n );
00085 }
00086 static 
00087 void s_free( SFXHASH * t, void * p )
00088 {
00089     sfmemcap_free( &t->mc, p );
00090 }
00091 
00092 /*
00093  *   User access to the memory management, do they need it ? WaitAndSee
00094  */
00095 void * sfxhash_alloc( SFXHASH * t, unsigned nbytes )
00096 {
00097     return s_malloc( t, nbytes );
00098 }
00099 void   sfxhash_free( SFXHASH * t, void * p )
00100 {
00101     s_free( t, p );
00102 }
00103 
00104 #ifdef XXXX
00105 /*
00106  *  A classic hash routine using prime numbers 
00107  *
00108  *  Constants for the Prime No. based hash routines  
00109  */
00110 #define PRIME_INIT   9791
00111 #define PRIME_SCALE  31
00112 static unsigned sfxhash_data( unsigned char *d, int n )
00113 {
00114     int i;
00115     register unsigned hash = PRIME_INIT;
00116     for(i=0;i<n;i++)
00117     {
00118         hash = hash * PRIME_SCALE + d[i];
00119     }  
00120     return hash;
00121 }
00122 
00123 #endif /* XXXX */
00124 
00125 /*
00126  *   Primiitive Prime number test, not very fast nor efficient, but should be ok for 
00127  *   hash table sizes of typical size.  NOT for real-time usage!
00128  */
00129 static int isPrime(int num )
00130 {
00131     int i;
00132     for(i=2;i<num;i++)
00133     {
00134         if( (num % i) == 0 ) break;//oops not prime, should have a remainder
00135     }
00136     if( i == num ) return 1;
00137     return 0;
00138 }
00139 /*
00140  *  Iterate number till we find a prime.
00141  */
00142 static int calcNextPrime(int num )
00143 {
00144     while( !isPrime( num ) ) num++;
00145 
00146     return num;
00147 }
00148 
00149 /*!
00150  *
00151  * Create a new hash table
00152  *
00153  * By default, this will "splay" nodes to the top of a free list. 
00154  *
00155  * @param nrows    number of rows in hash table
00156  * @param keysize  key size in bytes, same for all keys
00157  * @param datasize datasize in bytes, zero indicates user manages data
00158  * @param maxmem   maximum memory to use in bytes
00159  * @param anr_flag Automatic Node Recovery boolean flag
00160  * @param anrfree  users Automatic Node Recovery memory release function
00161  * @param usrfree  users standard memory release function
00162  *
00163  * @return SFXHASH*
00164  * @retval  0 out of memory
00165  * @retval !0 Valid SFXHASH pointer  
00166  *
00167  */
00168 /*
00169   Notes:
00170   if nrows < 0 don't cal the nearest prime.
00171   datasize must be the same for all entries, unless datasize is zero.
00172   maxmem of 0 indicates no memory limits.
00173 
00174 */
00175 SFXHASH * sfxhash_new( int nrows, int keysize, int datasize, int maxmem, 
00176                        int anr_flag, 
00177                        int (*anrfree)(void * key, void * data),
00178                        int (*usrfree)(void * key, void * data),
00179                        int recycle_flag )
00180 {
00181     int       i;
00182     SFXHASH * h;
00183 
00184     if( nrows > 0 ) /* make sure we have a prime number */
00185     {
00186         nrows = calcNextPrime( nrows );
00187     }
00188     else   /* use the magnitude or nrows as is */
00189     { 
00190         nrows = -nrows;
00191     }
00192 
00193     /* Allocate the table structure from general memory */
00194     h = (SFXHASH*) calloc( 1, sizeof(SFXHASH) );
00195     if( !h ) 
00196     {
00197         return 0;
00198     }
00199 
00200     /* this has a default hashing function */
00201     h->sfhashfcn = sfhashfcn_new( nrows );
00202     
00203     if( !h->sfhashfcn ) 
00204     {
00205         return 0;
00206     }
00207 
00208     sfmemcap_init( &h->mc, maxmem );
00209 
00210     /* Allocate the array of node ptrs */
00211     h->table = (SFXHASH_NODE**) s_malloc( h, sizeof(SFXHASH_NODE*) * nrows );
00212     if( !h->table ) 
00213     {
00214         return 0;
00215     }
00216 
00217     for( i=0; i<nrows; i++ )
00218     {
00219         h->table[i] = 0;
00220     }
00221 
00222     h->anrfree  = anrfree;
00223     h->usrfree  = usrfree;
00224     h->keysize  = keysize;
00225     h->datasize = datasize;
00226     h->nrows    = nrows;
00227     h->crow     = 0; 
00228     h->cnode    = 0; 
00229     h->count    = 0;
00230     h->ghead    = 0;
00231     h->gtail    = 0;
00232     h->anr_count= 0;    
00233     h->anr_tries= 0;
00234     h->anr_flag = anr_flag; 
00235     h->splay    = 1; 
00236     h->recycle_nodes = recycle_flag;
00237 
00238     h->find_success = 0;
00239     h->find_fail    = 0;
00240     
00241     /* save off how much we've already allocated from our memcap */    
00242     h->overhead_bytes = h->mc.memused;
00243     h->overhead_blocks = h->mc.nblocks;
00244 
00245     return h;
00246 }
00247 
00248 /*!
00249  *  Set Splay mode : Splays nodes to front of list on each access
00250  * 
00251  * @param t SFXHASH table pointer
00252  * @param n boolean flag toggles splaying of hash nodes
00253  *
00254  */
00255 void sfxhash_splaymode( SFXHASH * t, int n )
00256 {
00257     t->splay = n;
00258 }
00259 
00260 
00261 /*!
00262  *  Delete the hash Table 
00263  *
00264  *  free key's, free node's, and free the users data.
00265  *
00266  * @param h SFXHASH table pointer
00267  *
00268  */
00269 void sfxhash_delete( SFXHASH * h )
00270 {
00271     unsigned    i;
00272     SFXHASH_NODE * node, * onode;
00273 
00274     if( !h ) return;
00275 
00276      if( h->sfhashfcn ) sfhashfcn_free( h->sfhashfcn );
00277  
00278     if( h->table )
00279     {  
00280         for(i=0;i<h->nrows;i++)
00281         {
00282             for( node=h->table[i]; node;  )
00283             {
00284                 onode = node;
00285                 node  = node->next;
00286                 
00287                 /* Notify user that we are about to free this node function */
00288                 if( h->usrfree )
00289                     h->usrfree( onode->key, onode->data );
00290         
00291                 s_free( h,onode );
00292             }
00293         }
00294         s_free( h, h->table );
00295         h->table = 0;
00296     }
00297 
00298     free( h ); /* free the table from general memory */
00299 }
00300 
00301 /*!
00302  *  Get the # of Nodes in HASH the table
00303  *
00304  * @param t SFXHASH table pointer
00305  *
00306  */
00307 unsigned sfxhash_count( SFXHASH * t )
00308 {
00309     return t->count;
00310 }
00311 
00312 /*!
00313  *  Get the # auto recovery 
00314  *
00315  * @param t SFXHASH table pointer
00316  *
00317  */
00318 unsigned sfxhash_anr_count( SFXHASH * t )
00319 {
00320     return t->anr_count;
00321 }
00322 
00323 /*!
00324  *  Get the # finds
00325  *
00326  * @param t SFXHASH table pointer
00327  *
00328  */
00329 unsigned sfxhash_find_total( SFXHASH * t )
00330 {
00331     return t->find_success + t->find_fail;
00332 }
00333 
00334 /*!
00335  *  Get the # unsucessful finds
00336  *
00337  * @param t SFXHASH table pointer
00338  *
00339  */
00340 unsigned sfxhash_find_fail( SFXHASH * t )
00341 {
00342     return t->find_fail;
00343 }
00344 
00345 /*!
00346  *  Get the # sucessful finds
00347  *
00348  * @param t SFXHASH table pointer
00349  *
00350  */
00351 unsigned sfxhash_find_success( SFXHASH * t )
00352 {
00353     return t->find_success;
00354 }
00355 
00356 
00357 
00358 
00359 /*!
00360  *  Get the # of overhead bytes
00361  *
00362  * @param t SFXHASH table pointer
00363  *
00364  */
00365 unsigned sfxhash_overhead_bytes( SFXHASH * t )
00366 {
00367     return t->overhead_bytes;
00368 }
00369 
00370 /*!
00371  *  Get the # of overhead blocks
00372  *
00373  * @param t SFXHASH table pointer
00374  *
00375  */
00376 unsigned sfxhash_overhead_blocks( SFXHASH * t )
00377 {
00378     return t->overhead_blocks;
00379 }
00380 
00381 /*
00382  *  Free List - uses the NODE gnext/gprev fields
00383  */
00384 static
00385 void sfxhash_save_free_node( SFXHASH *t, SFXHASH_NODE * hnode )
00386 {
00387     /* Add A Node to the Free Node List */
00388     if( t->fhead ) /* add the node to head of the the existing list */
00389     {
00390         hnode->gprev    = 0;  
00391         hnode->gnext    = t->fhead;
00392         t->fhead->gprev = hnode;
00393         t->fhead        = hnode;
00394         /* tail is not affected */
00395     }
00396     else /* 1st node in this list */
00397     {
00398         hnode->gprev = 0;
00399         hnode->gnext = 0;
00400         t->fhead    = hnode;
00401         t->ftail    = hnode;
00402     }
00403 }
00404 static
00405 SFXHASH_NODE * sfxhash_get_free_node( SFXHASH *t )
00406 {
00407     SFXHASH_NODE * node = t->fhead;
00408 
00409     /* Remove A Node from the Free Node List - remove the head node */
00410     if( t->fhead  ) 
00411     {
00412         t->fhead = t->fhead->gnext;
00413         if( t->fhead ) 
00414             t->fhead->gprev = 0;
00415 
00416         if( t->ftail  == node ) /* no more nodes - clear the tail */
00417             t->ftail  =  0;
00418     }
00419 
00420     return node;
00421 }
00422 
00423 static
00424 void sfxhash_glink_node( SFXHASH *t, SFXHASH_NODE * hnode )
00425 {
00426     /* Add The Node */
00427     if( t->ghead ) /* add the node to head of the the existing list */
00428     {
00429         hnode->gprev    = 0;  
00430         hnode->gnext    = t->ghead;
00431         t->ghead->gprev = hnode;
00432         t->ghead        = hnode;
00433         /* tail is not affected */
00434     }
00435     else /* 1st node in this list */
00436     {
00437         hnode->gprev = 0;
00438         hnode->gnext = 0;
00439         t->ghead    = hnode;
00440         t->gtail    = hnode;
00441     }
00442 }
00443 
00444 static
00445 void sfxhash_gunlink_node( SFXHASH *t, SFXHASH_NODE * hnode )
00446 {
00447     /* Remove the Head Node */
00448     if( t->ghead == hnode ) /* add the node to head of the the existing list */
00449     {
00450         t->ghead = t->ghead->gnext;
00451         if( t->ghead ) 
00452             t->ghead->gprev = 0;
00453     }
00454 
00455     if( hnode->gprev ) hnode->gprev->gnext = hnode->gnext;
00456     if( hnode->gnext ) hnode->gnext->gprev = hnode->gprev;
00457 
00458     if( t->gtail  == hnode )
00459         t->gtail  =  hnode->gprev;             
00460 }
00461 
00462 void sfxhash_gmovetofront( SFXHASH *t, SFXHASH_NODE * hnode )
00463 {
00464     if( hnode != t->ghead )
00465     {
00466         sfxhash_gunlink_node( t, hnode );
00467         sfxhash_glink_node( t, hnode );
00468     }
00469 }
00470 
00471 /*
00472  *
00473  */
00474 static
00475 void sfxhash_link_node( SFXHASH * t, SFXHASH_NODE * hnode )
00476 {
00477     /* Add The Node to the Hash Table Row List */
00478     if( t->table[hnode->rindex] ) /* add the node to the existing list */
00479     {
00480         hnode->prev = 0;  // insert node as head node
00481         hnode->next=t->table[hnode->rindex];
00482         t->table[hnode->rindex]->prev = hnode;
00483         t->table[hnode->rindex] = hnode;
00484     }
00485     else /* 1st node in this list */
00486     {
00487         hnode->prev=0;
00488         hnode->next=0;
00489         t->table[hnode->rindex] = hnode;
00490     }
00491 }
00492 
00493 static
00494 void sfxhash_unlink_node( SFXHASH * t, SFXHASH_NODE * hnode )
00495 {
00496     if( hnode->prev )  // definitely not the 1st node in the list
00497     {
00498         hnode->prev->next = hnode->next;
00499         if( hnode->next ) 
00500             hnode->next->prev = hnode->prev;
00501     }
00502     else if( t->table[hnode->rindex] )  // must be the 1st node in the list
00503     {
00504         t->table[hnode->rindex] = t->table[hnode->rindex]->next;
00505         if( t->table[hnode->rindex] )
00506             t->table[hnode->rindex]->prev = 0;
00507     }
00508 }
00509 
00510 /*
00511  *  move a node to the front of the row list at row = 'index'
00512  */
00513 static void movetofront( SFXHASH *t, SFXHASH_NODE * n )
00514 {
00515     /* Modify Hash Node Row List */
00516     if( t->table[n->rindex] != n ) // if not at front of list already...
00517     {
00518         /* Unlink the node */
00519         sfxhash_unlink_node( t, n );
00520      
00521         /* Link at front of list */
00522         sfxhash_link_node( t, n );
00523     }
00524 
00525     /* Move node in the global hash node list to the front */
00526     sfxhash_gmovetofront( t, n );
00527 }
00528 
00529 /*
00530  * Allocat a new hash node, uses Auto Node Recovery if needed and enabled.
00531  * 
00532  * The oldest node is the one with the longest time since it was last touched, 
00533  * and does not have any direct indication of how long the node has been around.
00534  * We don't monitor the actual time since last being touched, instead we use a
00535  * splayed global list of node pointers. As nodes are accessed they are splayed
00536  * to the front of the list. The oldest node is just the tail node.
00537  *
00538  */
00539 static 
00540 SFXHASH_NODE * sfxhash_newnode( SFXHASH * t )
00541 {
00542     SFXHASH_NODE * hnode;
00543 
00544     /* Recycle Old Nodes - if any */
00545     hnode = sfxhash_get_free_node( t );
00546 
00547     /* Allocate memory for a node */
00548     if( ! hnode )
00549     {
00550         hnode = (SFXHASH_NODE*)s_malloc( t, sizeof(SFXHASH_NODE) +
00551                                          t->keysize + t->datasize );
00552     }
00553         
00554     /*  If we still haven't found hnode, we're at our memory limit.
00555      *
00556      *  Uses Automatic Node Recovery, to recycle the oldest node-based on access
00557      *  (Unlink and reuse the tail node)
00558      */ 
00559     if( !hnode && t->anr_flag && t->gtail )
00560     {
00561         /* Find the oldes node the users willing to let go. */
00562         for(hnode = t->gtail; hnode; hnode = hnode->gprev )
00563         {
00564             if( t->anrfree ) /* User has provided a permission+release callback function */
00565             {
00566                 t->anr_tries++;/* Count # ANR requests */
00567                 
00568                 /* Ask the user for permission to release this node, but let them say no! */
00569                 if( t->anrfree( hnode->key, hnode->data ) )
00570                 {
00571                     /* NO, don't recycle this node, user's not ready to let it go. */                            
00572                     continue;
00573                 }
00574                 
00575                 /* YES, user said we can recycle this node */
00576             }
00577 
00578             sfxhash_gunlink_node( t, hnode ); /* unlink from the global list */
00579             sfxhash_unlink_node( t, hnode ); /* unlink from the row list */
00580             t->count--;
00581             t->anr_count++; /* count # of ANR operations */
00582             break;
00583         }
00584     }
00585 
00586     /* either we are returning a node or we're all full and the user
00587      * won't let us allocate anymore and we return NULL */
00588     return hnode;
00589 }
00590 
00591 /*
00592  *
00593  *  Find a Node based on the key, return the node and the index.
00594  *  The index is valid even if the return value is NULL, in which
00595  *  case the index is the corect row in which the node should be 
00596  *  created.
00597  *
00598  */
00599 static 
00600 SFXHASH_NODE * sfxhash_find_node_row( SFXHASH * t, void * key, int * rindex )
00601 {
00602     unsigned       hashkey;
00603     int            index;
00604     SFXHASH_NODE  *hnode;
00605 
00606     hashkey = t->sfhashfcn->hash_fcn( t->sfhashfcn,
00607                                       (unsigned char*)key,
00608                                       t->keysize );
00609     
00610 /*     printf("hashkey: %u t->keysize: %d\n", hashkey, t->keysize); */
00611 /*     flowkey_fprint(stdout, key);  */
00612 /*     printf("****\n"); */
00613 
00614     index   = hashkey % t->nrows;
00615 
00616     *rindex = index;
00617    
00618     for( hnode=t->table[index]; hnode; hnode=hnode->next )
00619     {
00620         if( !t->sfhashfcn->keycmp_fcn(hnode->key,key,t->keysize) )
00621         {
00622             if( t->splay > 0 )
00623                 movetofront(t,hnode);
00624 
00625             t->find_success++;            
00626             return hnode;
00627         }
00628     }
00629 
00630     t->find_fail++;            
00631     return NULL;
00632 }
00633 
00634 
00635 
00636 /*!
00637  * Add a key + data pair to the hash table
00638  *
00639  * 2003-06-06:
00640  *  - unique_tracker.c assumes that this splays
00641  *    nodes to the top when they are added.
00642  *
00643  *    This is done because of the successful find.
00644  *
00645  * @param t SFXHASH table pointer
00646  * @param key  users key pointer
00647  * @param data  users data pointer
00648  *
00649  * @return integer
00650  * @retval SFXHASH_OK      success
00651  * @retval SFXHASH_INTABLE already in the table, t->cnode points to the node
00652  * @retval SFXHASH_NOMEM   not enough memory
00653  */
00654 int sfxhash_add( SFXHASH * t, void * key, void * data )
00655 {
00656     int            index;
00657     SFXHASH_NODE * hnode;
00658 
00659     /* Enforce uniqueness: Check for the key in the table */
00660     hnode = sfxhash_find_node_row( t, key, &index );
00661 
00662     if( hnode )
00663     {
00664         t->cnode = hnode;
00665 
00666         return SFXHASH_INTABLE; /* found it - return it. */
00667     }
00668 
00669     /* 
00670      *  Alloc new hash node - allocate key space and data space at the same time.
00671      */
00672     hnode = sfxhash_newnode( t );
00673     if( !hnode )
00674     {
00675         return SFXHASH_NOMEM;
00676     }
00677 
00678     /* Set up the new key pointer */
00679     hnode->key = (char*)hnode + sizeof(SFXHASH_NODE);
00680 
00681     /* Copy the key */
00682     memcpy(hnode->key,key,t->keysize);
00683 
00684     /* Save our table row index */
00685     hnode->rindex = index;
00686 
00687     /* Copy the users data - or if datasize is zero set ptr to users data */
00688     if( t->datasize )
00689     {
00690         /* Set up the new data pointer */
00691         hnode->data= (char*)hnode + sizeof(SFXHASH_NODE) + t->keysize;
00692 
00693         if(data)
00694         {
00695            memcpy(hnode->data,data,t->datasize);
00696         }
00697     }
00698     else 
00699     {
00700         hnode->data = data;
00701     }
00702     
00703     /* Link the node into the table row list */
00704     sfxhash_link_node ( t, hnode );
00705 
00706     /* Link at the front of the global node list */
00707     sfxhash_glink_node( t, hnode );
00708 
00709     /* Track # active nodes */
00710     t->count++;
00711 
00712     return SFXHASH_OK;
00713 }
00714 
00715 
00716 /*!
00717  * Add a key to the hash table, return the hash node
00718  *
00719  * 2003-06-06:
00720  *  - unique_tracker.c assumes that this splays
00721  *    nodes to the top when they are added.
00722  *
00723  *    This is done because of the successful find.
00724  *
00725  * @param t SFXHASH table pointer
00726  * @param key  users key pointer
00727  *
00728  * @return integer
00729  * @retval SFXHASH_OK      success
00730  * @retval SFXHASH_INTABLE already in the table, t->cnode points to the node
00731  * @retval SFXHASH_NOMEM   not enough memory
00732  */
00733 SFXHASH_NODE * sfxhash_get_node( SFXHASH * t, void * key )
00734 {
00735     int            index;
00736     SFXHASH_NODE * hnode;
00737 
00738     /* Enforce uniqueness: Check for the key in the table */
00739     hnode = sfxhash_find_node_row( t, key, &index );
00740 
00741     if( hnode )
00742     {
00743         t->cnode = hnode;
00744 
00745         return hnode; /* found it - return it. */
00746     }
00747 
00748     /* 
00749      *  Alloc new hash node - allocate key space and data space at the same time.
00750      */
00751     hnode = sfxhash_newnode( t );
00752     if( !hnode )
00753     {
00754         return NULL;
00755     }
00756 
00757     /* Set up the new key pointer */
00758     hnode->key = (char*)hnode + sizeof(SFXHASH_NODE);
00759 
00760     /* Copy the key */
00761     memcpy(hnode->key,key,t->keysize);
00762 
00763     /* Save our table row index */
00764     hnode->rindex = index;
00765 
00766     /* Copy the users data - or if datasize is zero set ptr to users data */
00767     if( t->datasize )
00768     {
00769         /* Set up the new data pointer */
00770         hnode->data= (char*)hnode + sizeof(SFXHASH_NODE) + t->keysize;
00771     }
00772     else 
00773     {
00774         hnode->data = NULL;
00775     }
00776     
00777     /* Link the node into the table row list */
00778     sfxhash_link_node ( t, hnode );
00779 
00780     /* Link at the front of the global node list */
00781     sfxhash_glink_node( t, hnode );
00782 
00783     /* Track # active nodes */
00784     t->count++;
00785 
00786     return hnode;
00787 }
00788 
00789 
00790 /*!
00791  * Find a Node based on the key
00792  *
00793  * @param t SFXHASH table pointer
00794  * @param key  users key pointer
00795  *
00796  * @return SFXHASH_NODE*   valid pointer to the hash node
00797  * @retval 0               node not found
00798  *
00799  */
00800 SFXHASH_NODE * sfxhash_find_node( SFXHASH * t, void * key)
00801 {
00802     int            rindex;
00803 
00804     return sfxhash_find_node_row( t, key, &rindex );
00805 }
00806 
00807 /*!
00808  * Find the users data based associated with the key
00809  *
00810  * @param t SFXHASH table pointer
00811  * @param key  users key pointer
00812  *
00813  * @return void*   valid pointer to the users data
00814  * @retval 0       node not found
00815  *
00816  */
00817 void * sfxhash_find( SFXHASH * t, void * key)
00818 {
00819     SFXHASH_NODE * hnode;
00820     int            rindex;
00821 
00822     hnode = sfxhash_find_node_row( t, key, &rindex );
00823 
00824     if( hnode ) return hnode->data;
00825 
00826     return NULL;
00827 }
00828 
00829 
00830 /** 
00831  * Get the HEAD of the in use list
00832  * 
00833  * @param t table pointer 
00834  * 
00835  * @return the head of the list or NULL
00836  */
00837 SFXHASH_NODE *sfxhash_ghead( SFXHASH * t )
00838 {
00839     if(t)
00840     {
00841         return t->ghead;
00842     }
00843 
00844     return NULL;
00845 }
00846 
00847 
00848 /** 
00849  * Walk the global list
00850  * 
00851  * @param n current node
00852  * 
00853  * @return the next node in the list or NULL when at the end
00854  */
00855 SFXHASH_NODE *sfxhash_gnext( SFXHASH_NODE *n )
00856 {
00857     if(n)
00858     {
00859         return n->gnext;
00860     }
00861 
00862     return NULL;
00863 }
00864 
00865 
00866 /*!
00867  * Return the most recently used data from the global list
00868  *
00869  * @param t SFXHASH table pointer
00870  *
00871  * @return void*   valid pointer to the users data
00872  * @retval 0       node not found
00873  *
00874  */
00875 void * sfxhash_mru( SFXHASH * t )
00876 {
00877     SFXHASH_NODE * hnode;
00878 
00879     hnode = sfxhash_ghead(t);
00880 
00881     if( hnode )
00882         return hnode->data;
00883         
00884     return NULL;
00885 }
00886 
00887 /*!
00888  * Return the least recently used data from the global list
00889  *
00890  * @param t SFXHASH table pointer
00891  *
00892  * @return void*   valid pointer to the users data
00893  * @retval 0       node not found
00894  *
00895  */
00896 void * sfxhash_lru( SFXHASH * t )
00897 {
00898     SFXHASH_NODE * hnode;
00899 
00900     hnode = t->gtail;
00901 
00902     if( hnode ) return hnode->data;
00903 
00904     return NULL;
00905 }
00906 
00907 /*!
00908  * Return the most recently used node from the global list
00909  *
00910  * @param t SFXHASH table pointer
00911  *
00912  * @return SFXHASH_NODE*   valid pointer to a node
00913  * @retval 0       node not found
00914  *
00915  */
00916 SFXHASH_NODE * sfxhash_mru_node( SFXHASH * t )
00917 {
00918     SFXHASH_NODE * hnode;
00919 
00920     hnode = sfxhash_ghead(t);
00921 
00922     if( hnode )
00923         return hnode;
00924         
00925     return NULL;
00926 }
00927 
00928 /*!
00929  * Return the least recently used node from the global list
00930  *
00931  * @param t SFXHASH table pointer
00932  *
00933  * @return SFXHASH_NODE*   valid pointer to a node
00934  * @retval 0       node not found
00935  *
00936  */
00937 SFXHASH_NODE * sfxhash_lru_node( SFXHASH * t )
00938 {
00939     SFXHASH_NODE * hnode;
00940 
00941     hnode = t->gtail;
00942 
00943     if( hnode ) return hnode;
00944 
00945     return NULL;
00946 }
00947 
00948 /*!
00949  * Get some hash table statistics. NOT FOR REAL TIME USE.
00950  *
00951  * 
00952  * @param t SFXHASH table pointer
00953  * @param filled how many 
00954  *
00955  * @return max depth of the table
00956  *
00957  */
00958 unsigned sfxhash_maxdepth( SFXHASH * t )
00959 {
00960     unsigned i;
00961     unsigned max_depth = 0;
00962 
00963     SFXHASH_NODE * hnode;
00964 
00965     for( i=0; i<t->nrows; i++ )
00966     {
00967         unsigned cur_depth = 0;
00968 
00969         for(hnode = t->table[i]; hnode != NULL; hnode = hnode->next)
00970         {
00971             cur_depth++;
00972         }
00973 
00974         if(cur_depth > max_depth)
00975             max_depth = cur_depth;
00976     }
00977 
00978     return max_depth;
00979 }
00980 
00981 /*
00982  *  Unlink and free the node
00983  */
00984 int sfxhash_free_node( SFXHASH * t, SFXHASH_NODE * hnode)
00985 {
00986     sfxhash_unlink_node( t, hnode ); /* unlink from the hash table row list */
00987 
00988     sfxhash_gunlink_node( t, hnode ); /* unlink from global-hash-node list */
00989 
00990     t->count--;
00991 
00992     if( t->usrfree )
00993     {
00994         t->usrfree( hnode->key, hnode->data );
00995     }
00996 
00997     if( t->recycle_nodes )
00998     {
00999         sfxhash_save_free_node( t, hnode );
01000     }
01001     else
01002     {
01003         s_free( t, hnode );
01004     }
01005 
01006     return SFXHASH_OK;
01007 }
01008 
01009 /*!
01010  * Remove a Key + Data Pair from the table.
01011  *
01012  * @param t SFXHASH table pointer
01013  * @param key  users key pointer
01014  *
01015  * @return 0   success
01016  * @retval !0  failed
01017  *
01018  */
01019 int sfxhash_remove( SFXHASH * t, void * key)
01020 {
01021     SFXHASH_NODE * hnode;
01022     unsigned hashkey, index;
01023 
01024     hashkey = t->sfhashfcn->hash_fcn( t->sfhashfcn,
01025                                       (unsigned char*)key,
01026                                       t->keysize );
01027     
01028     index = hashkey % t->nrows;
01029 
01030     hnode = t->table[index];
01031     
01032     for( hnode=t->table[index]; hnode; hnode=hnode->next )
01033     {
01034         if( !t->sfhashfcn->keycmp_fcn(hnode->key,key,t->keysize) )
01035         {
01036             return sfxhash_free_node( t, hnode );
01037         }
01038     }
01039 
01040     return SFXHASH_ERR;  
01041 }
01042 
01043 /* 
01044    Internal use only 
01045 */
01046 static 
01047 void sfxhash_next( SFXHASH * t )
01048 {
01049     if( !t->cnode )
01050         return ;
01051  
01052     /* Next node in current node list */
01053     t->cnode = t->cnode->next;
01054     if( t->cnode )
01055     {
01056         return;
01057     }
01058 
01059     /* Next row */ 
01060     /* Get 1st node in next non-emtoy row/node list */
01061     for( t->crow++; t->crow < t->nrows; t->crow++ )
01062     {    
01063         t->cnode = t->table[ t->crow ];
01064         if( t->cnode ) 
01065         {
01066             return;
01067         }
01068     }
01069 }
01070 /*!
01071  * Find and return the first hash table node
01072  *
01073  * @param t SFXHASH table pointer
01074  *
01075  * @return 0   failed
01076  * @retval !0  valid SFXHASH_NODE *
01077  *
01078  */
01079 SFXHASH_NODE * sfxhash_findfirst( SFXHASH * t )
01080 {
01081     SFXHASH_NODE * n;
01082 
01083     /* Start with 1st row */
01084     for( t->crow=0; t->crow < t->nrows; t->crow++ )
01085     {    
01086         /* Get 1st Non-Null node in row list */
01087         t->cnode = t->table[ t->crow ];
01088         if( t->cnode )
01089         {
01090             n = t->cnode;
01091             sfxhash_next( t ); // load t->cnode with the next entry
01092             return n;
01093         }
01094     }
01095     
01096     return NULL;
01097 }
01098 
01099 /*!
01100  * Find and return the next hash table node
01101  *
01102  * @param t SFXHASH table pointer
01103  *
01104  * @return 0   failed
01105  * @retval !0  valid SFXHASH_NODE *
01106  *
01107  */
01108 SFXHASH_NODE * sfxhash_findnext( SFXHASH * t )
01109 {
01110     SFXHASH_NODE * n;
01111 
01112     n = t->cnode;
01113     if( !n ) /* Done, no more entries */
01114     {
01115         return NULL;
01116     }
01117 
01118     /*
01119       Preload next node into current node 
01120     */
01121     sfxhash_next( t ); 
01122 
01123     return  n;
01124 }
01125 
01126 
01127 /** 
01128  * Make sfhashfcn use a separate set of operators for the backend.
01129  *
01130  * @param h sfhashfcn ptr
01131  * @param hash_fcn user specified hash function
01132  * @param keycmp_fcn user specified key comparisoin function
01133  */
01134 
01135 int sfxhash_set_keyops( SFXHASH *h ,
01136                         unsigned (*hash_fcn)( SFHASHFCN * p,
01137                                               unsigned char *d,
01138                                               int n),
01139                         int (*keycmp_fcn)( const void *s1,
01140                                            const void *s2,
01141                                            size_t n))
01142 {
01143     if(h && hash_fcn && keycmp_fcn)
01144     {
01145         return sfhashfcn_set_keyops(h->sfhashfcn, hash_fcn, keycmp_fcn);
01146     }
01147 
01148     return -1;
01149 }
01150 
01151 
01152 /*
01153  * -----------------------------------------------------------------------------------------
01154  *   Test Driver for Hashing
01155  * -----------------------------------------------------------------------------------------
01156  */
01157 #ifdef SFXHASH_MAIN 
01158 
01159 
01160 /* 
01161    This is called when the user releases a node or kills the table 
01162 */
01163 int usrfree( void * key, void * data )
01164 {
01165 
01166     /* Release any data you need to */
01167     return 0;  
01168 }
01169 
01170 /* 
01171    Auto Node Recovery Callback - optional 
01172 
01173    This is called to ask the user to kill a node, if it reutrns !0 than the hash
01174    library does not kill this node.  If the user os willing to let the node die,
01175    the user must do any free'ing or clean up on the node during this call.
01176 */
01177 int anrfree( void * key, void * data )
01178 {
01179     static int bx = 0;
01180 
01181     /* Decide if we can free this node. */
01182 
01183     //bx++; if(bx == 4 )bx=0;       /* for testing */
01184 
01185     /* if we are allowing the node to die, kill it */
01186     if( !bx ) usrfree( key, data );
01187 
01188     return bx;  /* Allow the caller to  kill this nodes data + key */
01189 }
01190 
01191 /*
01192  *       Hash test program : use 'sfxhash 1000 50000' to stress the Auto_NodeRecover feature
01193  */
01194 int main ( int argc, char ** argv )
01195 {
01196     int             i;
01197     SFXHASH      * t;
01198     SFXHASH_NODE * n;
01199     char            strkey[256], strdata[256], * p;
01200     int             num = 100;
01201     int             mem = 0;
01202 
01203     memset(strkey,0,20);
01204     memset(strdata,0,20);
01205 
01206     if( argc > 1 )
01207     {
01208         num = atoi(argv[1]);
01209     }
01210 
01211     if( argc > 2 )
01212     {
01213         mem = atoi(argv[2]);
01214     }
01215 
01216     /* Create a Hash Table */
01217     t = sfxhash_new( 100,        /* one row per element in table, when possible */
01218                      20,        /* key size :  padded with zeros */ 
01219                      20,        /* data size:  padded with zeros */ 
01220                      mem,       /* max bytes,  0=no max */  
01221                      1,         /* enable AutoNodeRecovery */
01222                      anrfree,   /* provide a function to let user know we want to kill a node */
01223                      usrfree, /* provide a function to release user memory */
01224                      1);      /* Recycle nodes */
01225     if(!t)
01226     {
01227         printf("Low Memory!\n");
01228         exit(0);
01229     }
01230     /* Add Nodes to the Hash Table */
01231     for(i=0;i<num;i++) 
01232     {
01233         sprintf(strkey, "KeyWord%5.5d",i+1);
01234         sprintf(strdata,"KeyWord%5.5d",i+1);
01235         //strupr(strdata);
01236         sfxhash_add( t, strkey  /* user key */ ,  strdata /* user data */ );
01237     }  
01238 
01239     /* Find and Display Nodes in the Hash Table */
01240     printf("\n** FIND KEY TEST\n");
01241     for(i=0;i<num;i++) 
01242     {
01243         sprintf(strkey,"KeyWord%5.5d",i+1);
01244 
01245         p = (char*) sfxhash_find( t, strkey );
01246 
01247         if(p)printf("Hash-key=%*s, data=%*s\n", strlen(strkey),strkey, strlen(strkey), p );
01248     }  
01249 
01250     /* Show memcap memory */
01251     printf("\n...******\n");
01252     sfmemcap_showmem(&t->mc);
01253     printf("...******\n");
01254 
01255     /* Display All Nodes in the Hash Table findfirst/findnext */
01256     printf("\n...FINDFIRST / FINDNEXT TEST\n");
01257     for( n  = sfxhash_findfirst(t); 
01258          n != 0; 
01259          n  = sfxhash_findnext(t) )
01260     {
01261         printf("hash-findfirst/next: n=%x, key=%s, data=%s\n", n, n->key, n->data );
01262 
01263         /*
01264           remove node we are looking at, this is first/next safe.
01265         */
01266         if( sfxhash_remove(t,n->key) ) 
01267         {
01268             printf("...ERROR: Could not remove the key node!\n");
01269         }
01270         else  
01271         {
01272             printf("...key node removed\n");
01273         }
01274     }
01275 
01276     printf("...Auto-Node-Recovery: %d recycle attempts, %d completions.\n",t->anr_tries,t->anr_count);
01277 
01278     /* Free the table and it's user data */
01279     printf("...sfxhash_delete\n");
01280 
01281     sfxhash_delete( t );
01282    
01283     printf("\nnormal pgm finish\n\n");
01284 
01285     return 0;
01286 }
01287 #endif
01288 
01289 

Generated on Sun May 14 14:51:18 2006 by  doxygen 1.4.2