modified: tags
[yuchen.git] / sfxhash.c
blob8d09231a3b34b0606513f65d4efff0e59eae4eca
1 /****************************************************************************
3 * Copyright (C) 2003-2009 Sourcefire, Inc.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License Version 2 as
7 * published by the Free Software Foundation. You may not use, modify or
8 * distribute this program under any other version of the GNU General
9 * Public License.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 ****************************************************************************/
22 /*!@file sfxhash.c
24 * A Customized hash table library for storing and accessing key + data pairs.
26 * This table incorporates a memory manager (memcap.c) to provide a memory cap,
27 * and an automatic node recovery system for out of memory management. Keys and
28 * Data are copied into the hash table during the add operation. The data may
29 * be allocated and free'd by the user (by setting the datasize to zero ). A
30 * user callback is provided to allow the user to do cleanup whenever a node
31 * is released, by either the ANR system or the relase() function.
33 * Users can and should delete nodes when they know they are not needed anymore,
34 * but this custom table is designed for the case where nodes are allocated
35 * permanently, we have to limit memory, and we wish to recycle old nodes.
36 * Many problems have a natural node ageing paradigm working in our favor,
37 * so automated node aging makes sense. i.e. thresholding, tcp state.
39 * This hash table maps keys to data. All keys must be unique.
40 * Uniqueness is enforcedby the code.
42 * Features:
44 * 1) Keys must be fixed length (per table) binary byte sequences.
45 * keys are copied during the add function
46 * 2) Data must be fixed length (per table) binary byte sequences.
47 * data is copied during the add function - if datasize > 0
48 * Data may be managed by the user as well.
49 * 3) Table row sizes can be automatically adjusted to
50 * the nearest prime number size during table initialization/creation.
51 * 4) Memory management includes tracking the size of each allocation,
52 * number of allocations, enforcing a memory cap, and automatic node
53 * recovery - when memory is low the oldest untouched node
54 * is unlinked and recycled for use as a new node.
56 * Per Node Memory Usage:
57 * ----------------------
58 * SFXHASH_NODE bytes
59 * KEYSIZE bytes
60 * [DATASIZE bytes] if datasize > 0 during call to sfxhash_new.
62 * The hash node memory (sfxhash_node,key,and data) is allocated with
63 * one call to s_alloc/memcap_alloc.
65 * Author: Marc Norton
67 * 2003-06-03: cmg - added sfxhash_{l,m}ru to return {least,most}
68 * recently used node from the global list
70 * - added _anrcount function
71 * - changed count function to return unsigned to match structure
73 * 2003-06-11: cmg added
74 * overhead_bytes + blocks to separate out the
75 * memcap constraints from the hash table itself
76 * find success v fail
78 * 2003-06-19: cmg added
80 * ability to set own hash function
81 * ability to set own key cmp function
83 * 2003-06-30: rdempster
84 * fixed bug in that would anr from the freelist
86 * 2005-11-15: modified sfxhash_add to check if 'data' is zero before memcpy'ing.
87 * this allows user to pass null for data, and set up the data area
88 * themselves after the call - this is much more flexible.
89 * 8/31/2006: man - changed to use prime table lookup.
91 #include <stdio.h>
92 #include <stdlib.h>
93 #include <string.h>
95 #include "sfxhash.h"
96 #include "sfprimetable.h"
98 /**@defgroup sfxhash sourcefire.container.sfxhash
99 * Implements SFXHASH as specialized hash container
100 * @{
104 * Private Malloc - abstract the memory system
106 static inline void * s_alloc( SFXHASH * t, int n )
108 return sfmemcap_alloc( &t->mc, n );
110 static inline void s_free( SFXHASH * t, void * p )
112 sfmemcap_free( &t->mc, p );
116 * User access to the memory management, do they need it ? WaitAndSee
118 void * sfxhash_alloc( SFXHASH * t, unsigned nbytes )
120 return s_alloc( t, nbytes );
122 void sfxhash_free( SFXHASH * t, void * p )
124 s_free( t, p );
127 int sfxhash_nearest_powerof2(int nrows)
129 unsigned i;
131 nrows -= 1;
132 for(i=1; i<sizeof(nrows) * 8; i <<= 1)
133 nrows = nrows | (nrows >> i);
134 nrows += 1;
136 return nrows;
139 int sfxhash_calcrows(int num)
141 return sfxhash_nearest_powerof2(num);
142 // return sf_nearest_prime( nrows );
148 * Create a new hash table
150 * By default, this will "splay" nodes to the top of a free list.
152 * @param nrows number of rows in hash table
153 * @param keysize key size in bytes, same for all keys
154 * @param datasize datasize in bytes, zero indicates user manages data
155 * @param maxmem maximum memory to use in bytes
156 * @param anr_flag Automatic Node Recovery boolean flag
157 * @param anrfree users Automatic Node Recovery memory release function
158 * @param usrfree users standard memory release function
160 * @return SFXHASH*
161 * @retval 0 out of memory
162 * @retval !0 Valid SFXHASH pointer
166 Notes:
167 if nrows < 0 don't cal the nearest prime.
168 datasize must be the same for all entries, unless datasize is zero.
169 maxmem of 0 indicates no memory limits.
172 SFXHASH * sfxhash_new( int nrows, int keysize, int datasize, int maxmem,
173 int anr_flag,
174 int (*anrfree)(void * key, void * data),
175 int (*usrfree)(void * key, void * data),
176 int recycle_flag )
178 int i;
179 SFXHASH * h;
181 if( nrows > 0 ) /* make sure we have a prime number */
183 // nrows = sf_nearest_prime( nrows );
184 /* If nrows is not a power of two, need to find the
185 * next highest power of two */
186 nrows = sfxhash_nearest_powerof2(nrows);
188 else /* use the magnitude of nrows as is */
190 nrows = -nrows;
193 /* Allocate the table structure from general memory */
194 //h = (SFXHASH*) calloc( 1, sizeof(SFXHASH) );
195 h = (SFXHASH*)calloc(sizeof(SFXHASH), sizeof(char));
196 if( !h )
198 return 0;
201 /* this has a default hashing function */
202 h->sfhashfcn = sfhashfcn_new( nrows );
204 if( !h->sfhashfcn )
206 free(h);
207 return 0;
210 sfmemcap_init( &h->mc, maxmem );
212 /* Allocate the array of node ptrs */
213 h->table = (SFXHASH_NODE**) s_alloc( h, sizeof(SFXHASH_NODE*) * nrows );
214 if( !h->table )
216 free(h->sfhashfcn);
217 free(h);
218 return 0;
221 for( i=0; i<nrows; i++ )
223 h->table[i] = 0;
226 h->anrfree = anrfree;
227 h->usrfree = usrfree;
228 h->keysize = keysize;
229 h->datasize = datasize;
230 h->nrows = nrows;
231 h->max_nodes = 0;
232 h->crow = 0;
233 h->cnode = 0;
234 h->count = 0;
235 h->ghead = 0;
236 h->gtail = 0;
237 h->anr_count= 0;
238 h->anr_tries= 0;
239 h->anr_flag = anr_flag;
240 h->splay = 1;
241 h->recycle_nodes = recycle_flag;
243 h->find_success = 0;
244 h->find_fail = 0;
246 /* save off how much we've already allocated from our memcap */
247 h->overhead_bytes = h->mc.memused;
248 h->overhead_blocks = h->mc.nblocks;
250 return h;
254 * Set the maximum nodes used in this hash table.
255 * Specifying 0 is unlimited (or otherwise limited by memcap).
257 * @param h SFXHASH table pointer
258 * @param max_nodes maximum nodes to allow.
261 void sfxhash_set_max_nodes( SFXHASH *h, int max_nodes )
263 if (h)
265 h->max_nodes = max_nodes;
270 * Set Splay mode : Splays nodes to front of list on each access
272 * @param t SFXHASH table pointer
273 * @param n boolean flag toggles splaying of hash nodes
276 void sfxhash_splaymode( SFXHASH * t, int n )
278 t->splay = n;
283 * Free all nodes in the free list
285 * Removes and frees all of the nodes in the free list
286 * No need to call the user free, since that should've been
287 * done when those nodes were put back in the free list.
289 * @param h SFXHASH table pointer
291 static
292 void sfxhash_delete_free_list(SFXHASH *t)
294 SFXHASH_NODE *cur = NULL;
295 SFXHASH_NODE *next = NULL;
297 if (t == NULL || t->fhead == NULL)
298 return;
300 cur = t->fhead;
302 while (cur != NULL)
304 next = cur->gnext;
305 s_free(t, (void *)cur);
306 cur = next;
309 t->fhead = NULL;
310 t->ftail = NULL;
314 * Delete the hash Table
316 * free key's, free node's, and free the users data.
318 * @param h SFXHASH table pointer
321 void sfxhash_delete( SFXHASH * h )
323 unsigned i;
324 SFXHASH_NODE * node, * onode;
326 if( !h ) return;
328 if( h->sfhashfcn ) sfhashfcn_free( h->sfhashfcn );
330 if( h->table )
332 for(i=0;i<h->nrows;i++)
334 for( node=h->table[i]; node; )
336 onode = node;
337 node = node->next;
339 /* Notify user that we are about to free this node function */
340 if( h->usrfree )
341 h->usrfree( onode->key, onode->data );
343 s_free( h,onode );
346 s_free( h, h->table );
347 h->table = 0;
350 sfxhash_delete_free_list( h );
352 free( h ); /* free the table from general memory */
356 * Empty out the hash table
358 * @param h SFXHASH table pointer
360 * @return -1 on error
362 int sfxhash_make_empty(SFXHASH *h)
364 SFXHASH_NODE *n = NULL;
365 SFXHASH_NODE *tmp = NULL;
366 unsigned i;
368 if (h == NULL)
369 return -1;
371 for (i = 0; i < h->nrows; i++)
373 for (n = h->table[i]; n != NULL; n = tmp)
375 tmp = n->next;
376 if (sfxhash_free_node(h, n) != SFXHASH_OK)
378 return -1;
383 h->max_nodes = 0;
384 h->crow = 0;
385 h->cnode = NULL;
386 h->count = 0;
387 h->ghead = NULL;
388 h->gtail = NULL;
389 h->anr_count = 0;
390 h->anr_tries = 0;
391 h->find_success = 0;
392 h->find_fail = 0;
394 return 0;
399 * Get the # of Nodes in HASH the table
401 * @param t SFXHASH table pointer
404 unsigned sfxhash_count( SFXHASH * t )
406 return t->count;
410 * Get the # auto recovery
412 * @param t SFXHASH table pointer
415 unsigned sfxhash_anr_count( SFXHASH * t )
417 return t->anr_count;
421 * Get the # finds
423 * @param t SFXHASH table pointer
426 unsigned sfxhash_find_total( SFXHASH * t )
428 return t->find_success + t->find_fail;
432 * Get the # unsucessful finds
434 * @param t SFXHASH table pointer
437 unsigned sfxhash_find_fail( SFXHASH * t )
439 return t->find_fail;
443 * Get the # sucessful finds
445 * @param t SFXHASH table pointer
448 unsigned sfxhash_find_success( SFXHASH * t )
450 return t->find_success;
457 * Get the # of overhead bytes
459 * @param t SFXHASH table pointer
462 unsigned sfxhash_overhead_bytes( SFXHASH * t )
464 return t->overhead_bytes;
468 * Get the # of overhead blocks
470 * @param t SFXHASH table pointer
473 unsigned sfxhash_overhead_blocks( SFXHASH * t )
475 return t->overhead_blocks;
478 /** Save the freed node for later use (recylcing).
479 * Free List - uses the NODE gnext/gprev fields
481 static
482 void sfxhash_save_free_node( SFXHASH *t, SFXHASH_NODE * hnode )
484 /* Add A Node to the Free Node List */
485 if( t->fhead ) /* add the node to head of the the existing list */
487 hnode->gprev = 0;
488 hnode->gnext = t->fhead;
489 t->fhead->gprev = hnode;
490 t->fhead = hnode;
491 /* tail is not affected */
493 else /* 1st node in this list */
495 hnode->gprev = 0;
496 hnode->gnext = 0;
497 t->fhead = hnode;
498 t->ftail = hnode;
502 /**Get a previously freed node for reuse.
504 static
505 SFXHASH_NODE * sfxhash_get_free_node( SFXHASH *t )
507 SFXHASH_NODE * node = t->fhead;
509 /* Remove A Node from the Free Node List - remove the head node */
510 if( t->fhead )
512 t->fhead = t->fhead->gnext;
513 if( t->fhead )
514 t->fhead->gprev = 0;
516 if( t->ftail == node ) /* no more nodes - clear the tail */
517 t->ftail = 0;
520 return node;
523 static
524 void sfxhash_glink_node( SFXHASH *t, SFXHASH_NODE * hnode )
526 /* Add The Node */
527 if( t->ghead ) /* add the node to head of the the existing list */
529 hnode->gprev = 0;
530 hnode->gnext = t->ghead;
531 t->ghead->gprev = hnode;
532 t->ghead = hnode;
533 /* tail is not affected */
535 else /* 1st node in this list */
537 hnode->gprev = 0;
538 hnode->gnext = 0;
539 t->ghead = hnode;
540 t->gtail = hnode;
544 static
545 void sfxhash_gunlink_node( SFXHASH *t, SFXHASH_NODE * hnode )
547 /* Remove the Head Node */
548 if( t->ghead == hnode ) /* add the node to head of the the existing list */
550 t->ghead = t->ghead->gnext;
551 if( t->ghead )
552 t->ghead->gprev = 0;
555 if( hnode->gprev ) hnode->gprev->gnext = hnode->gnext;
556 if( hnode->gnext ) hnode->gnext->gprev = hnode->gprev;
558 if( t->gtail == hnode )
559 t->gtail = hnode->gprev;
562 /**Move node to the front of global list. Node movement is application specific.
564 void sfxhash_gmovetofront( SFXHASH *t, SFXHASH_NODE * hnode )
566 if( hnode != t->ghead )
568 sfxhash_gunlink_node( t, hnode );
569 sfxhash_glink_node( t, hnode );
576 static
577 void sfxhash_link_node( SFXHASH * t, SFXHASH_NODE * hnode )
579 /* Add The Node to the Hash Table Row List */
580 if( t->table[hnode->rindex] ) /* add the node to the existing list */
582 hnode->prev = 0; // insert node as head node
583 hnode->next=t->table[hnode->rindex];
584 t->table[hnode->rindex]->prev = hnode;
585 t->table[hnode->rindex] = hnode;
587 else /* 1st node in this list */
589 hnode->prev=0;
590 hnode->next=0;
591 t->table[hnode->rindex] = hnode;
595 static
596 void sfxhash_unlink_node( SFXHASH * t, SFXHASH_NODE * hnode )
598 if( hnode->prev ) // definitely not the 1st node in the list
600 hnode->prev->next = hnode->next;
601 if( hnode->next )
602 hnode->next->prev = hnode->prev;
604 else if( t->table[hnode->rindex] ) // must be the 1st node in the list
606 t->table[hnode->rindex] = t->table[hnode->rindex]->next;
607 if( t->table[hnode->rindex] )
608 t->table[hnode->rindex]->prev = 0;
613 * move a node to the front of the row list at row = 'index'
615 static void movetofront( SFXHASH *t, SFXHASH_NODE * n )
617 /* Modify Hash Node Row List */
618 if( t->table[n->rindex] != n ) // if not at front of list already...
620 /* Unlink the node */
621 sfxhash_unlink_node( t, n );
623 /* Link at front of list */
624 sfxhash_link_node( t, n );
627 /* Move node in the global hash node list to the front */
628 sfxhash_gmovetofront( t, n );
632 * Allocat a new hash node, uses Auto Node Recovery if needed and enabled.
634 * The oldest node is the one with the longest time since it was last touched,
635 * and does not have any direct indication of how long the node has been around.
636 * We don't monitor the actual time since last being touched, instead we use a
637 * splayed global list of node pointers. As nodes are accessed they are splayed
638 * to the front of the list. The oldest node is just the tail node.
641 static
642 SFXHASH_NODE * sfxhash_newnode( SFXHASH * t )
644 SFXHASH_NODE * hnode;
646 /* Recycle Old Nodes - if any */
647 hnode = sfxhash_get_free_node( t );
649 /* Allocate memory for a node */
650 if( ! hnode )
652 if ((t->max_nodes == 0) || (t->count < t->max_nodes))
654 hnode = (SFXHASH_NODE*)s_alloc( t, sizeof(SFXHASH_NODE) +
655 t->keysize + t->datasize );
659 /* If we still haven't found hnode, we're at our memory limit.
661 * Uses Automatic Node Recovery, to recycle the oldest node-based on access
662 * (Unlink and reuse the tail node)
664 if( !hnode && t->anr_flag && t->gtail )
666 /* Find the oldes node the users willing to let go. */
667 for(hnode = t->gtail; hnode; hnode = hnode->gprev )
669 if( t->anrfree ) /* User has provided a permission+release callback function */
671 t->anr_tries++;/* Count # ANR requests */
673 /* Ask the user for permission to release this node, but let them say no! */
674 if( t->anrfree( hnode->key, hnode->data ) )
676 /* NO, don't recycle this node, user's not ready to let it go. */
677 continue;
680 /* YES, user said we can recycle this node */
683 sfxhash_gunlink_node( t, hnode ); /* unlink from the global list */
684 sfxhash_unlink_node( t, hnode ); /* unlink from the row list */
685 t->count--;
686 t->anr_count++; /* count # of ANR operations */
687 break;
691 /* either we are returning a node or we're all full and the user
692 * won't let us allocate anymore and we return NULL */
693 return hnode;
698 * Find a Node based on the key, return the node and the index.
699 * The index is valid even if the return value is NULL, in which
700 * case the index is the corect row in which the node should be
701 * created.
705 #define hashsize(n) ((uint32_t)1<<(n))
706 #define hashmask(n) (hashsize(n)-1)
708 static
709 SFXHASH_NODE * sfxhash_find_node_row( SFXHASH * t, void * key, int * rindex )
711 unsigned hashkey;
712 int index;
713 SFXHASH_NODE *hnode;
715 hashkey = t->sfhashfcn->hash_fcn( t->sfhashfcn,
716 (unsigned char*)key,
717 t->keysize );
719 /* printf("hashkey: %u t->keysize: %d\n", hashkey, t->keysize); */
720 /* flowkey_fprint(stdout, key); */
721 /* printf("****\n"); */
723 // index = hashkey % t->nrows;
724 /* Modulus is slow. Switched to a table size that is a power of 2. */
725 index = hashkey & (t->nrows - 1);
727 *rindex = index;
729 for( hnode=t->table[index]; hnode; hnode=hnode->next )
731 if( !t->sfhashfcn->keycmp_fcn(hnode->key,key,t->keysize) )
733 if( t->splay > 0 )
734 movetofront(t,hnode);
736 t->find_success++;
737 return hnode;
741 t->find_fail++;
742 return NULL;
748 * Add a key + data pair to the hash table
750 * 2003-06-06:
751 * - unique_tracker.c assumes that this splays
752 * nodes to the top when they are added.
754 * This is done because of the successful find.
756 * @param t SFXHASH table pointer
757 * @param key users key pointer
758 * @param data users data pointer
760 * @return integer
761 * @retval SFXHASH_OK success
762 * @retval SFXHASH_INTABLE already in the table, t->cnode points to the node
763 * @retval SFXHASH_NOMEM not enough memory
765 int sfxhash_add( SFXHASH * t, void * key, void * data )
767 int index;
768 SFXHASH_NODE * hnode;
770 /* Enforce uniqueness: Check for the key in the table */
771 hnode = sfxhash_find_node_row( t, key, &index );
773 if( hnode )
775 t->cnode = hnode;
777 return SFXHASH_INTABLE; /* found it - return it. */
781 * Alloc new hash node - allocate key space and data space at the same time.
783 hnode = sfxhash_newnode( t );
784 if( !hnode )
786 return SFXHASH_NOMEM;
789 /* Set up the new key pointer */
790 hnode->key = (char*)hnode + sizeof(SFXHASH_NODE);
792 /* Copy the key */
793 memcpy(hnode->key,key,t->keysize);
795 /* Save our table row index */
796 hnode->rindex = index;
798 /* Copy the users data - or if datasize is zero set ptr to users data */
799 if( t->datasize )
801 /* Set up the new data pointer */
802 hnode->data= (char*)hnode + sizeof(SFXHASH_NODE) + t->keysize;
804 if(data)
806 memcpy(hnode->data,data,t->datasize);
809 else
811 hnode->data = data;
814 /* Link the node into the table row list */
815 sfxhash_link_node ( t, hnode );
817 /* Link at the front of the global node list */
818 sfxhash_glink_node( t, hnode );
820 /* Track # active nodes */
821 t->count++;
823 return SFXHASH_OK;
828 * Add a key to the hash table, return the hash node
830 * 2003-06-06:
831 * - unique_tracker.c assumes that this splays
832 * nodes to the top when they are added.
834 * This is done because of the successful find.
836 * @param t SFXHASH table pointer
837 * @param key users key pointer
839 * @return integer
840 * @retval SFXHASH_OK success
841 * @retval SFXHASH_INTABLE already in the table, t->cnode points to the node
842 * @retval SFXHASH_NOMEM not enough memory
844 SFXHASH_NODE * sfxhash_get_node( SFXHASH * t, void * key )
846 int index;
847 SFXHASH_NODE * hnode;
849 /* Enforce uniqueness: Check for the key in the table */
850 hnode = sfxhash_find_node_row( t, key, &index );
852 if( hnode )
854 t->cnode = hnode;
856 return hnode; /* found it - return it. */
860 * Alloc new hash node - allocate key space and data space at the same time.
862 hnode = sfxhash_newnode( t );
863 if( !hnode )
865 return NULL;
868 /* Set up the new key pointer */
869 hnode->key = (char*)hnode + sizeof(SFXHASH_NODE);
871 /* Copy the key */
872 memcpy(hnode->key,key,t->keysize);
874 /* Save our table row index */
875 hnode->rindex = index;
877 /* Copy the users data - or if datasize is zero set ptr to users data */
878 if( t->datasize )
880 /* Set up the new data pointer */
881 hnode->data= (char*)hnode + sizeof(SFXHASH_NODE) + t->keysize;
883 else
885 hnode->data = NULL;
888 /* Link the node into the table row list */
889 sfxhash_link_node ( t, hnode );
891 /* Link at the front of the global node list */
892 sfxhash_glink_node( t, hnode );
894 /* Track # active nodes */
895 t->count++;
897 return hnode;
902 * Find a Node based on the key
904 * @param t SFXHASH table pointer
905 * @param key users key pointer
907 * @return SFXHASH_NODE* valid pointer to the hash node
908 * @retval 0 node not found
911 SFXHASH_NODE * sfxhash_find_node( SFXHASH * t, void * key)
913 int rindex;
915 return sfxhash_find_node_row( t, key, &rindex );
919 * Find the users data based associated with the key
921 * @param t SFXHASH table pointer
922 * @param key users key pointer
924 * @return void* valid pointer to the users data
925 * @retval 0 node not found
928 void * sfxhash_find( SFXHASH * t, void * key)
930 SFXHASH_NODE * hnode;
931 int rindex;
933 hnode = sfxhash_find_node_row( t, key, &rindex );
935 if( hnode ) return hnode->data;
937 return NULL;
941 /**
942 * Get the HEAD of the in use list
944 * @param t table pointer
946 * @return the head of the list or NULL
948 SFXHASH_NODE *sfxhash_ghead( SFXHASH * t )
950 if(t)
952 return t->ghead;
955 return NULL;
959 /**
960 * Walk the global list
962 * @param n current node
964 * @return the next node in the list or NULL when at the end
966 SFXHASH_NODE *sfxhash_gnext( SFXHASH_NODE *n )
968 if(n)
970 return n->gnext;
973 return NULL;
978 * Return the most recently used data from the global list
980 * @param t SFXHASH table pointer
982 * @return void* valid pointer to the users data
983 * @retval 0 node not found
986 void * sfxhash_mru( SFXHASH * t )
988 SFXHASH_NODE * hnode;
990 hnode = sfxhash_ghead(t);
992 if( hnode )
993 return hnode->data;
995 return NULL;
999 * Return the least recently used data from the global list
1001 * @param t SFXHASH table pointer
1003 * @return void* valid pointer to the users data
1004 * @retval 0 node not found
1007 void * sfxhash_lru( SFXHASH * t )
1009 SFXHASH_NODE * hnode;
1011 hnode = t->gtail;
1013 if( hnode ) return hnode->data;
1015 return NULL;
1019 * Return the most recently used node from the global list
1021 * @param t SFXHASH table pointer
1023 * @return SFXHASH_NODE* valid pointer to a node
1024 * @retval 0 node not found
1027 SFXHASH_NODE * sfxhash_mru_node( SFXHASH * t )
1029 SFXHASH_NODE * hnode;
1031 hnode = sfxhash_ghead(t);
1033 if( hnode )
1034 return hnode;
1036 return NULL;
1040 * Return the least recently used node from the global list
1042 * @param t SFXHASH table pointer
1044 * @return SFXHASH_NODE* valid pointer to a node
1045 * @retval 0 node not found
1048 SFXHASH_NODE * sfxhash_lru_node( SFXHASH * t )
1050 SFXHASH_NODE * hnode;
1052 hnode = t->gtail;
1054 if( hnode ) return hnode;
1056 return NULL;
1060 * Get some hash table statistics. NOT FOR REAL TIME USE.
1063 * @param t SFXHASH table pointer
1064 * @param filled how many
1066 * @return max depth of the table
1069 unsigned sfxhash_maxdepth( SFXHASH * t )
1071 unsigned i;
1072 unsigned max_depth = 0;
1074 SFXHASH_NODE * hnode;
1076 for( i=0; i<t->nrows; i++ )
1078 unsigned cur_depth = 0;
1080 for(hnode = t->table[i]; hnode != NULL; hnode = hnode->next)
1082 cur_depth++;
1085 if(cur_depth > max_depth)
1086 max_depth = cur_depth;
1089 return max_depth;
1092 double sfxhash_used_count( SFXHASH * t )
1094 unsigned i;
1095 double used = 0;
1097 SFXHASH_NODE * hnode;
1099 for( i=0; i<t->nrows; i++ )
1102 hnode = t->table[i];
1103 if(hnode != NULL)
1104 used++;
1107 return used/(t->nrows);
1112 * Unlink and free the node
1114 int sfxhash_free_node( SFXHASH * t, SFXHASH_NODE * hnode)
1116 sfxhash_unlink_node( t, hnode ); /* unlink from the hash table row list */
1118 sfxhash_gunlink_node( t, hnode ); /* unlink from global-hash-node list */
1120 t->count--;
1122 if( t->usrfree )
1124 t->usrfree( hnode->key, hnode->data );
1127 if( t->recycle_nodes )
1129 sfxhash_save_free_node( t, hnode );
1131 else
1133 s_free( t, hnode );
1136 return SFXHASH_OK;
1140 * Remove a Key + Data Pair from the table.
1142 * @param t SFXHASH table pointer
1143 * @param key users key pointer
1145 * @return 0 success
1146 * @retval !0 failed
1149 int sfxhash_remove( SFXHASH * t, void * key)
1151 SFXHASH_NODE * hnode;
1152 unsigned hashkey, index;
1154 hashkey = t->sfhashfcn->hash_fcn( t->sfhashfcn,
1155 (unsigned char*)key,
1156 t->keysize );
1158 // index = hashkey % t->nrows;
1159 /* Modulus is slow */
1160 index = hashkey & (t->nrows - 1);
1162 hnode = t->table[index];
1164 for( hnode=t->table[index]; hnode; hnode=hnode->next )
1166 if( !t->sfhashfcn->keycmp_fcn(hnode->key,key,t->keysize) )
1168 return sfxhash_free_node( t, hnode );
1172 return SFXHASH_ERR;
1176 Internal use only
1178 static
1179 void sfxhash_next( SFXHASH * t )
1181 if( !t->cnode )
1182 return ;
1184 /* Next node in current node list */
1185 t->cnode = t->cnode->next;
1186 if( t->cnode )
1188 return;
1191 /* Next row */
1192 /* Get 1st node in next non-emtoy row/node list */
1193 for( t->crow++; t->crow < t->nrows; t->crow++ )
1195 t->cnode = t->table[ t->crow ];
1196 if( t->cnode )
1198 return;
1203 * Find and return the first hash table node
1205 * @param t SFXHASH table pointer
1207 * @return 0 failed
1208 * @retval !0 valid SFXHASH_NODE *
1211 SFXHASH_NODE * sfxhash_findfirst( SFXHASH * t )
1213 SFXHASH_NODE * n;
1215 if(!t)
1216 return NULL;
1218 /* Start with 1st row */
1219 for( t->crow=0; t->crow < t->nrows; t->crow++ )
1221 /* Get 1st Non-Null node in row list */
1222 t->cnode = t->table[ t->crow ];
1223 if( t->cnode )
1225 n = t->cnode;
1226 sfxhash_next( t ); // load t->cnode with the next entry
1227 return n;
1231 return NULL;
1235 * Find and return the next hash table node
1237 * @param t SFXHASH table pointer
1239 * @return 0 failed
1240 * @retval !0 valid SFXHASH_NODE *
1243 SFXHASH_NODE * sfxhash_findnext( SFXHASH * t )
1245 SFXHASH_NODE * n;
1247 n = t->cnode;
1248 if( !n ) /* Done, no more entries */
1250 return NULL;
1254 Preload next node into current node
1256 sfxhash_next( t );
1258 return n;
1262 /**
1263 * Make sfhashfcn use a separate set of operators for the backend.
1265 * @param h sfhashfcn ptr
1266 * @param hash_fcn user specified hash function
1267 * @param keycmp_fcn user specified key comparisoin function
1270 int sfxhash_set_keyops( SFXHASH *h ,
1271 unsigned (*hash_fcn)( SFHASHFCN * p,
1272 unsigned char *d,
1273 int n),
1274 int (*keycmp_fcn)( const void *s1,
1275 const void *s2,
1276 size_t n))
1278 if(h && hash_fcn && keycmp_fcn)
1280 return sfhashfcn_set_keyops(h->sfhashfcn, hash_fcn, keycmp_fcn);
1283 return -1;
1288 * -----------------------------------------------------------------------------------------
1289 * Test Driver for Hashing
1290 * -----------------------------------------------------------------------------------------
1295 This is called when the user releases a node or kills the table
1297 int usrfree( void * key, void * data )
1300 /* Release any data you need to */
1301 return 0;
1305 Auto Node Recovery Callback - optional
1307 This is called to ask the user to kill a node, if it reutrns !0 than the hash
1308 library does not kill this node. If the user os willing to let the node die,
1309 the user must do any free'ing or clean up on the node during this call.
1311 int anrfree( void * key, void * data )
1313 static int bx = 0;
1315 /* Decide if we can free this node. */
1317 //bx++; if(bx == 4 )bx=0; /* for testing */
1319 /* if we are allowing the node to die, kill it */
1320 if( !bx ) usrfree( key, data );
1322 return bx; /* Allow the caller to kill this nodes data + key */
1326 * Hash test program : use 'sfxhash 1000 50000' to stress the Auto_NodeRecover feature
1328 int main ( int argc, char ** argv )
1330 int i;
1331 SFXHASH * t;
1332 SFXHASH_NODE * n;
1333 char strkey[256], strdata[256], * p;
1334 int num = 1000;
1335 int mem = 0;
1336 FILE *fp;
1337 char buf[1024] = {0};
1338 char *ret;
1340 memset(strkey,0,20);
1341 memset(strdata,0,20);
1343 if( argc > 1 )
1345 num = atoi(argv[1]);
1348 if( argc > 2 )
1350 mem = atoi(argv[2]);
1353 /* Create a Hash Table */
1354 t = sfxhash_new( 46028*2, /* one row per element in table, when possible */
1355 20, /* key size : padded with zeros */
1356 20, /* data size: padded with zeros */
1357 mem, /* max bytes, 0=no max */
1358 1, /* enable AutoNodeRecovery */
1359 anrfree, /* provide a function to let user know we want to kill a node */
1360 usrfree, /* provide a function to release user memory */
1361 1); /* Recycle nodes */
1362 if(!t)
1364 printf("Low Memory!\n");
1365 exit(0);
1367 /* Add Nodes to the Hash Table */
1368 fp = fopen("word_line.txt", "r");
1369 if(!fp)
1371 printf("open file failed!\n");
1372 exit(0);
1374 for(;;)
1376 ret = fgets(buf, 1024, fp);
1377 if(ret == NULL)
1378 break;
1379 sfxhash_add( t, buf/* key */, buf /* data */ );
1381 fclose(fp);
1383 /* Find and Display Nodes in the Hash Table */
1384 //printf("\n** FIND KEY TEST\n");
1385 for(i=0;i<num;i++)
1387 snprintf(strkey, sizeof(strkey) - 1, "KeyWord%5.5d",i+1);
1388 strkey[sizeof(strkey) - 1] = '\0';
1390 p = (char*) sfxhash_find( t, strkey );
1392 //if(p)printf("Hash-key=%*s, data=%*s\n", strlen(strkey),strkey, strlen(strkey), p );
1395 /* Show memcap memory */
1396 #if 0
1397 printf("\n...******\n");
1398 sfmemcap_showmem(&t->mc);
1399 printf("...******\n");
1400 #endif
1401 printf("node count: %d\n", t->count);
1402 printf("maxdepth: %d\n", sfxhash_maxdepth(t));
1403 printf("use rate: %.2f%%\n", sfxhash_used_count(t)*100);
1404 /* Display All Nodes in the Hash Table findfirst/findnext */
1405 //printf("\n...FINDFIRST / FINDNEXT TEST\n");
1406 for( n = sfxhash_findfirst(t);
1407 n != 0;
1408 n = sfxhash_findnext(t) )
1411 remove node we are looking at, this is first/next safe.
1413 if( sfxhash_remove(t,n->key) )
1415 printf("...ERROR: Could not remove the key node!\n");
1417 else
1423 /* Free the table and it's user data */
1424 // printf("...sfxhash_delete\n");
1426 sfxhash_delete( t );
1428 // printf("\nnormal pgm finish\n\n");
1430 return 0;