概述

freeswitch的核心源代码是基于apr库开发的,在不同的系统上有很好的移植性。

哈希表在开发中应用的非常广泛,主要场景是对查询效率要求较高的逻辑,是典型的空间换时间的数据结构实现。

大多数的底层库有各自的哈希表实现方法,那么apr库中对于哈希表究竟是如何实现的呢,其中有什么优点和缺点?

下面我们对apr库的哈希表实现做一个介绍。

环境

centos:CentOS  release 7.0 (Final)或以上版本

freeswitch:v1.8.7

GCC:4.8.5

哈希表数据结构

apr库的哈希表源代码文件在libs/apr目录下。

libs\apr\include\apr_hash.h

libs\apr\tables\apr_hash.c

哈希表结构体定义在apr_hash.c中。

struct apr_hash_entry_t {

apr_hash_entry_t *next;

unsigned int      hash;

const void       *key;

apr_ssize_t       klen;

const void       *val;

};

struct apr_hash_index_t {

apr_hash_t         *ht;

apr_hash_entry_t   *this, *next;

unsigned int        index;

};

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 */

};

常用函数

查看源代码头文件libs\apr\include\apr_hash.h。

apr_hashfunc_default          //默认hash函数

apr_hash_make                   //创建hash表

apr_hash_make_custom      //创建hash表,自定义hash函数

apr_hash_copy                    //复制hash表

apr_hash_set                       //hash表插入一个新的键值对

apr_hash_get                      //hash表查找key对应的value

apr_hash_first                     //hash表遍历,第一个节点

apr_hash_next                     //hash表遍历,下一个节点

apr_hash_this                      //hash表遍历,获取当前节点内容

apr_hash_count                  //hash表键值对计数

apr_hash_clear                    //清空所有键值对

apr_hash_overlay                //合并俩个hash表

apr_hash_merge                 //合并俩个hash表,自定义冲突处理函数

apr_hashfunc_default默认哈希函数

apr库哈希表默认的哈希函数使用times33哈希算法,times33算法很简单,就是不断的乘33。

对于字符串类型的key,times33算法很好用,计算很快,hash结果分布均匀。

核心代码如下:

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;

}

}

apr_hash_make创建

apr库哈希表的创建函数。

1, 从内存池分配apr_hash_t大小的内存。

2, 字段初始化,包括内存池指针赋值,free指针设置为NULL,count设置为0,max为INITIAL_MAX(15), array指针数组内存分配,hash函数使用apr_hashfunc_default。

apr_hash_set插入

apr库哈希表,插入键值对。

1, 查找。根据key计算hash值,并在hash值对应的array[hash & ht->max]下查找key,key已存在则返回当前entry。key不存在,优先从free链表中获取entry,否则新建entry并返回。

2, 检查获取到的entry。如果传入的val值为空,则将该entry删除并加入free链表。传入的val值正常,则替换entry中的val字段。

3, 检查count计数超过max时,扩展哈希表。

4, 扩展哈希表时,创建new_array数组,new_max大小为(max * 2 + 1)。遍历array,赋值到new_array并切换array数组。

apr_hash_get查找

apr哈希表查找的逻辑:

根据key计算hash值,并在hash值对应的array[hash & ht->max]下查找key,key存在则返回当前entry中的val值。key不存在则返回NULL。

apr_hash_clear清空

apr哈希表清空的逻辑:

遍历hash表,把所有的entry节点中的val设置为NULL。

所以,apr_hash_clear之后,hash表并没有清空,只是把所有的entry节点的val置空,并将entry放入了free链表。

总结

apr库的哈希表对于大部分场景已经足够使用了。但是有一些特殊的场景要考虑到出问题的可能。

apr库哈希表不是线程安全的。

apr库哈希表没有对冲突做进一步处理,在array[i]下的entry链表长度超过某个阈值时,通过某些方法降低冲突,比如扩容、修改hash算法、entry使用红黑树代替链表等等。


空空如常

求真得真

