📄 rbt.c
字号:
FINDCALLBACK(current)) { result = chain_name(chain, callback_name, ISC_FALSE); if (result != ISC_R_SUCCESS) { dns_rbtnodechain_reset(chain); return (result); } result = (callback)(current, callback_name, callback_arg); if (result != DNS_R_CONTINUE) { saved_result = result; /* * Treat this node as if it * had no down pointer. */ current = NULL; break; } } /* * Finally, head to the next tree level. */ current = DOWN(current); current_root = current; } else { /* * Though there are labels in common, the * entire name at this node is not common * with the search name so the search * name does not exist in the tree. */ INSIST(compared == dns_namereln_commonancestor || compared == dns_namereln_contains); current = NULL; } } } /* * If current is not NULL, NOEXACT is not disallowing exact matches, * and either the node has data or an empty node is ok, return * ISC_R_SUCCESS to indicate an exact match. */ if (current != NULL && (options & DNS_RBTFIND_NOEXACT) == 0 && (DATA(current) != NULL || (options & DNS_RBTFIND_EMPTYDATA) != 0)) { /* * Found an exact match. */ chain->end = current; chain->level_matches = chain->level_count; if (foundname != NULL) result = chain_name(chain, foundname, ISC_TRUE); else result = ISC_R_SUCCESS; if (result == ISC_R_SUCCESS) { *node = current; result = saved_result; } else *node = NULL; } else { /* * Did not find an exact match (or did not want one). */ if (*node != NULL) { /* * ... but found a partially matching superdomain. * Unwind the chain to the partial match node * to set level_matches to the level above the node, * and then to derive the name. * * chain->level_count is guaranteed to be at least 1 * here because by definition of finding a superdomain, * the chain is pointed to at least the first subtree. */ chain->level_matches = chain->level_count - 1; while (chain->levels[chain->level_matches] != *node) { INSIST(chain->level_matches > 0); chain->level_matches--; } if (foundname != NULL) { unsigned int saved_count = chain->level_count; chain->level_count = chain->level_matches + 1; result = chain_name(chain, foundname, ISC_FALSE); chain->level_count = saved_count; } else result = ISC_R_SUCCESS; if (result == ISC_R_SUCCESS) result = DNS_R_PARTIALMATCH; } else result = ISC_R_NOTFOUND; if (current != NULL) { /* * There was an exact match but either * DNS_RBTFIND_NOEXACT was set, or * DNS_RBTFIND_EMPTYDATA was set and the node had no * data. A policy decision was made to set the * chain to the exact match, but this is subject * to change if it becomes apparent that something * else would be more useful. It is important that * this case is handled here, because the predecessor * setting code below assumes the match was not exact. */ INSIST(((options & DNS_RBTFIND_NOEXACT) != 0) || ((options & DNS_RBTFIND_EMPTYDATA) == 0 && DATA(current) == NULL)); chain->end = current; } else if ((options & DNS_RBTFIND_NOPREDECESSOR) != 0) { /* * Ensure the chain points nowhere. */ chain->end = NULL; } else { /* * Since there was no exact match, the chain argument * needs to be pointed at the DNSSEC predecessor of * the search name. */ if (compared == dns_namereln_subdomain) { /* * Attempted to follow a down pointer that was * NULL, which means the searched for name was * a subdomain of a terminal name in the tree. * Since there are no existing subdomains to * order against, the terminal name is the * predecessor. */ INSIST(chain->level_count > 0); INSIST(chain->level_matches < chain->level_count); chain->end = chain->levels[--chain->level_count]; } else { isc_result_t result2; /* * Point current to the node that stopped * the search. * * With the hashing modification that has been * added to the algorithm, the stop node of a * standard binary search is not known. So it * has to be found. There is probably a more * clever way of doing this. * * The assignment of current to NULL when * the relationship is *not* dns_namereln_none, * even though it later gets set to the same * last_compared anyway, is simply to not push * the while loop in one more level of * indentation. */ if (compared == dns_namereln_none) current = last_compared; else current = NULL; while (current != NULL) { NODENAME(current, ¤t_name); compared = dns_name_fullcompare( search_name, ¤t_name, &order, &common_labels, &common_bits); last_compared = current; /* * Standard binary search movement. */ if (order < 0) current = LEFT(current); else current = RIGHT(current); } current = last_compared; /* * Reached a point within a level tree that * positively indicates the name is not * present, but the stop node could be either * less than the desired name (order > 0) or * greater than the desired name (order < 0). * * If the stop node is less, it is not * necessarily the predecessor. If the stop * node has a down pointer, then the real * predecessor is at the end of a level below * (not necessarily the next level). * Move down levels until the rightmost node * does not have a down pointer. * * When the stop node is greater, it is * the successor. All the logic for finding * the predecessor is handily encapsulated * in dns_rbtnodechain_prev. In the event * that the search name is less than anything * else in the tree, the chain is reset. * XXX DCL What is the best way for the caller * to know that the search name has * no predecessor? */ if (order > 0) { if (DOWN(current) != NULL) { ADD_LEVEL(chain, current); result2 = move_chain_to_last(chain, DOWN(current)); if (result2 != ISC_R_SUCCESS) result = result2; } else /* * Ah, the pure and simple * case. The stop node is the * predecessor. */ chain->end = current; } else { INSIST(order < 0); chain->end = current; result2 = dns_rbtnodechain_prev(chain, NULL, NULL); if (result2 == ISC_R_SUCCESS || result2 == DNS_R_NEWORIGIN) ; /* Nothing. */ else if (result2 == ISC_R_NOMORE) /* * There is no predecessor. */ dns_rbtnodechain_reset(chain); else result = result2; } } } } return (result);}/* * Get the data pointer associated with 'name'. */isc_result_tdns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name, unsigned int options, dns_name_t *foundname, void **data) { dns_rbtnode_t *node = NULL; isc_result_t result; REQUIRE(data != NULL && *data == NULL); result = dns_rbt_findnode(rbt, name, foundname, &node, NULL, options, NULL, NULL); if (node != NULL && (DATA(node) != NULL || (options & DNS_RBTFIND_EMPTYDATA) != 0)) *data = DATA(node); else result = ISC_R_NOTFOUND; return (result);}/* * Delete a name from the tree of trees. */isc_result_tdns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) { dns_rbtnode_t *node = NULL; isc_result_t result; REQUIRE(VALID_RBT(rbt)); REQUIRE(dns_name_isabsolute(name)); /* * First, find the node. * * When searching, the name might not have an exact match: * consider a.b.a.com, b.b.a.com and c.b.a.com as the only * elements of a tree, which would make layer 1 a single * node tree of "b.a.com" and layer 2 a three node tree of * a, b, and c. Deleting a.com would find only a partial depth * match in the first layer. Should it be a requirement that * that the name to be deleted have data? For now, it is. * * ->dirty, ->locknum and ->references are ignored; they are * solely the province of rbtdb.c. */ result = dns_rbt_findnode(rbt, name, NULL, &node, NULL, DNS_RBTFIND_NOOPTIONS, NULL, NULL); if (result == ISC_R_SUCCESS) { if (DATA(node) != NULL) result = dns_rbt_deletenode(rbt, node, recurse); else result = ISC_R_NOTFOUND; } else if (result == DNS_R_PARTIALMATCH) result = ISC_R_NOTFOUND; return (result);}/* * Remove a node from the tree of trees. * * NOTE WELL: deletion is *not* symmetric with addition; that is, reversing * a sequence of additions to be deletions will not generally get the * tree back to the state it started in. For example, if the addition * of "b.c" caused the node "a.b.c" to be split, pushing "a" to its own level, * then the subsequent deletion of "b.c" will not cause "a" to be pulled up, * restoring "a.b.c". The RBT *used* to do this kind of rejoining, but it * turned out to be a bad idea because it could corrupt an active nodechain * that had "b.c" as one of its levels -- and the RBT has no idea what * nodechains are in use by callers, so it can't even *try* to helpfully * fix them up (which would probably be doomed to failure anyway). * * Similarly, it is possible to leave the tree in a state where a supposedly * deleted node still exists. The first case of this is obvious; take * the tree which has "b.c" on one level, pointing to "a". Now deleted "b.c". * It was just established in the previous paragraph why we can't pull "a" * back up to its parent level. But what happens when "a" then gets deleted? * "b.c" is left hanging around without data or children. This condition * is actually pretty easy to detect, but ... should it really be removed? * Is a chain pointing to it? An iterator? Who knows! (Note that the * references structure member cannot be looked at because it is private to * rbtdb.) This is ugly and makes me unhappy, but after hours of trying to * make it more aesthetically proper and getting nowhere, this is the way it * is going to stay until such time as it proves to be a *real* problem. * * Finally, for reference, note that the original routine that did node * joining was called join_nodes(). It has been excised, living now only * in the CVS history, but comments have been left behind that point to it just * in case someone wants to muck with this some more. * * The one positive aspect of all of this is that joining used to have a * case where it might fail. Without trying to join, now this function always * succeeds. It still returns isc_result_t, though, so the API wouldn't change. */isc_result_tdns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, isc_boolean_t recurse){ dns_rbtnode_t *parent; REQUIRE(VALID_RBT(rbt)); REQUIRE(node != NULL); if (DOWN(node) != NULL) { if (recurse) dns_rbt_deletetree(rbt, DOWN(node)); else { if (DATA(node) != NULL && rbt->data_deleter != NULL) rbt->data_deleter(DATA(node), rbt->deleter_arg); DATA(node) = NULL; /* * Since there is at least one node below this one and * no recursion was requested, the deletion is * complete. The down node from this node might be all * by itself on a single level, so join_nodes() could * be used to collapse the tree (with all the caveats * of the comment at the start of this function). */ return (ISC_R_SUCCESS); } } /* * Note the node that points to the level of the node that is being * deleted. If the deleted node is the top level, parent will be set * to NULL. */ parent = find_up(node); /* * This node now has no down pointer (either because it didn't * have one to start, or because it was recursively removed). * So now the node needs to be removed from this level. */ dns_rbt_deletefromlevel(node, parent == NULL ? &rbt->root : &DOWN(parent)); if (DATA(node) != NULL && rbt->data_deleter != NULL) rbt->data_deleter(DATA(node), rbt->deleter_arg); unhash_node(rbt, node); isc_mem_put(rbt->mctx, node, NODE_SIZE(node)); rbt->nodecount--; /* * There are now two special cases that can exist that would * not have existed if the tree had been created using only * the names that now exist in it. (This is all related to * join_nodes() as described in this function's introductory comment.) * Both cases exist when the deleted node's parent (the node * that pointed to the deleted node's level) is not null but * it has no data: parent != NULL && DATA(parent) == NULL. * * The first case is that the deleted node was the last on its level: * DOWN(parent) == NULL. This case can only exist if the parent was * previously deleted -- and so now, apparently, the parent should go * away. That can't be done though because there might be external * references to it, such as through a nodechain. * * The other case also involves a parent with no data, but with the * deleted node being the next-to-last node instead of the last: * LEFT(DOWN(parent)) == NULL && RIGHT(DOWN(parent)) == NULL. * Presumably now the remaining node on the level should be joined * with the parent, but it's already been described why that can't be * done. */ /* * This function never fails. */ return (ISC_R_SUCCESS);}voiddns_rbt_namefromnode(dns_rbtnode_t *node, dns_name_t *name) { REQUIRE(node != NULL); REQUIRE(name != NULL); REQUIRE(name->offsets == NULL); NODENAME(node, name);}isc_result_tdns_rbt_fullnamefromnode(dns_rbtnode_t *node, dns_name_t *name) { dns_name_t current; isc_result_t result; REQUIRE(node != NULL); REQUIRE(name != NULL); REQUIRE(name->buffer != NULL); dns_name_init(¤t, NULL); dns_name_reset(name); do { INSIST(node != NULL); NODENAME(node, ¤t); result = dns_name_concatenate(name, ¤t, name, NULL); if (result != ISC_R_SUCCESS) break; node = find_up(node); } while (! dns_name_isabsolute(name)); return (result);}char *dns_rbt_formatnodename(dns_rbtnode_t *node, char *printname, unsigned int size){ dns_fixedname_t fixedname; dns_name_t *name; isc_result_t result; REQUIRE(node != NULL); REQUIRE(printname != NULL); dns_fixedname_init(&fixedname); name = dns_fixedname_name(&fixedname); result = dns_rbt_fullnamefromnode(node, name); if (result == ISC_R_SUCCESS) dns_name_format(name, printname, size); else snprintf(printname, size, "<error building name: %s>", dns_result_totext(result)); return (printname);}static isc_result_tcreate_node(isc_mem_t *mctx, dns_name_t *name, dns_rbtnode_t **nodep) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -