概述
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使用紅黑樹代替連結串列等等。
空空如常
求真得真