环境:OpenSSL 0.9.8l,Fedora 12
今天学的是《OpenSSL编程》第四章 哈希表。这一章主要讲了OpenSSL中哈希表的用法,和堆栈一样,OpenSSL实现了一个适用于任何数据类型的哈希表。
下面是哈希表中两个重要的数据结构,这些数据结构的定义在/crypto/lhash/lhash.h中:
这个结构体是一个链表,链表的每一个节点都是一个用户存入的数据:
typedef struct lhash_node_st
{
void *data; //用于保存指向数据的指针
struct lhash_node_st *next; //形成一个链表
#ifndef OPENSSL_NO_HASH_COMP
unsigned long hash; //数据的哈希值
#endif
} LHASH_NODE;
这个结构体表示一个哈希表:
typedef struct lhash_st
{
LHASH_NODE **b; //这是一个LHASH_NODE的指针数组,用于保存数据
LHASH_COMP_FN_TYPE comp; //哈希表的比较函数,OpenSSL提供默认的比较函数
LHASH_HASH_FN_TYPE hash; //哈希表的哈希函数,OpenSSL提供默认的哈希函数
unsigned int num_nodes;
unsigned int num_alloc_nodes; //b的长度
unsigned int p;
unsigned int pmax;
unsigned long up_load; /* load times 256 */ //装填因子的上限
unsigned long down_load; /* load times 256 */ //装填因子的下限
unsigned long num_items; //表中所存的数据的个数
/*下面的成员都是给各个操作计数用的,没什么好说的*/
unsigned long num_expands;
unsigned long num_expand_reallocs;
unsigned long num_contracts;
unsigned long num_contract_reallocs;
unsigned long num_hash_calls;
unsigned long num_comp_calls;
unsigned long num_insert;
unsigned long num_replace;
unsigned long num_delete;
unsigned long num_no_delete;
unsigned long num_retrieve;
unsigned long num_retrieve_miss;
unsigned long num_hash_comps;
int error;
} LHASH;
上面的结构体就是哈希表的结构体,要想理解OpenSSL中哈希表的实现,这个结构体中第一部分的成员每个的意义都必须理解才行。而这一部分的成员有一 些可以通过字面意思就知道它们做什么用的,但是还有3个是一两句注释说不清楚的,它们分别是:num_nodes,p,pmax。至于这3个成员分别什么 意思,一会儿下面再说,我们现在先看一下OpenSSL的哈希表是如何解决冲突的。
从上面两个结构体,我们可以大概看 出,OpenSSL的哈希表是把数据data的地址保存在结构LHASH_NODE中,然后用哈希函数计算出数据data对应的哈希值,再通过得到的哈希 值计算出保存数据地址的结构体在数组b中的下标,最后把结构体的地址保存到数组b相应的下标处。
不过,在哈希表中经常存在冲突:
1. 因为哈希函数的问题导致两个不同数据的哈希值相同
2. 因为两个不同数据的不同的哈希值,在计算在数组b中的下标时,得到的是相同的下标
以上这两个原因都导致两个不同的数据在数组b中发生冲突,那么如何解决冲突就成为一个不可避免的问题。
通常,哈希表中解决冲突的方法有下面几种:
1. 开放定址法