freeswitch APR庫雜湊表

求真得真發表於2021-12-01

 

 

 

概述

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使用紅黑樹代替連結串列等等。

 

 


 

空空如常

求真得真

 

相關文章