📄 rbt.c
字号:
dns_rbtnode_t *node; isc_region_t region; unsigned int labels; REQUIRE(name->offsets != NULL); dns_name_toregion(name, ®ion); labels = dns_name_countlabels(name); ENSURE(labels > 0); /* * Allocate space for the node structure, the name, and the offsets. */ node = (dns_rbtnode_t *)isc_mem_get(mctx, sizeof(*node) + region.length + labels); if (node == NULL) return (ISC_R_NOMEMORY); node->is_root = 0; PARENT(node) = NULL; RIGHT(node) = NULL; LEFT(node) = NULL; DOWN(node) = NULL; DATA(node) = NULL;#ifdef DNS_RBT_USEHASH HASHNEXT(node) = NULL; HASHVAL(node) = 0;#endif LOCKNUM(node) = 0; REFS(node) = 0; WILD(node) = 0; DIRTY(node) = 0; node->find_callback = 0; MAKE_BLACK(node); /* * The following is stored to make reconstructing a name from the * stored value in the node easy: the length of the name, the number * of labels, whether the name is absolute or not, the name itself, * and the name's offsets table. * * XXX RTH * The offsets table could be made smaller by eliminating the * first offset, which is always 0. This requires changes to * lib/dns/name.c. */ NAMELEN(node) = region.length; PADBYTES(node) = 0; OFFSETLEN(node) = labels; ATTRS(node) = name->attributes; memcpy(NAME(node), region.base, region.length); memcpy(OFFSETS(node), name->offsets, labels); *nodep = node; return (ISC_R_SUCCESS);}#ifdef DNS_RBT_USEHASHstatic inline voidhash_add_node(dns_rbt_t *rbt, dns_rbtnode_t *node) { unsigned int hash; compute_node_hash(node); hash = HASHVAL(node) % rbt->hashsize; HASHNEXT(node) = rbt->hashtable[hash]; rbt->hashtable[hash] = node;}static isc_result_tinithash(dns_rbt_t *rbt) { unsigned int bytes; rbt->hashsize = RBT_HASH_SIZE; bytes = rbt->hashsize * sizeof(dns_rbtnode_t *); rbt->hashtable = isc_mem_get(rbt->mctx, bytes); if (rbt->hashtable == NULL) return (ISC_R_NOMEMORY); memset(rbt->hashtable, 0, bytes); return (ISC_R_SUCCESS);}static isc_result_trehash(dns_rbt_t *rbt) { unsigned int oldsize; dns_rbtnode_t **oldtable; dns_rbtnode_t *node; unsigned int hash; unsigned int i; oldsize = rbt->hashsize; rbt->hashsize *= 2; oldtable = rbt->hashtable; rbt->hashtable = isc_mem_get(rbt->mctx, rbt->hashsize * sizeof(dns_rbtnode_t *)); if (rbt->hashtable == NULL) return (ISC_R_NOMEMORY); for (i = 0; i < rbt->hashsize; i++) rbt->hashtable[i] = NULL; for (i = 0; i < oldsize; i++) { node = oldtable[i]; while (node != NULL) { hash = HASHVAL(node) % rbt->hashsize; oldtable[i] = HASHNEXT(node); HASHNEXT(node) = rbt->hashtable[hash]; rbt->hashtable[hash] = node; node = oldtable[i]; } } isc_mem_put(rbt->mctx, oldtable, rbt->hashsize * sizeof(dns_rbtnode_t *) / 2); return (ISC_R_SUCCESS);}static inline isc_result_thash_node(dns_rbt_t *rbt, dns_rbtnode_t *node) { isc_result_t result = ISC_R_SUCCESS; if (rbt->hashtable == NULL) result = inithash(rbt); else if (rbt->nodecount >= rbt->hashsize) result = rehash(rbt); if (result == ISC_R_SUCCESS) hash_add_node(rbt, node); return (result);}static inline voidunhash_node(dns_rbt_t *rbt, dns_rbtnode_t *node) { unsigned int bucket; dns_rbtnode_t *bucket_node; if (rbt->hashtable != NULL) { bucket = HASHVAL(node) % rbt->hashsize; bucket_node = rbt->hashtable[bucket]; if (bucket_node == node) rbt->hashtable[bucket] = HASHNEXT(node); else { while (HASHNEXT(bucket_node) != node) { INSIST(HASHNEXT(bucket_node) != NULL); bucket_node = HASHNEXT(bucket_node); } HASHNEXT(bucket_node) = HASHNEXT(node); } }}#endif /* DNS_RBT_USEHASH */static inline voidrotate_left(dns_rbtnode_t *node, dns_rbtnode_t **rootp) { dns_rbtnode_t *child; REQUIRE(node != NULL); REQUIRE(rootp != NULL); child = RIGHT(node); INSIST(child != NULL); RIGHT(node) = LEFT(child); if (LEFT(child) != NULL) PARENT(LEFT(child)) = node; LEFT(child) = node; if (child != NULL) PARENT(child) = PARENT(node); if (IS_ROOT(node)) { *rootp = child; child->is_root = 1; node->is_root = 0; } else { if (LEFT(PARENT(node)) == node) LEFT(PARENT(node)) = child; else RIGHT(PARENT(node)) = child; } PARENT(node) = child;}static inline voidrotate_right(dns_rbtnode_t *node, dns_rbtnode_t **rootp) { dns_rbtnode_t *child; REQUIRE(node != NULL); REQUIRE(rootp != NULL); child = LEFT(node); INSIST(child != NULL); LEFT(node) = RIGHT(child); if (RIGHT(child) != NULL) PARENT(RIGHT(child)) = node; RIGHT(child) = node; if (child != NULL) PARENT(child) = PARENT(node); if (IS_ROOT(node)) { *rootp = child; child->is_root = 1; node->is_root = 0; } else { if (LEFT(PARENT(node)) == node) LEFT(PARENT(node)) = child; else RIGHT(PARENT(node)) = child; } PARENT(node) = child;}/* * This is the real workhorse of the insertion code, because it does the * true red/black tree on a single level. */static voiddns_rbt_addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order, dns_rbtnode_t **rootp){ dns_rbtnode_t *child, *root, *parent, *grandparent; dns_name_t add_name, current_name; dns_offsets_t add_offsets, current_offsets; REQUIRE(rootp != NULL); REQUIRE(node != NULL && LEFT(node) == NULL && RIGHT(node) == NULL); REQUIRE(current != NULL); root = *rootp; if (root == NULL) { /* * First node of a level. */ MAKE_BLACK(node); node->is_root = 1; PARENT(node) = current; *rootp = node; return; } child = root; dns_name_init(&add_name, add_offsets); NODENAME(node, &add_name); dns_name_init(¤t_name, current_offsets); NODENAME(current, ¤t_name); if (order < 0) { INSIST(LEFT(current) == NULL); LEFT(current) = node; } else { INSIST(RIGHT(current) == NULL); RIGHT(current) = node; } INSIST(PARENT(node) == NULL); PARENT(node) = current; MAKE_RED(node); while (node != root && IS_RED(PARENT(node))) { /* * XXXDCL could do away with separate parent and grandparent * variables. They are vestiges of the days before parent * pointers. However, they make the code a little clearer. */ parent = PARENT(node); grandparent = PARENT(parent); if (parent == LEFT(grandparent)) { child = RIGHT(grandparent); if (child != NULL && IS_RED(child)) { MAKE_BLACK(parent); MAKE_BLACK(child); MAKE_RED(grandparent); node = grandparent; } else { if (node == RIGHT(parent)) { rotate_left(parent, &root); node = parent; parent = PARENT(node); grandparent = PARENT(parent); } MAKE_BLACK(parent); MAKE_RED(grandparent); rotate_right(grandparent, &root); } } else { child = LEFT(grandparent); if (child != NULL && IS_RED(child)) { MAKE_BLACK(parent); MAKE_BLACK(child); MAKE_RED(grandparent); node = grandparent; } else { if (node == LEFT(parent)) { rotate_right(parent, &root); node = parent; parent = PARENT(node); grandparent = PARENT(parent); } MAKE_BLACK(parent); MAKE_RED(grandparent); rotate_left(grandparent, &root); } } } MAKE_BLACK(root); ENSURE(IS_ROOT(root)); *rootp = root; return;}/* * This is the real workhorse of the deletion code, because it does the * true red/black tree on a single level. */static voiddns_rbt_deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp) { dns_rbtnode_t *child, *sibling, *parent; dns_rbtnode_t *successor; REQUIRE(delete != NULL); /* * Verify that the parent history is (apparently) correct. */ INSIST((IS_ROOT(delete) && *rootp == delete) || (! IS_ROOT(delete) && (LEFT(PARENT(delete)) == delete || RIGHT(PARENT(delete)) == delete))); child = NULL; if (LEFT(delete) == NULL) { if (RIGHT(delete) == NULL) { if (IS_ROOT(delete)) { /* * This is the only item in the tree. */ *rootp = NULL; return; } } else /* * This node has one child, on the right. */ child = RIGHT(delete); } else if (RIGHT(delete) == NULL) /* * This node has one child, on the left. */ child = LEFT(delete); else { dns_rbtnode_t holder, *tmp = &holder; /* * This node has two children, so it cannot be directly * deleted. Find its immediate in-order successor and * move it to this location, then do the deletion at the * old site of the successor. */ successor = RIGHT(delete); while (LEFT(successor) != NULL) successor = LEFT(successor); /* * The successor cannot possibly have a left child; * if there is any child, it is on the right. */ if (RIGHT(successor) != NULL) child = RIGHT(successor); /* Swap the two nodes; it would be simpler to just replace * the value being deleted with that of the successor, * but this rigamarole is done so the caller has complete * control over the pointers (and memory allocation) of * all of nodes. If just the key value were removed from * the tree, the pointer to the node would be unchanged. */ /* * First, put the successor in the tree location of the * node to be deleted. Save its existing tree pointer * information, which will be needed when linking up * delete to the successor's old location. */ memcpy(tmp, successor, sizeof(dns_rbtnode_t)); if (IS_ROOT(delete)) { *rootp = successor; successor->is_root = ISC_TRUE; delete->is_root = ISC_FALSE; } else if (LEFT(PARENT(delete)) == delete) LEFT(PARENT(delete)) = successor; else RIGHT(PARENT(delete)) = successor; PARENT(successor) = PARENT(delete); LEFT(successor) = LEFT(delete); RIGHT(successor) = RIGHT(delete); COLOR(successor) = COLOR(delete); if (LEFT(successor) != NULL) PARENT(LEFT(successor)) = successor; if (RIGHT(successor) != successor) PARENT(RIGHT(successor)) = successor; /* * Now relink the node to be deleted into the * successor's previous tree location. PARENT(tmp) * is the successor's original parent. */ INSIST(! IS_ROOT(delete)); if (PARENT(tmp) == delete) { /* * Node being deleted was successor's parent. */ RIGHT(successor) = delete; PARENT(delete) = successor; } else { LEFT(PARENT(tmp)) = delete; PARENT(delete) = PARENT(tmp); } /* * Original location of successor node has no left. */ LEFT(delete) = NULL; RIGHT(delete) = RIGHT(tmp); COLOR(delete) = COLOR(tmp); } /* * Remove the node by removing the links from its parent. */ if (! IS_ROOT(delete)) { if (LEFT(PARENT(delete)) == delete) LEFT(PARENT(delete)) = child; else RIGHT(PARENT(delete)) = child; if (child != NULL) PARENT(child) = PARENT(delete); } else { /* * This is the root being deleted, and at this point * it is known to have just one child. */ *rootp = child; child->is_root = 1; PARENT(child) = PARENT(delete); } /* * Fix color violations. */ if (IS_BLACK(delete)) { parent = PARENT(delete); while (child != *rootp && IS_BLACK(child)) { INSIST(child == NULL || ! IS_ROOT(child)); if (LEFT(parent) == child) { sibling = RIGHT(parent); if (IS_RED(sibling)) { MAKE_BLACK(sibling); MAKE_RED(parent); rotate_left(parent, rootp); sibling = RIGHT(parent); } if (IS_BLACK(LEFT(sibling)) && IS_BLACK(RIGHT(sibling))) { MAKE_RED(sibling); child = parent; } else { if (IS_BLACK(RIGHT(sibling))) { MAKE_BLACK(LEFT(sibling)); MAKE_RED(sibling);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -