`
baiguomeng
  • 浏览: 964436 次
文章分类
社区版块
存档分类
最新评论

Apache中的哈希表剖析(1)

 
阅读更多
转载请注明来源:http://blog.csdn.net/tingya
3.4 哈希表
3.4.1哈希表概述
作为线性数据结构,与前面所说的表格和队列等相比,哈希表无疑是查找速度比较快的一种。APR中较好的支持哈希表。APR中哈希表在文件apr_hash.h和apr_hash.c中实现。其数据结构定义如下:
struct apr_hash_t {–
apr_pool_t *pool;
apr_hash_entry_t **array;
apr_hash_index_t iterator;/* For apr_hash_first(NULL, ...) */
unsigned int count, max;
apr_hashfunc_t hash_func;
apr_hash_entry_t *free;/* List of recycled entries */
};
与其余的数据结构类似,第一个成员通常是分配该结构的内存池。哈希表中的每一个元素都用apr_hash_entry_t结构描述,array指向一个数组,该数组中每一个元素都是一个指向apr_hash_entry_t类型的链表。为了方便对哈希表的迭代循环遍历,每个结构中都维持一个apr_hash_index_t用以辅助迭代,该成员的详细细节我们在后面的部分会深入讨论。
count用以记录当前整个哈希表中结点的总数目。max则是当前哈希表中允许的哈希值的最大值,反映到结构中就是array数组中的元素的个数。
hash_func则是哈希函数,通过该函数可以确定给定元素在哈希表中的索引,可以使用默认的哈希函数,也可以使用自定义的哈希函数。
现在我们来看一下哈希表元素数据结构apr_hash_entry_t,该结构定义如下:
struct apr_hash_entry_t {
apr_hash_entry_t *next;
unsigned int hash;
const void *key;
apr_ssize_tklen;
const void *val;
};
hash是当前元素在哈希表中所对应的哈希索引值,key则是哈希项的键,value则是哈希项的值。哈希表中以键值key作为唯一的标识索引。如果键值存在冲突,那么这些冲突键将用链表关联起来。整个哈希表的结构可以用下面的图描述:
3.4.2哈希表创建
3.4.2.1 零创建
APR中创建一个哈希表可以通过两种途径:apr_hash_make和apr_hash_make_custom实现:
APR_DECLARE(apr_hash_t *) apr_hash_make(apr_pool_t *pool);
APR_DECLARE(apr_hash_t *) apr_hash_make_custom(apr_pool_t *pool, apr_hashfunc_t hash_func);
两者的区别就是哈希算法的不同。对于apr_hash_make而言,它的主要的工作就是创建apr_hash_t结构,并对其中的成员进行初始化,其中哈希元素的个数被初始化为16个,同时使用默认的哈希算法apr_hashfunc_default,而apr_hash_make_custom则使用自定义的哈希函数hash_func。
unsigned int apr_hashfunc_default(const char *char_key, apr_ssize_t *klen)
{
unsigned int hash = 0;
const unsigned char *key = (const unsigned char *)char_key;
const unsigned char *p;
apr_ssize_t i;
if (*klen == APR_HASH_KEY_STRING) {
for (p = key; *p; p++) {
hash = hash * 33 + *p;
}
*klen = p - key;
}
else {
for (p = key, i = *klen; i; i--, p++) {
hash = hash * 33 + *p;
}
}
return hash;
}
对于给定的键值key,apr_hashfunc_default返回它在哈希表中的索引。默认哈希算法采用了目前最为流行的times 33哈希算法,目前该算法被广泛使用在多个软件项目包括perl和巴克利(Berkeley DB)数据库中。对于字符串而言这是目前所知道的最好的哈希算法,原因在于该算法的速度非常快,而且分类非常好。
不过你不愿意使用该索引算法而希望使用自己的,那么你可以使用apr_hash_make_custom函数,它接受一个自定义的哈希算法函数,并将其赋值给apr_hash_t结构内的func成员,从而取代默认算法函数。
apr_hashfunc_t函数指针定义如下:
typedef unsigned int (*apr_hashfunc_t)(const char *key, apr_ssize_t *klen);
它需要两个参数,一个是需要进行哈希计算的键,另一个则是该键的长度。函数返回计算后的索引。
3.4.2.2 拷贝创建
与大部分数据结构一样,对于哈希表,APR也提供了拷贝创建方法,允许从一个已有的哈希表创建一个新的哈希表,拷贝函数声明如下:
APR_DECLARE(apr_hash_t *) apr_hash_copy(apr_pool_t *pool,const apr_hash_t *orig)
orig是源哈希表,在拷贝中所用所有的内存都来自内存池pool,拷贝后的哈希表由函数返回。
APR_DECLARE(apr_hash_t *) apr_hash_copy(apr_pool_t *pool,const apr_hash_t *orig)
{
apr_hash_t *ht;
apr_hash_entry_t *new_vals;
unsigned int i, j;
ht = apr_palloc(pool, sizeof(apr_hash_t) +
sizeof(*ht->array) * (orig->max + 1) +
sizeof(apr_hash_entry_t) * orig->count);
ht->pool = pool;
ht->free = NULL;
ht->count = orig->count;
ht->max = orig->max;
ht->hash_func = orig->hash_func;
ht->array = (apr_hash_entry_t **)((char *)ht + sizeof(apr_hash_t));
new_vals = (apr_hash_entry_t *)((char *)(ht) + sizeof(apr_hash_t) +
sizeof(*ht->array) * (orig->max + 1));
尽管称之为哈希表拷贝,但是apr_hash_copy实现的仅仅是一种影像拷贝。之所以称之为影像拷贝,是因为尽管拷贝后的哈希表能够实现与源哈希表相同的功能,但是内部数据组织已经发生了变化,最大的变化就是从源哈希表的不连续的链表结构转换为连续的块状结构。
既然是块状数据结构,我们首先就必须考虑块状结构的大小,然后才能分配。新的块状结构的大小应该与原有的链表结构大小相等。总的大小包括三方面:
1)、apr_hash_t结构的大小sizeof(apr_hash_t)
2)、apr_hash_t结构内array数组的大小,数组的总元素个数为max+1,每一个元素都是一个 apr_hash_entry_t类型的指针,因此整个数组的大小为(orig->max+1)*sizeof(*ht->array),或者也可以写成(orig->max+1)*sizeof(apr_hash_entry_t*)。
3)、整个哈希表中apr_hash_entry_t类型结点的总数,其值由orig->count决定,每个结点的大小为sizeof(apr_hash_entry_t),故总大小为sizeof(apr_hash_entry_t) * orig->count。
一旦确定了总的需要分配的内存大小,APR将从内存池中一次性分配足够的连续内存。这些内存将被分为三部分:apr_hash_t部分、max+1个apr_hash_entry_t指针部分以及count个哈希元素的大小。因此内存一旦分配完毕,除了使用原有哈希表结构成员初始化新的哈希表成员包括pool、count、max以及hash_func等之外,最重要的就是设置指针指向后面的两个内存部分,初始化后的布局如下所示:
1. j =0;
2. for (i = 0; i <= ht->max; i++) {
3. apr_hash_entry_t **new_entry = &(ht->array[i]); u
4. apr_hash_entry_t *orig_entry = orig->array[i];
5. while (orig_entry) {
6. *new_entry = &new_vals[j++];
7. (*new_entry)->hash = orig_entry->hash;
8. (*new_entry)->key = orig_entry->key;
9. (*new_entry)->klen = orig_entry->klen;
10. (*new_entry)->val = orig_entry->val;
11. new_entry = &((*new_entry)->next);v
12. orig_entry = orig_entry->next;w
13. }
14. *new_entry = NULL;
15. }
16. return ht;
在源哈希表中,ht->array数组中的每一个元素都是一个apr_hash_entry_t类型的指针,指向的是一个链表,而到了目标哈希表中,该指针指向的则是一个数组。因此,拷贝的一个重要任务就是调整array数组中的各个指针将其指向new_vals内存的适当的部分。调整后的布局如下图所示。
调整过程包括三个大步骤,图示中用jkl进行标识:
j array数组中的每一个元素都是一个指针,必须调整数组中的每一个指针指向new_vals部分的合适的位置。原则是:如果源哈希表中对应元素为NULL,则新指针也为NULL;如果对应的结点链表结点数目为n,则下一个索引指针与前一个偏移n*sizeof(apr_hash_entry_t)个。具体如灰色代码部分所示。
k 对每一个结点进行内容拷贝,如7.8.9.10行。
l 调整各个结点内部的next指针,指向它的直接后继结点,或者是紧靠它的下一个apr_hash_entry_t结点,或者是NULL。
new_entry = &((*new_entry)->next);

在上图中我们用虚线将链表结构和块状结构中对应得结点连接起来,以方便对比。

关于作者
张中庆,目前主要的研究方向是嵌入式浏览器,移动中间件以及大规模服务器设计。目前正在进行Apache的源代码分析,计划出版《Apache源代码全景分析》上下册。Apache系列文章为本书的草案部分,对Apache感兴趣的朋友可以通过flydish1234 at sina.com.cn与之联系!

如果你觉得本文不错,请点击文后的“推荐本文”链接!!
分享到:
评论

相关推荐

    基于Pytorch实现的语音情感识别源代码+使用说明文档(高分项目)

    基于Pytorch实现的语音情感识别源代码+使用说明文档(高分项目) 基于Pytorch实现的语音情感识别源代码+使用说明文档 使用准备 Anaconda 3 Python 3.8 Pytorch 1.13.1 Windows 10 or Ubuntu 18.04 模型测试表 模型 Params(M) 预处理方法 数据集 类别数量 准确率 BidirectionalLSTM 1.8 Flank RAVDESS 8 0.78 说明: RAVDESS数据集只使用Audio_Speech_Actors_01-24.zip 安装环境 首先安装的是Pytorch的GPU版本,如果已经安装过了,请跳过。 conda install pytorch==1.13.1 torchvision==0.14.1 torchaudio==0.13.1 pytorch-cuda=11.6 -c pytorch -c nvidia基于Pytorch实现的语音情感识别源代码+使用说明文档(高分项目)基于Pytorch实现的语音情感识别源代码+使用说明文档(高分项目)基于Pytorch实现的语音情感识别源代码+

    cryptography-1.3.1-pp226-pp226u-macosx_10_10_x86_64.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    一个摄像头相关的工具箱,使用C语言写成.zip

    一个摄像头相关的工具箱,使用C语言写成

    pyzmq-25.1.2-cp36-cp36m-win_amd64.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    grpcio-1.2.0-cp36-cp36m-win_amd64.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    aiohttp-3.6.1-cp37-cp37m-manylinux1_x86_64.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    pyzmq-25.1.1b2-cp38-cp38-macosx_10_9_x86_64.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    使用c语言实现的死锁检测组件.zip

    使用c语言实现的死锁检测组件

    数字化高校安全解决方案(70页).pptx

    数字化高校安全解决方案(70页)

    俄罗斯方块的JAVA实现

    俄罗斯方块的JAVA实现——适用于JAVA程序的大作业

    苹果cms电影一号影视网站模板

    苹果cms影视网站模板——电影一号一款含有简约风格,极速加载,电脑手机多屏兼容能力强,布局美观为一身的多功能模板。模板无授权功能,无域名限制。

    【雷达成像】基于matlab SAR合成孔径雷达图像点目标【含Matlab源码 4670期】.mp4

    Matlab研究室上传的视频均有对应的完整代码,皆可运行,亲测可用,适合小白; 1、代码压缩包内容 主函数:main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2019b;若运行有误,根据提示修改;若不会,私信博主; 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开main.m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可私信博主或扫描视频QQ名片; 4.1 博客或资源的完整代码提供 4.2 期刊或参考文献复现 4.3 Matlab程序定制 4.4 科研合作

    grpcio-1.8.1-cp34-cp34m-win32.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    aiohttp-3.8.5-cp38-cp38-musllinux_1_1_s390x.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    一个C语言的开发工具包。.zip

    一个C语言的开发工具包。

    教育行业数字化高校安全管理平台解决方案_2.pptx

    教育行业数字化高校安全管理平台解决方案_2

    AdaLoRA Adaptive Budget Allocation for PEFT.pdf

    AdaLoRA Adaptive Budget Allocation for PEFT.pdf

    STM32-HAL-CAN 查询式 回环模式 实现收发数据

    STM32-HAL-CAN 查询式 回环模式 实现收发数据

    aiohttp-3.8.0a7-cp36-cp36m-musllinux_1_1_i686.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    jigsawPuzzleGame.zip

    jigsawPuzzleGame.zip

Global site tag (gtag.js) - Google Analytics