菜鳥nginx源碼剖析數據結構篇(三) 單向鏈表 ngx_list_tnginx
Author:Echo Chen(陳斌)數組
Email:chenb19870707@gmail.com數據結構
Blog:Blog.csdn.net/chen19870707app
Date:October 23h, 2014學習
ngx_list _t是一個順序容器,它其實是動態數組和單向鏈表的結合體,擴容起來比動態數組簡單的多,能夠一次擴容一個數組,因此說它結合了 鏈表插入刪除不須要移動的 和 數組下標快速索引 的優點,設計很是讓人叫絕,此外它還有如下特色:ui
鏈表中存儲的元素是靈活的,能夠是任何一種數據結構。spa
頭文件:http://trac.nginx.org/nginx/browser/nginx/src/core/ngx_list.h.net
源文件:http://trac.nginx.org/nginx/browser/nginx/src/core/ngx_list.c設計
前面說到ngx_list_t是一個數組鏈表,鏈表中的每一個結點都是一個數組,ngx_list_part_t 描述的是鏈表中的一個結點,這個結點又是一個數組,elts爲數組首地址,nelts爲該數組已經使用的個數,*next爲下一個鏈表結點的指針,定義以下:指針
1: typedef struct ngx_list_part_s ngx_list_part_t;
2:
3: //描述鏈表中的一個結點,這個結點又是一個數組
4: struct ngx_list_part_s {
5: void *elts; //首地址
6: ngx_uint_t nelts; //已經使用的個數
7: ngx_list_part_t *next; //下一個鏈表節點的指針
8: };
ngx_list_t是描述整個鏈表,其中last 爲鏈表中最後一個數組,part爲鏈表中首個數組,size爲數組每一個元素佔用空間小,nalloc爲每一個數組能夠容納的元素個數,pool爲鏈表內存池對象,定義以下:
1: typedef struct {
2: ngx_list_part_t *last; //鏈表中最後一個數組元素
3: ngx_list_part_t part; //鏈表中的首個數組元素
4: size_t size; //每一個數組元素佔用的空間大小
5: ngx_uint_t nalloc; //每一個數組結點的容量,即每一個數組最多能夠存放多少個元素
6: ngx_pool_t *pool; //鏈表中的內存池對象指針
7: } ngx_list_t;
其結構以下圖所示,最下面一行爲內存映像,能夠看到整個內存中僅僅多了 一個ngx_list_t 和 幾個ngx_list_part_s結果,內存並無太多的浪費,nginx在內存方面的苛刻確實值得咱們學習。
1: //鏈表建立,pool爲內存池對象,size爲每一個數組元素的大小,n爲每一個數組能夠容納的元素個數
2: ngx_list_t *ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size)
3: {
4: ngx_list_t *list;
5:
6: //分配ngx_list_t結構
7: list = ngx_palloc(pool, sizeof(ngx_list_t));
8: if (list == NULL) {
9: return NULL;
10: }
11:
12: //初始化
13: if (ngx_list_init(list, pool, n, size) != NGX_OK) {
14: return NULL;
15: }
16:
17: return list;
18: }
19:
20: //鏈表初始化,list爲鏈表結構(在create中建立),pool爲內存池,size爲每一個數組元素的大小,n爲每一個數組能夠容納的元素個數
21: static ngx_inline ngx_int_t ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size)
22: {
23: //分配一個數組,並用鏈表首結點指向它
24: list->part.elts = ngx_palloc(pool, n * size);
25: if (list->part.elts == NULL) {
26: return NGX_ERROR;
27: }
28:
29: //已使用個數爲0
30: list->part.nelts = 0;
31: //沒有下一個結點,next指爲NULL
32: list->part.next = NULL;
33: //最後一個結點也指向剛剛分配的數組
34: list->last = &list->part;
35: //數組每一個元素大小爲size
36: list->size = size;
37: //數組能容納元素個數爲n
38: list->nalloc = n;
39: //內存池爲pool
40: list->pool = pool;
41:
42: return NGX_OK;
43: }
能夠看到,ngx_list_init調用成功後,會建立一個數組結點。
1: //鏈表添加元素,l爲鏈表結構
2: void *ngx_list_push(ngx_list_t *l)
3: {
4: void *elt;
5: ngx_list_part_t *last;
6:
7: //last指向鏈表最後一個結點
8: last = l->last;
9:
10: //若是最後一個結點的數組已經滿了
11: if (last->nelts == l->nalloc) {
12:
13: /* the last part is full, allocate a new list part */
14:
15: //從內存池中分配一個ngx_list_part_t數組對象
16: last = ngx_palloc(l->pool, sizeof(ngx_list_part_t));
17: if (last == NULL) {
18: return NULL;
19: }
20:
21: //從內存池中給數組對象元素分配空間,每一個元素大小爲size,元素數目爲nalloc
22: last->elts = ngx_palloc(l->pool, l->nalloc * l->size);
23: if (last->elts == NULL) {
24: return NULL;
25: }
26:
27: //新分配的數組已使用結點個數爲0,下一個鏈表結點爲NULL
28: last->nelts = 0;
29: last->next = NULL;
30:
31: //將新分配的數組鏈到鏈表,並把鏈表尾指針指向新分配數組
32: l->last->next = last;
33: l->last = last;
34: }
35:
36: //elts爲數組首地址,elts + size * nelts 即位爲分配數組元素的位置
37: elt = (char *) last->elts + l->size * last->nelts;
38:
39: //分配個數+1
40: last->nelts++;
41:
42: return elt;
43: }
源代碼中只給了鏈表的插入操做,沒有刪除操做,看起來不完整,這裏猜想這個鏈表結構應該不會直接使用,而是被再次封裝。
看完源代碼,這個list設計仍是很巧妙的,結合了數組和鏈表各自的優點,確實不得不讓人拍手叫絕。下面嘗試給出ngx_list_t的使用例子:
1: int main()
2: {
3: ngx_list_t *pList = ngx_list_create( r->pool,4,sizeof( ngx_str_t));
4: if( NULL == pList)
5: {
6: return -1;
7: }
8:
9: ngx_str_t *str = ngx_list_push(pList);
10: if( NULL == str)
11: {
12: return -1;
13: }
14:
15: str->len = sizeof("This is a test for ngx_list_t");
16: str->value = "This is a test for ngx_list_t";
17:
18: //遍歷
19: ngx_list_part_t *part = &pList->part;
20: ngx_str_t *str = part->elts;
21:
22: for(int i = 0;;i++)
23: {
24: if( i >= part->nelts)
25: {
26: //已經遍歷完最後一個數組,沒有下一個結點了
27: if( NULL == part->next)
28: {
29: break;
30: }
31:
32: //還有下一個數組,part指到下一個數組,並把str指到新數組的地址
33: part = part->next;
34: str = part->elts;
35:
36: //重置計數器
37: i = 0;
38: }
39:
40: //輸出結點
41: cout << "list elemts :" << str[i] << endl;
42:
43: }