nginx源码学习2——扒、封装红黑树代码

来源:互联网 发布:初学者学化妆知乎 编辑:程序博客网 时间:2024/05/19 23:14

说明:

1.nginx红黑树除了是个通用结构体,还是定时器实现的载体,进一步学习定时器代码需要了解红黑树用法;

2.nginx红黑树基本上只提供了一个初始化红黑树、增、删节点,查找最小节点四种操作,其实对nginx也够了;

3.nginx红黑树细节暴露比较多,节点要自己malloc自己构造,插入操作的第一步(旋转前)要自己实现(尽管各种实现的不同处都只是在key相同时提供比较大小的方法),节点删除后要自己释放(当然有内存池要方便不少),树结构不要了也要自己释放;

4.因为是内部使用,没有异常检查。

综上,打算扒出来代码改点(主要是增加key比较逻辑),之后封装完全隐藏细节并构造、析构统一化,顺便加点注释,进行基本测试。本质还是要在改的过程中学习代码。


修改之后的核心代码,mytool_rbtree_raw.h

#ifndef MYTOOL_RBTREE_RAW_H #define MYTOOL_RBTREE_RAW_H namespace mytool{        typedef bool (*smallerFun)(void *,void *);        struct rbtree_node_t{                 unsigned key;        rbtree_node_t *left;        rbtree_node_t *right;        rbtree_node_t *parent;        unsigned char color;        unsigned char data;        };        struct rbtree_t{         rbtree_node_t *root;        rbtree_node_t *sentinel;        smallerFun sfun;        };#define rbtree_init(tree, s, i)                                           \    rbtree_sentinel_init(s);                                              \    (tree)->root = s;                                                         \    (tree)->sentinel = s;                                                     \    (tree)->sfun = ivoid rbtree_insert(rbtree_t *tree,rbtree_node_t *node);void rbtree_delete(rbtree_t *tree,rbtree_node_t *node);//默认insert函数,已无必要//void rbtree_insert_value(rbtree_node_t *root,rbtree_node_t *node,rbtree_node_t *sentinel);//timer的insert函数,已无必要//void rbtree_insert_timer_value(rbtree_node_t *root,rbtree_node_t *node,rbtree_node_t *sentinel);//原代码对于timer对比没有相等的情况,可惜这个inline还是可能得变成函数指针inline bool timer_smaller(void *p1,void *p2){return true;}#define rbt_red(node) ((node)->color = 1)#define rbt_black(node) ((node)->color = 0)#define rbt_is_red(node) ((node)->color)#define rbt_is_black(node) (!rbt_is_red(node))#define rbt_copy_color(n1, n2) (n1->color = n2->color)#define rbtree_sentinel_init(node)  rbt_black(node)static inline rbtree_node_t *rbtree_min(rbtree_node_t *node,rbtree_node_t *sentinel){    while(node->left != sentinel)        node = node->left;    return node;}}#endif

mytool_rbtree_raw.cpp

