📄 rbt.c
字号:
} 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); 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; } } } } ENSURE(*node == NULL || DNS_RBTNODE_VALID(*node)); 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(DNS_RBTNODE_VALID(node)); if (DOWN(node) != NULL) { if (recurse) RUNTIME_CHECK(dns_rbt_deletetree(rbt, DOWN(node)) == ISC_R_SUCCESS); 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);#if DNS_RBT_USEMAGIC node->magic = 0;#endif 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(DNS_RBTNODE_VALID(node)); 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(DNS_RBTNODE_VALID(node)); 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(DNS_RBTNODE_VALID(node)); 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) { 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);#if DNS_RBT_USEMAGIC node->magic = DNS_RBTNODE_MAGIC;#endif *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 voidrehash(dns_rbt_t *rbt) { unsigned int oldsize; dns_rbtnode_t **oldtable; dns_rbtnode_t *node; unsigned int hash; unsigned int i; oldsize = rbt->hashsize; oldtable = rbt->hashtable; rbt->hashsize *= 2 + 1; rbt->hashtable = isc_mem_get(rbt->mctx, rbt->hashsize * sizeof(dns_rbtnode_t *)); if (rbt->hashtable == NULL) { rbt->hashtable = oldtable; rbt->hashsize = oldsize; return; } for (i = 0; i < rbt->hashsize; i++) rbt->hashtable[i] = NULL; for (i = 0; i < oldsize; i++) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -