freebsd rb.h insert を読んでみよう

前に赤黒木に挿入するときは赤が2つ来るのを防ぐように調節すると書きました。

それがfreebsdのmallocではどのように実装されているのかを見てみましょう。

freebsdは9.3です。rb.hは/usr/src/lib/libc/stdlib内にありますね。

 

    a_attr void								\
a_prefix##insert(a_rbt_type *rbtree, a_type *node) {			\
    struct {								\
	a_type *node;							\
	int cmp;							\
    } path[sizeof(void *) << 4], *pathp;				\
    rbt_node_new(a_type, a_field, rbtree, node);			\
    /* Wind. */								\
    path->node = rbtree->rbt_root;					\
    for (pathp = path; pathp->node != &rbtree->rbt_nil; pathp++) {	\
	int cmp = pathp->cmp = a_cmp(node, pathp->node);		\
	assert(cmp != 0);						\
	if (cmp < 0) {							\
	    pathp[1].node = rbtn_left_get(a_type, a_field,		\
	      pathp->node);						\
	} else {							\
	    pathp[1].node = rbtn_right_get(a_type, a_field,		\
	      pathp->node);						\
	}								\
    }									\
    pathp->node = node;							\
    /* Unwind. */							\
    for (pathp--; (uintptr_t)pathp >= (uintptr_t)path; pathp--) {	\
	a_type *cnode = pathp->node;					\
	if (pathp->cmp < 0) {						\
	    a_type *left = pathp[1].node;				\
	    rbtn_left_set(a_type, a_field, cnode, left);		\
	    if (rbtn_red_get(a_type, a_field, left)) {			\
		a_type *leftleft = rbtn_left_get(a_type, a_field, left);\
		if (rbtn_red_get(a_type, a_field, leftleft)) {		\
		    /* Fix up 4-node. */				\
		    a_type *tnode;					\
		    rbtn_black_set(a_type, a_field, leftleft);		\
		    rbtn_rotate_right(a_type, a_field, cnode, tnode);	\
		    cnode = tnode;					\
		}							\
	    } else {							\
		return;							\
	    }								\
	} else {							\
	    a_type *right = pathp[1].node;				\
	    rbtn_right_set(a_type, a_field, cnode, right);		\
	    if (rbtn_red_get(a_type, a_field, right)) {			\
		a_type *left = rbtn_left_get(a_type, a_field, cnode);	\
		if (rbtn_red_get(a_type, a_field, left)) {		\
		    /* Split 4-node. */					\
		    rbtn_black_set(a_type, a_field, left);		\
		    rbtn_black_set(a_type, a_field, right);		\
		    rbtn_red_set(a_type, a_field, cnode);		\
		} else {						\
		    /* Lean left. */					\
		    a_type *tnode;					\
		    bool tred = rbtn_red_get(a_type, a_field, cnode);	\
		    rbtn_rotate_left(a_type, a_field, cnode, tnode);	\
		    rbtn_color_set(a_type, a_field, tnode, tred);	\
		    rbtn_red_set(a_type, a_field, cnode);		\
		    cnode = tnode;					\
		}							\
	    } else {							\
		return;							\
	    }								\
	}								\
	pathp->node = cnode;						\
    }									\
    /* Set root, and make it black. */					\
    rbtree->rbt_root = path->node;					\
    rbtn_black_set(a_type, a_field, rbtree->rbt_root);			\
}

という感じです。読みやすくはないですね。

/* Unwind. */ までは赤黒木を準備しているところです。cmpでの比較を使って

右左と入れていきます。

肝心なのは/* Unwind. */以降ですね。

まず挿入されるnodeをpathp->node にいれています。これは一番後ろ(下)の

ノードでここからrootへ遡っていきます。

 

 for (pathp--; (uintptr_t)pathp >= (uintptr_t)path; pathp--) {    \

このループの中でのnodeがcnodeです。current nodeでしょうか。

ループ内ではまず (pathp->cmp < 0) によって場合分けされます。

では読んで行きましょう。

pathp->cmp <0 //今のノードのcmpがマイナスの時

a_type *left = pathp[1].node; //今のノードの次の(下の)ノードを

rbtn_left_set(a_type, a_field, cnode, left);       //今のノードの左にセットします。

if (rbtn_red_get(a_type, a_field, left)) {            //そしてその左のノードが赤で
       a_type *leftleft = rbtn_left_get(a_type, a_field, left);
       if (rbtn_red_get(a_type, a_field, leftleft)) {  //その左のノードの左の子ノードが赤なら

rbtn_black_set(a_type, a_field, leftleft);        //左の左子ノードを黒にします
            rbtn_rotate_right(a_type, a_field, cnode, tnode);    //右に回します
            cnode = tnode;  //回した後の親ノードをcnodeにします。

これは前にやった2番のタイプ(赤の左の子が赤)です。赤黒木をやってみよう 2 - yamshing's diary

   黒

          /

  赤

       /

 赤

これの最後を黒にして右に回すと

     赤

            /      \

  黒  黒

これで赤が2個になることが解消されます。

ここで一休み次回は右に赤が来る場合を見て行きましょう。