#include "mytool_rbtree_raw.h"#include <cstdlib> //for NULLnamespace mytool{static inline void rbtree_left_rotate(rbtree_node_t **root,rbtree_node_t *sentinel,rbtree_node_t *node);static inline void rbtree_right_rotate(rbtree_node_t **root,rbtree_node_t *sentinel,rbtree_node_t *node);void rbtree_insert(rbtree_t *tree,rbtree_node_t *node){    rbtree_node_t **root,*temp,*sentinel;    /* a binary tree insert */    root = (rbtree_node_t **)&tree->root;    sentinel = tree->sentinel;    if(*root == sentinel){        node->parent = NULL;        node->left = sentinel;        node->right = sentinel;        rbt_black(node);        *root = node;        return;    }        //之前的insert需要重新实现,但其实只有key相等的部分需要,其他的比如迭代过程收尾过程都是一样的,所以在此把insert函数改成了一个smallFun只比data大小的    //tree->insert(*root, node, sentinel);        {                rbtree_node_t **p;                rbtree_node_t *temp = *root;        for(;;){            if(node->key < temp->key){                p = &temp->left;            }else if(node->key > temp->key){                p = &temp->right;            }else if(tree->sfun(&(node->data),&(temp->data))){                p = &temp->left;            }else{                p = &temp->right;                        }            if(*p == sentinel)                break;            temp = *p;        }        *p = node;        node->parent = temp;        node->left = sentinel;        node->right = sentinel;        rbt_red(node);        }    /* re-balance tree */    while(node != *root && rbt_is_red(node->parent)){        if(node->parent == node->parent->parent->left){            temp = node->parent->parent->right;            if(rbt_is_red(temp)){                rbt_black(node->parent);                rbt_black(temp);                rbt_red(node->parent->parent);                node = node->parent->parent;            }else{                if(node == node->parent->right){                    node = node->parent;                    rbtree_left_rotate(root, sentinel, node);                }                rbt_black(node->parent);                rbt_red(node->parent->parent);                rbtree_right_rotate(root, sentinel, node->parent->parent);            }        }else{            temp = node->parent->parent->left;            if(rbt_is_red(temp)){                rbt_black(node->parent);                rbt_black(temp);                rbt_red(node->parent->parent);                node = node->parent->parent;            }else{                if(node == node->parent->left){                    node = node->parent;                    rbtree_right_rotate(root, sentinel, node);                }                rbt_black(node->parent);                rbt_red(node->parent->parent);                rbtree_left_rotate(root, sentinel, node->parent->parent);            }        }    }    rbt_black(*root);}/*void rbtree_insert_value(rbtree_node_t *temp,rbtree_node_t *node,rbtree_node_t *sentinel){    rbtree_node_t **p;    for(;;){        p = (node->key < temp->key) ? &temp->left : &temp->right;        if(*p == sentinel)            break;        temp = *p;    }    *p = node;    node->parent = temp;    node->left = sentinel;    node->right = sentinel;    rbt_red(node);}*/void rbtree_delete(rbtree_t *tree,rbtree_node_t *node){    unsigned red;    rbtree_node_t **root,*sentinel,*subst,*temp,*w;    /* a binary tree delete */    root = (rbtree_node_t **)&tree->root;    sentinel = tree->sentinel;    if (node->left == sentinel){        temp = node->right;        subst = node;    }else if(node->right == sentinel){        temp = node->left;        subst = node;    }else{        subst = rbtree_min(node->right, sentinel);        if(subst->left != sentinel){            temp = subst->left;        }else{            temp = subst->right;        }    }    if(subst == *root){        *root = temp;        rbt_black(temp);        /* DEBUG stuff */        node->left = NULL;        node->right = NULL;        node->parent = NULL;        node->key = 0;        return;    }    red = rbt_is_red(subst);    if(subst == subst->parent->left){        subst->parent->left = temp;    }else{        subst->parent->right = temp;    }    if(subst == node){        temp->parent = subst->parent;    }else{        if(subst->parent == node){            temp->parent = subst;        }else{            temp->parent = subst->parent;        }        subst->left = node->left;        subst->right = node->right;        subst->parent = node->parent;        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 */    while(temp != *root && rbt_is_black(temp)){        if(temp == temp->parent->left){            w = temp->parent->right;            if(rbt_is_red(w)){                rbt_black(w);                rbt_red(temp->parent);                rbtree_left_rotate(root,sentinel,temp->parent);                w = temp->parent->right;            }            if(rbt_is_black(w->left) && rbt_is_black(w->right)){                rbt_red(w);                temp = temp->parent;            }else{                if(rbt_is_black(w->right)){                    rbt_black(w->left);                    rbt_red(w);                    rbtree_right_rotate(root,sentinel,w);                    w = temp->parent->right;                }                rbt_copy_color(w,temp->parent);                rbt_black(temp->parent);                rbt_black(w->right);                rbtree_left_rotate(root,sentinel,temp->parent);                temp = *root;            }        }else{            w = temp->parent->left;            if(rbt_is_red(w)){                rbt_black(w);                rbt_red(temp->parent);                rbtree_right_rotate(root,sentinel,temp->parent);                w = temp->parent->left;            }            if(rbt_is_black(w->left) && rbt_is_black(w->right)){                rbt_red(w);                temp = temp->parent;            }else{                if(rbt_is_black(w->left)){                    rbt_black(w->right);                    rbt_red(w);                    rbtree_left_rotate(root,sentinel,w);                    w = temp->parent->left;                }                rbt_copy_color(w, temp->parent);                rbt_black(temp->parent);                rbt_black(w->left);                rbtree_right_rotate(root,sentinel,temp->parent);                temp = *root;            }        }    }    rbt_black(temp);}static inline void rbtree_left_rotate(rbtree_node_t **root,rbtree_node_t *sentinel,rbtree_node_t *node){    rbtree_node_t *temp;    temp = node->right;    node->right = temp->left;    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 inline void rbtree_right_rotate(rbtree_node_t **root,rbtree_node_t *sentinel,rbtree_node_t *node){    rbtree_node_t *temp;    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;}}

