哈希表的种类很多,普遍的实现是链式哈希表(解决哈希冲突的问题)。snort也是这样实现的, 每个bucket是一个双向链表。
每次插入和查询某个key value时, 如果成功都会将该元素放置在链表头, 作为缓存方便下次使用。
涉及的文件有sgghash.h|c、sfhashgcn.h|c。
/*
* 哈希表控制对象
*/
typedef struct _SFHASHFCN {
/* seed scale 用于控制哈希表散列更加均匀 */
unsigned seed;
unsigned scale;
unsigned hardener;
/* 计算key的哈希值的函数 */
unsigned (*hash_fcn)(struct _SFHASHFCN * p,
unsigned char *d,
int n );
/* key的比较函数*/
int (*keycmp_fcn)( const void *s1,
const void *s2,
size_t n);
} SFHASHFCN;
/*
* 哈希表节点元素类型,双向链表, 并保存key value
*/
typedef struct _sfghash_node
{
struct _sfghash_node * next, * prev;
void * key; /* Copy of, or Pointer to, the Users key */
void * data; /* Pointer to the users data, this is never copied! */
} SFGHASH_NODE;
/*
* 哈希表的定义
*/
typedef struct _sfghash
{
/* 哈希函数*/
SFHASHFCN * sfhashfcn;
/* key的大小,如果小于0 表示字符串类型, 否则就是固定长度的key。在add时会分配内存*/
int keysize; /* bytes in key, if < 0 -> keys are strings */
/* 如果用户拥有key, 对key进行浅拷贝, 否则深拷贝 */
int userkey; /* user owns the key */
/* 哈希表桶数组, 每个桶都是节点元素指针,代表一个链表(一行)*/
SFGHASH_NODE ** table; /* array of node ptr's */
/* 多少个桶*/
int nrows; /* # rows int the hash table use a prime number 211, 9871 */
/* 哈希表有多少个元素*/
unsigned count; /* total # nodes in table */
/* value 释放的回调函数 */
void (*userfree)( void * );
/* 当前的行号 */
int crow; // findfirst/next row in table
/* 当前行的链表的一个元素指针*/
SFGHASH_NODE * cnode; // findfirst/next node ptr
/* 用于控制每次查询等操作是否把当前节点放置到对应链表的头部*/
int splay;
} SFGHASH, SFDICT;
/* 主要接口函数, 增删改查, 设置哈希表的属性等 */
SFGHASH * sfghash_new( int nrows, int keysize, int userkeys, void (*userfree)(void*p) );
void sfghash_delete( SFGHASH * h );
int sfghash_add ( SFGHASH * h, void * key, void * data );
int sfghash_remove( SFGHASH * h, void * key);
int sfghash_count( SFGHASH * h);
void * sfghash_find( SFGHASH * h, void * key );
int sfghash_find2(SFGHASH *, void *, void **);
SFGHASH_NODE * sfghash_findfirst( SFGHASH * h );
SFGHASH_NODE * sfghash_findnext ( SFGHASH * h );
void sfghash_splaymode( SFGHASH * t, int n );
int sfghash_set_keyops( SFGHASH *h ,
unsigned (*hash_fcn)( SFHASHFCN * p,
unsigned char *d,
int n),
int (*keycmp_fcn)( const void *s1,
const void *s2,
size_t n));
/*
* 创建哈希表
* nrows :多少行
* keysize : key的大小
* userkeys : 使用者自己给Key分配内存,自行管理
* userfree : value的释放回调函数,可不填
*/
SFGHASH * sfghash_new( int nrows, int keysize, int userkeys, void (*userfree)(void*p) )
{
int i;
SFGHASH * h;
if( nrows > 0 ) /* make sure we have a prime number */
{
/* 重新计算需要分配的行的数量, 改成素数, 让哈希计算的结果尽量离散 */
nrows = sf_nearest_prime( nrows );
}
else /* use the magnitude or nrows as is */
{
nrows = -nrows;
}
h = (SFGHASH*)s_alloc( sizeof(SFGHASH) );
if( !h )
return 0;
memset( h, 0, sizeof(SFGHASH) );
h->sfhashfcn = sfhashfcn_new( nrows );
if( !h->sfhashfcn )
{
free(h);
return 0;
}
/* 分配哈希桶空间, 并初始化为0*/
h->table = (SFGHASH_NODE**) s_alloc( sizeof(SFGHASH_NODE*) * nrows );
if( !h->table )
{
free(h->sfhashfcn);
free(h);
return 0;
}
for( i=0; i<nrows; i++ )
{
h->table[i] = 0;
}
/*其他属性的初始化*/
h->userkey = userkeys;
h->keysize = keysize;
h->nrows = nrows;
h->count = 0;
h->userfree = userfree;
h->crow = 0; // findfirst/next current row
h->cnode = 0; // findfirst/next current node ptr
return h;
}
/* 设置优化参数, 将每次获取到的元素放置在链表头的标志*/
void sfghash_splaymode( SFGHASH * t, int n )
{
t->splay = n;
}
/*
* 释放哈希表, 循环遍历每一行,针对每一行的链表遍历释放每个节点
*/
void sfghash_delete( SFGHASH * h )
{
int i;
SFGHASH_NODE * node, * onode;
if( !h ) return;
sfhashfcn_free( h->sfhashfcn );
if( h->table )
{
for(i=0;i<h->nrows;i++)
{
for( node=h->table[i]; node; )
{
onode = node;
node = node->next;
if( !h->userkey && onode->key )
s_free( onode->key );
if( h->userfree && onode->data )
h->userfree( onode->data ); /* free users data, with users function */
s_free( onode );
}
}
s_free( h->table );
h->table = 0;
}
s_free( h );
}
/*
* 往哈希表中insert key/value
*/
int sfghash_add( SFGHASH * t, void * key, void * data )
{
unsigned hashkey;
int klen;
int index;
SFGHASH_NODE *hnode;
if (t == NULL)
return SFGHASH_ERR;
/*
* Get proper Key Size
*/
if( t->keysize > 0 )
{
klen = t->keysize;
}
else
{
/* need the null byte for strcmp() in sfghash_find() */
klen = strlen( (char*)key ) + 1;
}
/* 计算key的哈希值,定位到具体的行*/
hashkey = t->sfhashfcn->hash_fcn( t->sfhashfcn, (unsigned char*) key, klen );
index = hashkey % t->nrows;
/*
* Uniqueness:
* Check 1st to see if the key is already in the table
* Just bail if it is.
*/
/* 循环遍历该行,使用创建哈希表时,设置的key的比较函数,进行对比,如果存在终止循环
* 直接返回
*/
for( hnode=t->table[index]; hnode; hnode=hnode->next )
{
if( t->keysize > 0 )
{
if( !t->sfhashfcn->keycmp_fcn(hnode->key,key,klen) )
{
t->cnode = hnode; /* save pointer to the node */
return SFGHASH_INTABLE; /* found it */
}
}
else
{
if( !strcmp((const char *)hnode->key,(const char*)key) )
{
t->cnode = hnode; /* save pointer to the node */
return SFGHASH_INTABLE; /* found it */
}
}
}
/*
* Create new node
*/
/* 不存在, 创建节点、根据传入的Key value 进行初始化 */
hnode = (SFGHASH_NODE*)s_alloc(sizeof(SFGHASH_NODE));
if( !hnode )
return SFGHASH_NOMEM;
/* Add the Key */
if( t->userkey )
{
/* Use the Users key */
hnode->key = key;
}
else
{
/* Create new key */
hnode->key = s_alloc( klen );
if( !hnode->key )
{
free(hnode);
return SFGHASH_NOMEM;
}
/* Copy key */
memcpy(hnode->key,key,klen);
}
/* Add The Node */
/* 将该节点插入到对应行的头部, 加速下次查询,经验规则 */
if( t->table[index] ) /* add the node to the existing list */
{
hnode->prev = 0; // insert node as head node
hnode->next=t->table[index];
hnode->data=data;
t->table[index]->prev = hnode;
t->table[index] = hnode;
}
else /* 1st node in this list */
{
hnode->prev=0;
hnode->next=0;
hnode->data=data;
t->table[index] = hnode;
}
t->count++;
return SFGHASH_OK;
}
/*
* 哈希表查找:其实这部分和之前的add操作很类似,先计算Key的hash,定位到具体的行,存在,遍历比较,
* 并更新链表头节点,不存在直接返回
* t : 哈希表
* key : 查询的key
*/
void * sfghash_find( SFGHASH * t, void * key)
{
SFGHASH_NODE * hnode;
if (t == NULL)
return NULL;
hnode = sfghash_find_node( t, key );
if( hnode ) return hnode->data;
return NULL;
}
/*
* 哈希表删除key/value,也是需要县查找定位,然后进行链表的删除操作
* t :哈希表
* key : 要删除的Key
*/
int sfghash_remove( SFGHASH * t, void * key)
{
SFGHASH_NODE * hnode;
int klen;
unsigned hashkey, index;
if( t->keysize > 0 )
{
klen = t->keysize;
}
else
{
klen = strlen((char*)key) + 1;
}
hashkey = t->sfhashfcn->hash_fcn( t->sfhashfcn, (unsigned char*) key, klen );
index = hashkey % t->nrows;
for( hnode=t->table[index]; hnode; hnode=hnode->next )
{
if( t->keysize > 0 )
{
if( !t->sfhashfcn->keycmp_fcn(hnode->key,key,klen) )
{
return sfghash_free_node( t, index, hnode );
}
}
else
{
if( !strcmp((const char *)hnode->key,(const char*)key) )
{
return sfghash_free_node( t, index, hnode );
}
}
}
return SFGHASH_ERR;
}
snort的哈希表的实现的主要操作基本上分析完了。