7.5.5 如何自定义添加成员方法
由于节点的key关键字必须是整型,这导致很多情况下不同的节点会具有相同的key关键字。如果不希望出现具有相同key关键字的不同节点在向红黑树添加时出现覆盖原节点的情况,就需要实现自有的ngx_rbtree_insert_pt方法。
许多Nginx模块在使用红黑树时都自定义了ngx_rbtree_insert_pt方法(如geo、filecache模块等),本节以7.5.3节中介绍过的ngx_str_rbtree_insert_value为例,来说明如何定义这样的方法。先看一下ngx_str_rbtree_insert_value的实现。代码如下。
void
ngx_str_rbtree_insert_value(ngx_rbtree_node_t*temp,
ngx_rbtree_node_tnode,ngx_rbtree_node_tsentinel)
{
ngx_str_node_t
n,t;
ngx_rbtree_node_t**p;
for(;){
n=(ngx_str_node_t*)node;
t=(ngx_str_node_t*)temp;
//首先比较key关键字,红黑树中以key作为第一索引关键字
if(node->key!=temp->key){
//左子树节点的关键节小于右子树
p=(node->key<temp->key)?&temp->left:&temp->right;
}
//当key关键字相同时,以字符串长度为第二索引关键字
else if(n->str.len!=t->str.len){
//左子树节点字符串的长度小于右子树
p=(n->str.len<t->str.len)?&temp->left:&temp->right;
}else{
//key关键字相同且字符串长度相同时,再继续比较字符串内容
p=(ngx_memcmp(n->str.data,t->str.data,n->str.len)<0)?&temp->left:&temp->right;
}
//如果当前节点p是哨兵节点,那么跳出循环准备插入节点
if(*p==sentinel){
break;
}
//p节点与要插入的节点具有相同的标识符时,必须覆盖内容
temp=*p;
}
*p=node;
//置插入节点的父节点
node->parent=temp;
//左右子节点都是哨兵节点
node->left=sentinel;
node->right=sentinel;
/将节点颜色置为红色。注意,红黑树的ngx_rbtree_insert方法会在可能的旋转操作后重置该节点的颜色/
ngx_rbt_red(node);
}
可以看到,该代码与7.5.4节中介绍过的检索节点代码很相似。它所要处理的主要问题就是当key关键字相同时,继续以何种数据结构作为标准来确定红黑树节点的唯一性。Nginx中已经实现的诸多ngx_rbtree_insert_pt方法都是非常相似的,读者完全可以参照ngx_str_rbtree_insert_value方法来自定义红黑树节点添加方法。