核心代码基本测试代码:

#include "mytool_rbtree_raw.h"#include <cstring>#include <cstdio>using namespace mytool;struct person{        struct rbtree_node_t node;        char namePad[30];};int main(){        struct rbtree_t rbt;        struct rbtree_node_t sentinel,*prn;        memset(&sentinel,sizeof(sentinel),0);        rbtree_init(&rbt,&sentinel,timer_smaller);        struct person n1,n2,n3,n4;        n1.node.key = 11;        n2.node.key = 22;        n3.node.key = 33;        n4.node.key = 44;        strcpy((char *)(&(n1.node.data)),"once");        strcpy((char *)(&(n2.node.data)),"veintidos");        strcpy((char *)(&(n3.node.data)),"treinta y tres");        strcpy((char *)(&(n4.node.data)),"cuarenta y cuatro");        rbtree_insert(&rbt,&(n4.node));        rbtree_insert(&rbt,&(n3.node));        rbtree_insert(&rbt,&(n2.node));        rbtree_insert(&rbt,&(n1.node));        int i = 4;        while(i > 0){                prn = rbtree_min(rbt.root,rbt.sentinel);                printf("%dth——key:%u,name:%s\n",i,prn->key,(char *)(&(prn->data)));                rbtree_delete(&rbt,prn);                i--;        }}

封装头文件:

mytool_rbtree.h

#ifndef MYTOOL_RBTREE_H#define MYTOOL_RBTREE_H#include <cstddef>namespace mytool{//特化的红黑树,操作只支持求最小的节点class Rbtree{        typedef bool (*smallerFun)(void *,void *);        public:                //fun不能为NULL                //异常:out_of_memory,invalid_argument                explicit Rbtree(smallerFun fun) noexcept(false) {                        _initPrb(fun);                }                //data其实可以为NULL,如果data全部为NULL就退化成了set,所以data还是不能为NULL,所以size不能为0                //为了防止数据拷贝到node内部的开销,要求使用者使用getNodeData动态获取内存,node的头结构隐藏在了返回指针的前面                //返回指针已经考虑到了对齐                //异常:out_of_memory,invalid_argument                void *getNodeData(size_t size) const noexcept(false);                //异常:logic_error                void insertNodeData(void *data,unsigned key) const noexcept(false);                //异常:invalid_argument,logic_error                //每个红黑树可以增加一个全局的编号,来验证要删除的data是不是本树的,未实现,靠调用者保证                void deleteNodeData(void *data) const noexcept(false);                void *getMinData() const noexcept;                //删除节点一直到空,并且删除_prb                ~Rbtree() noexcept;        private:                void _initPrb(smallerFun fun) noexcept(false);                void *_prb;};}#endif

mytool_rbtree.cpp