freeswitch APR库哈希表的更多相关文章

  1. freeswitch APR库

    概述 freeswitch依赖库源代码基本都可以在libs目录下找到. 在freeswitch的官方手册中,可以找到freeswitch的依赖库表格,其中freeswitch的core核心代码依赖库主 ...

  2. freeswitch APR库线程读写锁

    概述 freeswitch的核心源代码是基于apr库开发的,在不同的系统上有很好的移植性. 线程读写锁在多线程服务中有重要的作用.对于读数据比写数据频繁的服务,用读写锁代替互斥锁可以提高效率. 由于A ...

  3. 简单的哈希表实现 C语言

    简单的哈希表实现 简单的哈希表实现 原理 哈希表和节点数据结构的定义 初始化和释放哈希表 哈希散列算法 辅助函数strDup 哈希表的插入和修改 哈希表中查找 哈希表元素的移除 哈希表打印 测试一下 ...

  4. C++ STL中哈希表Map 与 hash_map 介绍

    0 为什么需要hash_map 用过map吧?map提供一个很常用的功能,那就是提供key-value的存储和查找功能.例如,我要记录一个人名和相应的存储,而且随时增加,要快速查找和修改: 岳不群-华 ...

  5. python 全栈开发,Day61(库的操作,表的操作,数据类型,数据类型(2),完整性约束)

    昨日内容回顾 一.回顾 定义:mysql就是一个基于socket编写的C / S架构的软件 包含: ---服务端软件 - socket服务端 - 本地文件操作 - 解析指令(mysql语句) ---客 ...

  6. 库的操作&表的操作

    一 库的操作 掌握库的增删改查 一.系统数据库 执行如下命令,查看系统库 show databases; information_schema: 虚拟库,不占用磁盘空间,存储的是数据库启动后的一些参数 ...

  7. stl vector、红黑树、set、multiset、map、multimap、迭代器失效、哈希表(hash_table)、hashset、hashmap、unordered_map、list

    stl:即标准模板库,该库包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法 六大组件: 容器.迭代器.算法.仿函数.空间配置器.迭代适配器 迭代器:迭代器(iterator)是一种抽象的设计 ...

  8. mysql更新(三)语句 库的操作 表的操作

    04-初始mysql语句   本节课先对mysql的基本语法初体验. 操作文件夹(库) 增 create database db1 charset utf8; 查 # 查看当前创建的数据库 show ...

  9. MySQL常见的库操作,表操作,数据操作集锦及一些注意事项

    一 库操作(文件夹) 1 数据库命名规则 可以由字母.数字.下划线.@.#.$ 区分大小写 唯一性 不能使用关键字如 create select 不能单独使用数字 最长128位 2 数据库相关操作 创 ...

随机推荐

  1. web全栈后台权限管理系统(VUE+ElementUi+nodeJs+koa2)

    web全栈后台权限管理系统(VUE+ElementUi+nodeJs+koa2) 主要技术 前端 vue 全家桶 ElementUI 后端 Node.js Koa2 Mongoess 数据库 mong ...

  2. C语言中while 语句

    while的执行顺序 while 循环的执行顺序非常简单,它的格式是: while (表达式) { 语句: } 概念:当表达式为真,则执行下面的语句:语句执行完之后再判断表达式是否为真,如果为真,再次 ...

  3. 【UE4 设计模式】设计模式一些概念

    定义 设计模式是一套被反复使用的.多数人知晓的.经过分类编目的.代码设计经验的总结. 使用设计模式是为了重用代码.让代码更容易被他人理解.保证代码可靠性. 四人帮 GOF ( Gang of Four ...

  4. Vue CLI 5 和 vite 创建 vue3.x 项目以及 Vue CLI 和 vite 的区别

    这几天进入 Vue CLI 官网,发现不能选择 Vue CLI 的版本,也就是说查不到 vue-cli 4 以下版本的文档. 如果此时电脑上安装了 Vue CLI,那么旧版安装的 vue 项目很可能会 ...

  5. 聊聊 Kubernetes Pod or Namespace 卡在 Terminating 状态的场景

    这个话题,想必玩过kubernetes的同学当不陌生,我会分Pod和Namespace分别来谈. 开门见山,为什么Pod会卡在Terminationg状态? 一句话,本质是API Server虽然标记 ...

  6. mybatis自定义分页拦截器

    最近看了一下项目中代码,发现系统中使用的mybatis分页使用的是mybatis自带的分页,即使用RowBounds来进行分页,而这种分页是基于内存分页,即一次查出所有的数据,然后再返回分页需要的数据 ...

  7. NOIP模拟84(多校17)

    T1 宝藏 解题思路 考场上一眼出 \(nlog^2\) 做法,然后没看见是 1s 3e5 的数据,我竟然以为自己切了?? 考完之后尝试着把二分改为指针的移动,然后就过了??或许是数据水吧,感觉自己的 ...

  8. Linux该如何学习新手入门遇到问题又该如何解决

    本节旨在介绍对于初学者如何学习 Linux 的建议.如果你已经确定对 Linux 产生了兴趣,那么接下来我们介绍一下学习 Linux 的方法. 如何去学习 学习大多类似庖丁解牛,对事物的认识一般都是由 ...

  9. 常用JAVA API :HashSet 和 TreeSet

    set容器的特点是不包含重复元素,也就是说自动去重. HashSet HashSet基于哈希表实现,无序. add(E e)//如果容器中不包含此元素,则添加. clear()//清空 contain ...

  10. 21.7.31 test

    \(NOIP\) 测试 好久没有这种感觉能阿克的冲动了!但还是挂了分 T1 WOJ2608(模拟,拓扑排序) 签到题,直接模拟,有点像拓扑排序. 要给点打标记不然可能被某次操作中弹出多次该点导致WA ...