红黑数的性质这里就不多说了网上很多,主要看看nginx是如何定义的rbtree和怎么使用它
rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t)); //从共享内存中创建一颗红黑树
sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t)); //创建了一个红黑树的节点作为哨兵节点
ngx_rbtree_init(ctx->rbtree, ctx->sentinel, ngx_http_srs_hook_rbtree_insert_value); //初始化这颗红黑树
主要看下ngx_rbtree_init的实现:
#define ngx_rbtree_init(tree, s, i) \
ngx_rbtree_sentinel_init(s); \
(tree)->root = s; \
(tree)->sentinel = s; \
(tree)->insert = i
关键的就是参数i,这个是个函数指针,作为红黑树的插入节点时候的算法,这个需要用户自己实现
typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
第二个参数就是需要插入的节点数据,第三个参数是哨兵节点
接下去看下怎么实现这个插入函数(ps: nginx很多模块中已经有了rbtree插入的实现,我也是参考写的)
static void
ngx_http_srs_hook_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) {
ngx_rbtree_node_t **p;
p = NULL;
if(node == NULL) {
return;
}
for ( ;; ) {
if (node->key < temp->key) {
p = &temp->left;
} else if (node->key > temp->key) {
p = &temp->right;
} else { /* node->key == temp->key */
break; //目前的实现不支持相同的key的节点
}
if (*p == sentinel) {
break;
}
temp = *p;
}
*p = node;
node->parent = temp;
node->left = sentinel;
node->right = sentinel;
ngx_rbt_red(node); //最后就是要让插入的节点是红色的(这是红黑树的性质)
}
对于使用者只需要负责插入的算法, 对于红黑数的反转等问题nginx都为大家做了
现在看看怎么进行节点的插入的
typedef struct{
ngx_rbtree_node_t node;
void *data;
}channel; //定义的红黑树节点
channel findchannel;
ngx_rbtree_insert(srshookctx->rbtree,&(findchannel->node));
看下ngx_rbtree_insert的实现:
ngx_rbtree_insert(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 */ //数据插入之后, nginx就为我们做了很复杂的红黑树的翻转来保持树的平衡
while (node != *root && ngx_rbt_is_red(node->parent)) {
if (node->parent == node->parent->parent->left) {
temp = node->parent->parent->right;
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->right) {
node = node->parent;
ngx_rbtree_left_rotate(root, sentinel, node);
}
ngx_rbt_black(node->parent);
ngx_rbt_red(node->parent->parent);
ngx_rbtree_right_rotate(root, sentinel, node->parent->parent);
}
} else {
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);
}
这样红黑数的插入就完成了,那如何来查找树里的节点呢?这里我实现了一个查找节点的函数
static requestchannel_st*
ngx_http_channel_tree_lookup(ngx_http_srs_hook_ctx_t *srsctx, uint64_t key)
{
ngx_rbtree_key_t node_key;
ngx_rbtree_node_t *node;
ngx_rbtree_node_t *sentinel;
requestchannel_st *fcn;
node_key = key;
node = srsctx->rbtree->root; //这里记录下要查找的树的根节点
sentinel = srsctx->rbtree->sentinel; //记录下哨兵节点
while (node != sentinel) {
if (node_key < node->key) { //通过左右子树查找key一样的节点
node = node->left;
continue;
}
if (node_key > node->key) {
node = node->right;
continue;
}
/* node_key == node->key */
fcn = (requestchannel_st *) node; //找到节点就转成节点数据结构,返回
return fcn;
}
/* not found */
return NULL;
}