物聯網安全himqtt防火牆數據結構之紅黑樹源碼分析

 

 

物聯網安全himqtt防火牆數據結構之紅黑樹源碼分析node

 

隨着5G的發展,物聯網安全顯得特別重要,himqtt是首款完整源碼的高性能MQTT物聯網防火牆 - MQTT Application FireWallC語言編寫,採用epoll模式支持IoT數十萬的高併發鏈接,而且兼容ModSecurity部分規則。 代碼很是優秀,很是值得收藏和學習。linux

紅黑樹是一種特殊的自平衡二叉樹數據結構,其自旋和左旋乃天才的設計,linux內核中大量使用。阿里巴巴面試很是看重紅黑樹,由於從幾十萬的數據中,用短短的幾步就可能查到所要的數據,其原理教程網上不少,今天咱們從首款物聯網防火牆himqtt裏面上分析紅黑樹源碼。nginx

首先在github上下載himqtt的源碼:https://github.com/qq4108863/himqttgit

找到src目錄下的mqtt_rbtree.cmqtt_rbtree.h文件,結構圖以下:程序員

 

 

 

 

一、結構體

 

typedef ngx_uint_t  ngx_rbtree_key_t;github

typedef ngx_int_t   ngx_rbtree_key_int_t;面試

/* 紅黑樹節點結構 */編程

typedef struct ngx_rbtree_node_s  ngx_rbtree_node_t;安全

 

struct ngx_rbtree_node_s {數據結構

    ngx_rbtree_key_t       key;     /* 節點的鍵值 */

    ngx_rbtree_node_t     *left;    /* 節點的左孩子 */

    ngx_rbtree_node_t     *right;   /* 節點的右孩子 */

    ngx_rbtree_node_t     *parent;  /* 節點的父親 */

    u_char                 color;   /* 節點的顏色 */

    u_char                 data;    /* */};

 

typedef struct ngx_rbtree_s  ngx_rbtree_t;

 

typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,

    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);

/* 紅黑樹結構 */

struct ngx_rbtree_s {

    ngx_rbtree_node_t     *root;    /* 指向樹的根節點 */

    ngx_rbtree_node_t     *sentinel;/* 指向樹的葉子節點NIL */

    ngx_rbtree_insert_pt   insert;  /* 添加元素節點的函數指針,解決具備相同鍵值,但不一樣顏色節點的衝突問題;

                                     * 該函數指針決定新節點的行爲是新增仍是替換原始某個節點*/};

 

二、初始化

/* 給節點着色,1表示紅色,0表示黑色  */

#define ngx_rbt_red(node)               ((node)->color = 1)

#define ngx_rbt_black(node)             ((node)->color = 0)/* 判斷節點的顏色 */

#define ngx_rbt_is_red(node)            ((node)->color)

#define ngx_rbt_is_black(node)          (!ngx_rbt_is_red(node))/* 複製某個節點的顏色 */

#define ngx_rbt_copy_color(n1, n2)      (n1->color = n2->color)

/* 節點着黑色的宏定義 *//* a sentinel must be black */

 

#define ngx_rbtree_sentinel_init(node)  ngx_rbt_black(node)

/* 初始化紅黑樹,即爲空的紅黑樹 *//* tree 是指向紅黑樹的指針,

 * s 是紅黑樹的一個NIL節點,

 * i 表示函數指針,決定節點是新增仍是替換

 */

#define ngx_rbtree_init(tree, s, i)                                           \

    ngx_rbtree_sentinel_init(s);                                              \

    (tree)->root = s;                                                         \

    (tree)->sentinel = s;                                                     \

    (tree)->insert = i

 

 

三、天才的左旋和右旋

/* 左旋轉操做 */

static ngx_inline voidngx_rbtree_left_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,

    ngx_rbtree_node_t *node){

    ngx_rbtree_node_t  *temp;

 /*

       |                       |

       x                       y

      / \                     / \

     a   y     ---left--->   x   c

        / \                 / \

       b   c               a   b

     */

/*node = x*/

    temp = node->right;/* temp爲node節點的右孩子 */

    node->right = temp->left;/* 設置node節點的右孩子爲temp的左孩子 */

 

    if (temp->left != sentinel) {

        temp->left->parent = node;

    }

 

    temp->parent = node->parent;

 

    if (node == *root) {

        *root = temp;

 

    } else if (node == node->parent->left) {

        node->parent->left = temp;

 

    } else {

        node->parent->right = temp;

    }

 

    temp->left = node;

node->parent = temp;}

 