#include "mytool_rbtree.h"#include "mytool_rbtree_raw.h"#include <stdexcept>#include <cstring>#define RBTREEP(p) ((struct rbtree_t *)(p))  #define RBTREENODEP(p) ((struct rbtree_node_t *)(p))  #define NODEMAGIC 88 #define ALIGNSIZE 16#define NODEALIGNEDSIZE ((sizeof(struct rbtree_node_t) + ALIGNSIZE - 1) & ~(ALIGNSIZE - 1))namespace mytool{        void Rbtree::_initPrb(smallerFun fun) noexcept(false){        if(fun == NULL)            throw std::invalid_argument("smallerFun can not be empty");                //用new不用malloc因为new能够有分配失败策略,再失败直接抛异常                //尽管是new char,也是按照最大对齐规则的                _prb = (void *)(new char[sizeof(struct rbtree_t)]);                struct rbtree_node_t *psentinel = RBTREENODEP(new char[sizeof(struct rbtree_node_t)]);                rbtree_init(RBTREEP(_prb),psentinel,fun);        }        void *Rbtree::getNodeData(size_t size) const noexcept(false){        if(size == 0)            throw std::invalid_argument("size can not be zero");                struct rbtree_node_t *prn = RBTREENODEP(new char[NODEALIGNEDSIZE + size]);                memset(prn,0,NODEALIGNEDSIZE);                //隐藏rbtree_node_t细节后可以加一个魔数验证传的到底是不是node的data,这里用已经没有用了的data字段                prn->data = NODEMAGIC;                //color用来表示是否已经加入过node,当前合法color只有0,1                prn->color = NODEMAGIC;                return (char *)prn + NODEALIGNEDSIZE;        }        void Rbtree::insertNodeData(void *data,unsigned key) const noexcept(false){                if(data == NULL)                        return;                struct rbtree_node_t *prn = RBTREENODEP((char *)data - NODEALIGNEDSIZE);                if(prn->data != NODEMAGIC)                        throw std::logic_error("data is not a valid node");                if(prn->color != NODEMAGIC)                        throw std::logic_error("data has been added before");                struct rbtree_t *pr = RBTREEP(_prb);                prn->key = key;                //底层代码没细看,说不定有坑                rbtree_insert(pr,prn);        }        void Rbtree::deleteNodeData(void *data) const noexcept(false){                struct rbtree_node_t *prn = RBTREENODEP((char *)data - NODEALIGNEDSIZE);                if(data == NULL)                        return;                if(prn->data != NODEMAGIC)                        throw std::logic_error("data is not a valid node");                struct rbtree_t *pr = RBTREEP(_prb);                rbtree_delete(pr,prn);                delete [] (char *)prn;        }        void *Rbtree::getMinData() const noexcept{                struct rbtree_t *pr = RBTREEP(_prb);                if(pr->root == pr->sentinel)                        return NULL;                struct rbtree_node_t *prn = rbtree_min(pr->root,pr->sentinel);                return (char *)prn + NODEALIGNEDSIZE;        }        Rbtree::~Rbtree(){                void *p = getMinData();                while(p != NULL){                        deleteNodeData(p);                        p = getMinData();                }                delete [] (char *)_prb;        }}

封装基本测试,testTree.cpp:

#include "mytool_rbtree.h"#include <iostream>using namespace mytool;bool smallerInt(void *p1,void *p2){        return *((int *)p1) < *((int *)p1);}int main(){        Rbtree r(smallerInt);        int *p1,*p2,*p3,*p4;        p1 = (int *)r.getNodeData(sizeof(int));        p2 = (int *)r.getNodeData(sizeof(int));        p3 = (int *)r.getNodeData(sizeof(int));        p4 = (int *)r.getNodeData(sizeof(int));        *p1 = 11;        *p2 = 22;        *p3 = 33;        *p4 = 44;        r.insertNodeData(p3,33);        r.insertNodeData(p2,22);        r.insertNodeData(p4,44);        r.insertNodeData(p1,11);        int *p = (int *)r.getMinData();        while(p != NULL){                std::cout<<*p<<std::endl;                r.deleteNodeData(p);                p = (int *)(r.getMinData());        }}



原创粉丝点击