snort 源码分析之基础数据结构 哈希表

哈希表的种类很多,普遍的实现是链式哈希表(解决哈希冲突的问题)。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的哈希表的实现的主要操作基本上分析完了。

猜你喜欢

转载自blog.csdn.net/guoguangwu/article/details/88532351
今日推荐