/* 旋轉操做 */

static ngx_inline voidngx_rbtree_right_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,

    ngx_rbtree_node_t *node){

    ngx_rbtree_node_t  *temp;

   /*

       |                       |

       x                       y

      / \                     / \

     a   y     <---right--   x   c

        / \                 / \

       b   c               a   b

     */

    /*node = y*/

    temp = node->left;

    node->left = temp->right;

 

    if (temp->right != sentinel) {

        temp->right->parent = node;

    }

 

    temp->parent = node->parent;

 

    if (node == *root) {

        *root = temp;

 

    } else if (node == node->parent->right) {

        node->parent->right = temp;

 

    } else {

        node->parent->left = temp;

    }

 

    temp->right = node;

    node->parent = temp;}

 

/* 獲取紅黑樹鍵值最小的節點 */

static ngx_inline ngx_rbtree_node_t *ngx_rbtree_min(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel){

    while (node->left != sentinel) {

        node = node->left;

    }

 

    return node;}

/* 插入節點 *//* 插入節點的步驟:

 * 一、首先按照二叉查找樹的插入操做插入新節點;

 * 二、而後把新節點着色爲紅色(避免破壞紅黑樹性質5);

 * 三、爲維持紅黑樹的性質,調整紅黑樹的節點(着色並旋轉),使其知足紅黑樹的性質;

 */

 

4、插入值

voidngx_rbtree_insert(ngx_thread_volatile ngx_rbtree_t *tree,

    ngx_rbtree_node_t *node){

    ngx_rbtree_node_t  **root, *temp, *sentinel;

 

    /* a binary tree insert */

 

    root = (ngx_rbtree_node_t **) &tree->root;

    sentinel = tree->sentinel;

 

    /* 若紅黑樹爲空,則比較簡單,把新節點做爲根節點,

     * 並初始化該節點使其知足紅黑樹性質

     */

    if (*root == sentinel) {

        node->parent = NULL;

        node->left = sentinel;

        node->right = sentinel;

        ngx_rbt_black(node);

        *root = node;

 

        return;

    }

 

    /* 若紅黑樹不爲空,則按照二叉查找樹的插入操做進行

     * 該操做由函數指針提供

     */

    tree->insert(*root, node, sentinel);

 

    /* re-balance tree */

 

    /* 調整紅黑樹,使其知足性質,

     * 其實這裏只是破壞了性質4:若一個節點是紅色,則孩子節點都爲黑色;

     * 若破壞了性質4,則新節點 node 及其父親節點 node->parent 都爲紅色;

     */

    while (node != *root && ngx_rbt_is_red(node->parent)) {

 

        /* 若node的父親節點是其祖父節點的左孩子 */

        if (node->parent == node->parent->parent->left) {

            temp = node->parent->parent->right;/* temp節點爲node的叔叔節點 */

 

            /* case1:node的叔叔節點是紅色 */

            /* 此時,node的父親及叔叔節點都爲紅色;

             * 解決辦法:將node的父親及叔叔節點着色爲黑色,將node祖父節點着色爲紅色;

             * 而後沿着祖父節點向上判斷是否會破會紅黑樹的性質;

             */

            if (ngx_rbt_is_red(temp)) {

                ngx_rbt_black(node->parent);

                ngx_rbt_black(temp);

                ngx_rbt_red(node->parent->parent);

                node = node->parent->parent;

 

            } else {

                /* case2:node的叔叔節點是黑色且node是父親節點的右孩子 */

                /* 則此時,以node父親節點進行左旋轉,使case2轉變爲case3;

                 */

                if (node == node->parent->right) {

                    node = node->parent;

                    ngx_rbtree_left_rotate(root, sentinel, node);

                }

 

                /* case3:node的叔叔節點是黑色且node是父親節點的左孩子 */

                /* 首先,將node的父親節點着色爲黑色,祖父節點着色爲紅色;

                 * 而後以祖父節點進行一次右旋轉;

                 */

                ngx_rbt_black(node->parent);

                ngx_rbt_red(node->parent->parent);

                ngx_rbtree_right_rotate(root, sentinel, node->parent->parent);

            }

 

        } else {/* 若node的父親節點是其祖父節點的右孩子 */

            /* 這裏跟上面的狀況是對稱的,就再也不進行講解了

             */

            temp = node->parent->parent->left;

 

            if (ngx_rbt_is_red(temp)) {

                ngx_rbt_black(node->parent);

                ngx_rbt_black(temp);

                ngx_rbt_red(node->parent->parent);

                node = node->parent->parent;

 

            } else {

                if (node == node->parent->left) {

                    node = node->parent;

                    ngx_rbtree_right_rotate(root, sentinel, node);

                }

 

                ngx_rbt_black(node->parent);

                ngx_rbt_red(node->parent->parent);

                ngx_rbtree_left_rotate(root, sentinel, node->parent->parent);

            }

        }

    }

 

    /* 根節點必須爲黑色 */

    ngx_rbt_black(*root);}

