nginx學習六 高級數據結構之雙向鏈表ngx_queue_t

1 ngx_queue_t簡介

ngx_queue_t是nginx提供的一個輕量級的雙向鏈表容器,它不負責存儲數據,既不提供數據的內存分配,它只有兩個指針負責把數據鏈入鏈表,它跟stl提供的queue不同,stl提供的queue幫助用戶存儲數據,用戶只需要相容器裏添加數據即可,而ngx_queue_t,用戶必須自己提供存儲數據的內存,並且必須定義一種數據結構把ngx_queue_t包含在其中,然後利用ngx_queue_t提供的函數來進行相應的操作。

2 ngx_queue_t結構及其操作

2.1 ngx_queue_t

struct ngx_queue_s {
    ngx_queue_t  *prev;
    ngx_queue_t  *next;
};
ngx_queue_t只提供一個指向前驅和一個指向後繼的指針,結構非常簡單,這也是其能夠通用性的原因。

2.2 操作函數

ngx_queue_init(q)            //初始化鏈表
ngx_queue_empty(h)           //判斷鏈表是否爲空                                                   
ngx_queue_insert_head(h, x)  //在頭部插入一個元素                                       
ngx_queue_insert_after       //在h元素前面插入一個元素
ngx_queue_insert_tail(h, x)  //在h尾部插入一個元素 
ngx_queue_head(h)            //返回第一個元素
#define ngx_queue_last(h)    //返回最後一個元素 
ngx_queue_sentinel(h)        //返回鏈表容器結構體的指針
ngx_queue_next(q)            //返回下一個q的下一個元素  
ngx_queue_prev(q)            //返回q的前一個元素
ngx_queue_remove(x)          //刪除x結點                                           
ngx_queue_split(h, q, n)     //把h分爲兩個鏈表h和n,並且n的第一元素爲q
ngx_queue_add(h, n)          //把鏈表n增加到h鏈表的尾部
ngx_queue_data(q, type, link)//取出包含q的type類型的地址,這樣我們就可以訪問type內的成員

ngx_queue_t提供了14個常用的操作給用戶使用,基本涵蓋了插入、刪除、移動,訪問數據等等操作,這14個函數都是宏定義,有興趣的可以看下源碼,非常簡單。這裏說一個最後一個操作函數ngx_queue_data:

#define ngx_queue_data(q, type, link)                                         \
    (type *) ((u_char *) q - offsetof(type, link))

q爲ngx_queue_t的指針, type是用戶自定義的包含ngx_queue_t的數據類型type,link是type的成員,類型是ngx_queue_t。

這裏舉個列子來說明這個操作的用法。

先看一個自定義的結構體:

typedef struct
{
	ngx_int_t num;
	ngx_str_t str;
	ngx_queue_t queue;
}TestNode;

如果我們有一個ngx_queue_t的指針q指向testNode.queue,現在我們不知到testNode的地址,只知道queue,如果我們想訪問testNode裏面的成員num,我們必須知道testNode的地址,這樣才能訪問其num成員。怎樣知道testNode的地址呢?這時候ngx_queue_data就閃亮登場了。我們可以用一下語句來取得testNode的地址:

TestNode* testnode  = ngx_queue_data(q, TestNode, queue);
這樣我們就可以訪問num了。
2.3 ngx_queue_middle

這個函數取出鏈表中間位置的節點。用一個慢指針midlle和一個快指針next,middle沒走一步,next走兩步,當next指針到達鏈表未的時候,midlle就指向鏈表的中間位置。


ngx_queue_t *
ngx_queue_middle(ngx_queue_t *queue)
{
    ngx_queue_t  *middle, *next;

    middle = ngx_queue_head(queue);

    if (middle == ngx_queue_last(queue)) {
        return middle;
    }

    next = ngx_queue_head(queue);

    for ( ;; ) {//middle每前進 一步,next都要前進兩步,直到鏈表的尾部
        middle = ngx_queue_next(middle);

        next = ngx_queue_next(next);

        if (next == ngx_queue_last(queue)) {
            return middle;
        }

        next = ngx_queue_next(next);

        if (next == ngx_queue_last(queue)) {
            return middle;
        }
    }
}

2.4 ngx_ngx_queue_sort

void
ngx_queue_sort(ngx_queue_t *queue,
    ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *))
{
    ngx_queue_t  *q, *prev, *next;

    q = ngx_queue_head(queue);

    if (q == ngx_queue_last(queue)) {
        return;
    }

    //遍歷鏈表中的每一個元素,然後遍歷它前面的元素是否比它大,直到找到不比它大第一個元素,然後插入。
    for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = next) {

        prev = ngx_queue_prev(q);
        next = ngx_queue_next(q);

        ngx_queue_remove(q);

        do {//遍歷它前面的元素
            if (cmp(prev, q) <= 0) {
                break;
            }

            prev = ngx_queue_prev(prev);

        } while (prev != ngx_queue_sentinel(queue));

        ngx_queue_insert_after(prev, q);//q前面的元素必須是小於q
    }
}

3 一個例子

#include <stdio.h>
#include <stdlib.h>
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_conf_file.h>
#include <nginx.h>
#include <ngx_queue.h>

//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
//這兩個東東必須寫,不爲有編譯錯誤
volatile ngx_cycle_t  *ngx_cycle;

void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
            const char *fmt, ...)
{
}
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////

typedef struct
{
	ngx_int_t num;
	ngx_str_t str;
	ngx_queue_t queue;
}TestNode;

ngx_int_t compare_node(const ngx_queue_t *left, const ngx_queue_t *right)
{
	TestNode* left_node  = ngx_queue_data(left, TestNode, queue);
	TestNode* right_node = ngx_queue_data(right, TestNode, queue);
	
	return left_node->num > right_node->num;
}


int main()
{
    ngx_queue_t QueHead;
	ngx_queue_init(&QueHead);

	TestNode Node[10];
	ngx_int_t i;
	for (i=0; i<10; ++i)
	{
		Node[i].num = rand()%100;
	}

    ngx_queue_insert_head(&QueHead, &Node[0].queue);
	ngx_queue_insert_tail(&QueHead, &Node[1].queue);
	ngx_queue_insert_after(&QueHead, &Node[2].queue);
    ngx_queue_insert_head(&QueHead, &Node[4].queue);
	ngx_queue_insert_tail(&QueHead, &Node[3].queue);
    ngx_queue_insert_head(&QueHead, &Node[5].queue);
	ngx_queue_insert_tail(&QueHead, &Node[6].queue);
	ngx_queue_insert_after(&QueHead, &Node[7].queue);
    ngx_queue_insert_head(&QueHead, &Node[8].queue);
	ngx_queue_insert_tail(&QueHead, &Node[9].queue);

	ngx_queue_t *q;
	for (q = ngx_queue_head(&QueHead); q != ngx_queue_sentinel(&QueHead); q = ngx_queue_next(q))
	{
		TestNode* Node = ngx_queue_data(q, TestNode, queue);
		printf("Num=%d\n", Node->num);
	}

    ngx_queue_sort(&QueHead, compare_node);
	
    printf("\n");
	for (q = ngx_queue_head(&QueHead); q != ngx_queue_sentinel(&QueHead); q = ngx_queue_next(q))
	{
		TestNode* Node = ngx_queue_data(q, TestNode, queue);
		printf("Num=%d\n", Node->num);
	}
    
	return 0;
}

http://blog.csdn.net/xiaoliangsky/article/details/39646141
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章