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
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 ****************************************************************************/
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.
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 * ----------------------
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.
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
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.
96 #include "sfprimetable.h"
98 /**@defgroup sfxhash sourcefire.container.sfxhash
99 * Implements SFXHASH as specialized hash container
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
)
127 int sfxhash_nearest_powerof2(int nrows
)
132 for(i
=1; i
<sizeof(nrows
) * 8; i
<<= 1)
133 nrows
= nrows
| (nrows
>> i
);
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
161 * @retval 0 out of memory
162 * @retval !0 Valid SFXHASH pointer
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
,
174 int (*anrfree
)(void * key
, void * data
),
175 int (*usrfree
)(void * key
, void * data
),
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 */
193 /* Allocate the table structure from general memory */
194 //h = (SFXHASH*) calloc( 1, sizeof(SFXHASH) );
195 h
= (SFXHASH
*)calloc(sizeof(SFXHASH
), sizeof(char));
201 /* this has a default hashing function */
202 h
->sfhashfcn
= sfhashfcn_new( nrows
);
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
);
221 for( i
=0; i
<nrows
; i
++ )
226 h
->anrfree
= anrfree
;
227 h
->usrfree
= usrfree
;
228 h
->keysize
= keysize
;
229 h
->datasize
= datasize
;
239 h
->anr_flag
= anr_flag
;
241 h
->recycle_nodes
= recycle_flag
;
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
;
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
)
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
)
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
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
)
305 s_free(t
, (void *)cur
);
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
)
324 SFXHASH_NODE
* node
, * onode
;
328 if( h
->sfhashfcn
) sfhashfcn_free( h
->sfhashfcn
);
332 for(i
=0;i
<h
->nrows
;i
++)
334 for( node
=h
->table
[i
]; node
; )
339 /* Notify user that we are about to free this node function */
341 h
->usrfree( onode
->key
, onode
->data
);
346 s_free( h
, h
->table
);
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
;
371 for (i
= 0; i
< h
->nrows
; i
++)
373 for (n
= h
->table
[i
]; n
!= NULL
; n
= tmp
)
376 if (sfxhash_free_node(h
, n
) != SFXHASH_OK
)
399 * Get the # of Nodes in HASH the table
401 * @param t SFXHASH table pointer
404 unsigned sfxhash_count( SFXHASH
* t
)
410 * Get the # auto recovery
412 * @param t SFXHASH table pointer
415 unsigned sfxhash_anr_count( SFXHASH
* t
)
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
)
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
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 */
488 hnode
->gnext
= t
->fhead
;
489 t
->fhead
->gprev
= hnode
;
491 /* tail is not affected */
493 else /* 1st node in this list */
502 /**Get a previously freed node for reuse.
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 */
512 t
->fhead
= t
->fhead
->gnext
;
516 if( t
->ftail
== node
) /* no more nodes - clear the tail */
524 void sfxhash_glink_node( SFXHASH
*t
, SFXHASH_NODE
* hnode
)
527 if( t
->ghead
) /* add the node to head of the the existing list */
530 hnode
->gnext
= t
->ghead
;
531 t
->ghead
->gprev
= hnode
;
533 /* tail is not affected */
535 else /* 1st node in this list */
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
;
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
);
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 */
591 t
->table
[hnode
->rindex
] = hnode
;
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
;
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.
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 */
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. */
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 */
686 t
->anr_count
++; /* count # of ANR operations */
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 */
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
705 #define hashsize(n) ((uint32_t)1<<(n))
706 #define hashmask(n) (hashsize(n)-1)
709 SFXHASH_NODE
* sfxhash_find_node_row( SFXHASH
* t
, void * key
, int * rindex
)
715 hashkey
= t
->sfhashfcn
->hash_fcn( t
->sfhashfcn
,
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);
729 for( hnode
=t
->table
[index
]; hnode
; hnode
=hnode
->next
)
731 if( !t
->sfhashfcn
->keycmp_fcn(hnode
->key
,key
,t
->keysize
) )
734 movetofront(t
,hnode
);
748 * Add a key + data pair to the hash table
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
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
)
768 SFXHASH_NODE
* hnode
;
770 /* Enforce uniqueness: Check for the key in the table */
771 hnode
= sfxhash_find_node_row( t
, key
, &index
);
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
);
786 return SFXHASH_NOMEM
;
789 /* Set up the new key pointer */
790 hnode
->key
= (char*)hnode
+ sizeof(SFXHASH_NODE
);
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 */
801 /* Set up the new data pointer */
802 hnode
->data
= (char*)hnode
+ sizeof(SFXHASH_NODE
) + t
->keysize
;
806 memcpy(hnode
->data
,data
,t
->datasize
);
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 */
828 * Add a key to the hash table, return the hash node
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
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
)
847 SFXHASH_NODE
* hnode
;
849 /* Enforce uniqueness: Check for the key in the table */
850 hnode
= sfxhash_find_node_row( t
, key
, &index
);
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
);
868 /* Set up the new key pointer */
869 hnode
->key
= (char*)hnode
+ sizeof(SFXHASH_NODE
);
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 */
880 /* Set up the new data pointer */
881 hnode
->data
= (char*)hnode
+ sizeof(SFXHASH_NODE
) + t
->keysize
;
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 */
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
)
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
;
933 hnode
= sfxhash_find_node_row( t
, key
, &rindex
);
935 if( hnode
) return hnode
->data
;
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
)
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
)
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
);
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
;
1013 if( hnode
) return hnode
->data
;
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
);
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
;
1054 if( hnode
) return hnode
;
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
)
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
)
1085 if(cur_depth
> max_depth
)
1086 max_depth
= cur_depth
;
1092 double sfxhash_used_count( SFXHASH
* t
)
1097 SFXHASH_NODE
* hnode
;
1099 for( i
=0; i
<t
->nrows
; i
++ )
1102 hnode
= t
->table
[i
];
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 */
1124 t
->usrfree( hnode
->key
, hnode
->data
);
1127 if( t
->recycle_nodes
)
1129 sfxhash_save_free_node( t
, hnode
);
1140 * Remove a Key + Data Pair from the table.
1142 * @param t SFXHASH table pointer
1143 * @param key users key pointer
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
,
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
);
1179 void sfxhash_next( SFXHASH
* t
)
1184 /* Next node in current node list */
1185 t
->cnode
= t
->cnode
->next
;
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
];
1203 * Find and return the first hash table node
1205 * @param t SFXHASH table pointer
1208 * @retval !0 valid SFXHASH_NODE *
1211 SFXHASH_NODE
* sfxhash_findfirst( SFXHASH
* t
)
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
];
1226 sfxhash_next( t
); // load t->cnode with the next entry
1235 * Find and return the next hash table node
1237 * @param t SFXHASH table pointer
1240 * @retval !0 valid SFXHASH_NODE *
1243 SFXHASH_NODE
* sfxhash_findnext( SFXHASH
* t
)
1248 if( !n
) /* Done, no more entries */
1254 Preload next node into current node
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
,
1274 int (*keycmp_fcn
)( const void *s1
,
1278 if(h
&& hash_fcn
&& keycmp_fcn
)
1280 return sfhashfcn_set_keyops(h
->sfhashfcn
, hash_fcn
, keycmp_fcn
);
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 */
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
)
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
)
1333 char strkey
[256], strdata
[256], * p
;
1337 char buf
[1024] = {0};
1340 memset(strkey
,0,20);
1341 memset(strdata
,0,20);
1345 num
= atoi(argv
[1]);
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 */
1364 printf("Low Memory!\n");
1367 /* Add Nodes to the Hash Table */
1368 fp
= fopen("word_line.txt", "r");
1371 printf("open file failed!\n");
1376 ret
= fgets(buf
, 1024, fp
);
1379 sfxhash_add( t
, buf
/* key */, buf
/* data */ );
1383 /* Find and Display Nodes in the Hash Table */
1384 //printf("\n** FIND KEY TEST\n");
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 */
1397 printf("\n...******\n");
1398 sfmemcap_showmem(&t
->mc
);
1399 printf("...******\n");
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
);
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");
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");