/* 這裏只是將節點插入到紅黑樹中,並無判斷是否知足紅黑樹的性質;

 * 相似於二叉查找樹的插入操做,這個函數爲紅黑樹插入操做的函數指針;

 */

voidngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,

    ngx_rbtree_node_t *sentinel){

    ngx_rbtree_node_t  **p;

 

    for ( ;; ) {

 

        /* 判斷node節點鍵值與temp節點鍵值的大小,以決定node插入到temp節點的左子樹仍是右子樹 */

        p = (node->key < temp->key) ? &temp->left : &temp->right;

 

        if (*p == sentinel) {

            break;

        }

 

        temp = *p;

    }

 

    /* 初始化node節點,並着色爲紅色 */

    *p = node;

    node->parent = temp;

    node->left = sentinel;

    node->right = sentinel;

    ngx_rbt_red(node);}

 

voidngx_rbtree_insert_timer_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,

    ngx_rbtree_node_t *sentinel){

    ngx_rbtree_node_t  **p;

 

    for ( ;; ) {

 

        /*

         * Timer values

         * 1) are spread in small range, usually several minutes,

         * 2) and overflow each 49 days, if milliseconds are stored in 32 bits.

         * The comparison takes into account that overflow.

         */

 

        /*  node->key < temp->key */

 

        p = ((ngx_rbtree_key_int_t) (node->key - temp->key) < 0)

            ? &temp->left : &temp->right;

 

        if (*p == sentinel) {

            break;

        }

 

        temp = *p;

    }

 

    *p = node;

    node->parent = temp;

    node->left = sentinel;

    node->right = sentinel;

    ngx_rbt_red(node);}

 

5、刪除值

/* 刪除節點 */

