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

说明:

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 = i
void 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 NULL
namespace 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());
        }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值