voidngx_rbtree_delete(ngx_thread_volatile ngx_rbtree_t *tree,

    ngx_rbtree_node_t *node){

    ngx_uint_t           red;

    ngx_rbtree_node_t  **root, *sentinel, *subst, *temp, *w;

 

    /* a binary tree delete */

 

    root = (ngx_rbtree_node_t **) &tree->root;

    sentinel = tree->sentinel;

 

    /* 下面是獲取temp節點值,temp保存的節點是準備替換節點node ;

     * subst是保存要被替換的節點的後繼節點;

     */

 

    /* case1:若node節點沒有左孩子(這裏包含了存在或不存在右孩子的狀況)*/

    if (node->left == sentinel) {

        temp = node->right;

        subst = node;

 

    } else if (node->right == sentinel) {/* case2:node節點存在左孩子,可是不存在右孩子 */

        temp = node->left;

        subst = node;

 

    } else {/* case3:node節點既有左孩子,又有右孩子 */

        subst = ngx_rbtree_min(node->right, sentinel);/* 獲取node節點的後續節點 */

 

        if (subst->left != sentinel) {

            temp = subst->left;

        } else {

            temp = subst->right;

        }

    }

 

    /* 若被替換的節點subst是根節點,則temp直接替換subst稱爲根節點 */

    if (subst == *root) {

        *root = temp;

        ngx_rbt_black(temp);

 

        /* DEBUG stuff */

        node->left = NULL;

        node->right = NULL;

        node->parent = NULL;

        node->key = 0;

 

        return;

    }

 

    /* red記錄subst節點的顏色 */

    red = ngx_rbt_is_red(subst);

 

    /* temp節點替換subst 節點 */

    if (subst == subst->parent->left) {

        subst->parent->left = temp;

 

    } else {

        subst->parent->right = temp;

    }

 

    /* 根據subst是否爲node節點進行處理 */

    if (subst == node) {

 

        temp->parent = subst->parent;

 

    } else {

 

        if (subst->parent == node) {

            temp->parent = subst;

 

        } else {

            temp->parent = subst->parent;

        }

 

        /* 複製node節點屬性 */

        subst->left = node->left;

        subst->right = node->right;

        subst->parent = node->parent;

        ngx_rbt_copy_color(subst, node);

 

        if (node == *root) {

            *root = subst;

 

        } else {

            if (node == node->parent->left) {

                node->parent->left = subst;

            } else {

                node->parent->right = subst;

            }

        }

 

        if (subst->left != sentinel) {

            subst->left->parent = subst;

        }

 

        if (subst->right != sentinel) {

            subst->right->parent = subst;

        }

    }

 

    /* DEBUG stuff */

    node->left = NULL;

    node->right = NULL;

    node->parent = NULL;

    node->key = 0;

 

    if (red) {

        return;

    }

 

    /* 下面開始是調整紅黑樹的性質 */

    /* a delete fixup */

 

    /* 根據temp節點進行處理 ,若temp不是根節點且爲黑色 */

    while (temp != *root && ngx_rbt_is_black(temp)) {

 

        /* 若temp是其父親節點的左孩子 */

        if (temp == temp->parent->left) {

            w = temp->parent->right;/* w爲temp的兄弟節點 */

 

            /* case A:temp兄弟節點爲紅色 */

            /* 解決辦法:

             * 一、改變w節點及temp父親節點的顏色;

             * 二、對temp父親節的作一次左旋轉,此時,temp的兄弟節點是旋轉以前w的某個子節點,該子節點顏色爲黑色;

             * 三、此時,case A已經轉換爲case B、case C 或 case D;

             */

            if (ngx_rbt_is_red(w)) {

                ngx_rbt_black(w);

                ngx_rbt_red(temp->parent);

                ngx_rbtree_left_rotate(root, sentinel, temp->parent);

                w = temp->parent->right;

            }

 

            /* case B:temp的兄弟節點w是黑色,且w的兩個子節點都是黑色 */

            /* 解決辦法:

             * 一、改變w節點的顏色;

             * 二、把temp的父親節點做爲新的temp節點;

             */

            if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {

                ngx_rbt_red(w);

                temp = temp->parent;

 

            } else {/* case C:temp的兄弟節點是黑色,且w的左孩子是紅色,右孩子是黑色 */

                /* 解決辦法:

                 * 一、將改變w及其左孩子的顏色;

                 * 二、對w節點進行一次右旋轉;

                 * 三、此時,temp新的兄弟節點w有着一個紅色右孩子的黑色節點,轉爲case D;

                 */

                if (ngx_rbt_is_black(w->right)) {

                    ngx_rbt_black(w->left);

                    ngx_rbt_red(w);

                    ngx_rbtree_right_rotate(root, sentinel, w);

                    w = temp->parent->right;

                }

 

                /* case D:temp的兄弟節點w爲黑色,且w的右孩子爲紅色 */

                /* 解決辦法:

                 * 一、將w節點設置爲temp父親節點的顏色,temp父親節點設置爲黑色;

                 * 二、w的右孩子設置爲黑色;

                 * 三、對temp的父親節點作一次左旋轉;

                 * 四、最後把根節點root設置爲temp節點;*/

                ngx_rbt_copy_color(w, temp->parent);

                ngx_rbt_black(temp->parent);

                ngx_rbt_black(w->right);

                ngx_rbtree_left_rotate(root, sentinel, temp->parent);

                temp = *root;

            }

 

        } else {/* 這裏針對的是temp節點爲其父親節點的左孩子的狀況 */

            w = temp->parent->left;

 

            if (ngx_rbt_is_red(w)) {

                ngx_rbt_black(w);

                ngx_rbt_red(temp->parent);

                ngx_rbtree_right_rotate(root, sentinel, temp->parent);

                w = temp->parent->left;

            }

 

            if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {

                ngx_rbt_red(w);

                temp = temp->parent;

 

            } else {

                if (ngx_rbt_is_black(w->left)) {

                    ngx_rbt_black(w->right);

                    ngx_rbt_red(w);

                    ngx_rbtree_left_rotate(root, sentinel, w);

                    w = temp->parent->left;

                }

 

                ngx_rbt_copy_color(w, temp->parent);

                ngx_rbt_black(temp->parent);

                ngx_rbt_black(w->left);

                ngx_rbtree_right_rotate(root, sentinel, temp->parent);

                temp = *root;

            }

        }

    }

 

    ngx_rbt_black(temp);}

 

 

 

紅黑樹比較複雜,但根本不用程序員本身編寫,直接用就能夠了,himqtt的紅黑樹也是來源於nginx的數據結構,很是感謝那些開源大神,有了github,人類的知識和智慧進一步得以傳承。

做爲程序員,會copy優秀的代碼也是一種很是重要的能力,你們不用再面向你的對象編程,面向github就好了。

相關文章
相關標籤/搜索