📄 rbt.c
字号:
* this is the only real use of chains in the * function. It could be done instead with * a simple integer variable, but I am pressed * for time. */ if (chain.level_count == (sizeof(chain.levels) / sizeof(*chain.levels))) { result = ISC_R_NOSPACE; break; } /* * Split the name into two parts, a prefix * which is the not-in-common parts of the * two names and a suffix that is the common * parts of them. */ dns_name_split(¤t_name, common_labels, prefix, suffix); result = create_node(rbt->mctx, suffix, &new_current); if (result != ISC_R_SUCCESS) break; /* * Reproduce the tree attributes of the * current node. */ new_current->is_root = current->is_root; PARENT(new_current) = PARENT(current); LEFT(new_current) = LEFT(current); RIGHT(new_current) = RIGHT(current); COLOR(new_current) = COLOR(current); /* * Fix pointers that were to the current node. */ if (parent != NULL) { if (LEFT(parent) == current) LEFT(parent) = new_current; else RIGHT(parent) = new_current; } if (LEFT(new_current) != NULL) PARENT(LEFT(new_current)) = new_current; if (RIGHT(new_current) != NULL) PARENT(RIGHT(new_current)) = new_current; if (*root == current) *root = new_current; NAMELEN(current) = prefix->length; OFFSETLEN(current) = prefix->labels; memcpy(OFFSETS(current), prefix->offsets, prefix->labels); PADBYTES(current) += (current_name.length - prefix->length) + (current_name.labels - prefix->labels); /* * Set up the new root of the next level. * By definition it will not be the top * level tree, so clear DNS_NAMEATTR_ABSOLUTE. */ current->is_root = 1; PARENT(current) = new_current; DOWN(new_current) = current; root = &DOWN(new_current); ADD_LEVEL(&chain, new_current); LEFT(current) = NULL; RIGHT(current) = NULL; MAKE_BLACK(current); ATTRS(current) &= ~DNS_NAMEATTR_ABSOLUTE; rbt->nodecount++; hash_node(rbt, new_current); if (common_labels == dns_name_countlabels(add_name)) { /* * The name has been added by pushing * the not-in-common parts down to * a new level. */ *nodep = new_current; return (ISC_R_SUCCESS); } else { /* * The current node has no data, * because it is just a placeholder. * Its data pointer is already NULL * from create_node()), so there's * nothing more to do to it. */ /* * The not-in-common parts of the new * name will be inserted into the new * level following this loop (unless * result != ISC_R_SUCCESS, which * is tested after the loop ends). */ dns_name_split(add_name, common_labels, add_name, NULL); break; } } } } while (child != NULL); if (result == ISC_R_SUCCESS) result = create_node(rbt->mctx, add_name, &new_current); if (result == ISC_R_SUCCESS) { dns_rbt_addonlevel(new_current, current, order, root); rbt->nodecount++; *nodep = new_current; hash_node(rbt, new_current); } return (result);}/* * Add a name to the tree of trees, associating it with some data. */isc_result_tdns_rbt_addname(dns_rbt_t *rbt, dns_name_t *name, void *data) { isc_result_t result; dns_rbtnode_t *node; REQUIRE(VALID_RBT(rbt)); REQUIRE(dns_name_isabsolute(name)); node = NULL; result = dns_rbt_addnode(rbt, name, &node); /* * dns_rbt_addnode will report the node exists even when * it does not have data associated with it, but the * dns_rbt_*name functions all behave depending on whether * there is data associated with a node. */ if (result == ISC_R_SUCCESS || (result == ISC_R_EXISTS && DATA(node) == NULL)) { DATA(node) = data; result = ISC_R_SUCCESS; } return (result);}/* * Find the node for "name" in the tree of trees. */isc_result_tdns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname, dns_rbtnode_t **node, dns_rbtnodechain_t *chain, unsigned int options, dns_rbtfindcallback_t callback, void *callback_arg){ dns_rbtnode_t *current, *last_compared, *current_root; dns_rbtnodechain_t localchain; dns_name_t *search_name, current_name, *callback_name; dns_fixedname_t fixedcallbackname, fixedsearchname; dns_namereln_t compared; isc_result_t result, saved_result; unsigned int common_labels; int order; REQUIRE(VALID_RBT(rbt)); REQUIRE(dns_name_isabsolute(name)); REQUIRE(node != NULL && *node == NULL); REQUIRE((options & (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR)) != (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR)); /* * If there is a chain it needs to appear to be in a sane state, * otherwise a chain is still needed to generate foundname and * callback_name. */ if (chain == NULL) { options |= DNS_RBTFIND_NOPREDECESSOR; chain = &localchain; dns_rbtnodechain_init(chain, rbt->mctx); } else dns_rbtnodechain_reset(chain); if (rbt->root == NULL) return (ISC_R_NOTFOUND); else { /* * Appease GCC about variables it incorrectly thinks are * possibly used uninitialized. */ compared = dns_namereln_none; last_compared = NULL; } dns_fixedname_init(&fixedcallbackname); callback_name = dns_fixedname_name(&fixedcallbackname); /* * search_name is the name segment being sought in each tree level. * By using a fixedname, the search_name will definitely have offsets * for use by any splitting. * By using dns_name_clone, no name data should be copied thanks to * the lack of bitstring labels. */ dns_fixedname_init(&fixedsearchname); search_name = dns_fixedname_name(&fixedsearchname); dns_name_clone(name, search_name); dns_name_init(¤t_name, NULL); saved_result = ISC_R_SUCCESS; current = rbt->root; current_root = rbt->root; while (current != NULL) { NODENAME(current, ¤t_name); compared = dns_name_fullcompare(search_name, ¤t_name, &order, &common_labels); last_compared = current; if (compared == dns_namereln_equal) break; if (compared == dns_namereln_none) {#ifdef DNS_RBT_USEHASH dns_name_t hash_name; dns_rbtnode_t *hnode; dns_rbtnode_t *up_current; unsigned int nlabels; unsigned int tlabels = 1; unsigned int hash; /* * If there is no hash table, hashing can't be done. */ if (rbt->hashtable == NULL) goto nohash; /* * The case of current != current_root, that * means a left or right pointer was followed, * only happens when the algorithm fell through to * the traditional binary search because of a * bitstring label. Since we dropped the bitstring * support, this should not happen. */ INSIST(current == current_root); nlabels = dns_name_countlabels(search_name); /* * current_root is the root of the current level, so * it's parent is the same as it's "up" pointer. */ up_current = PARENT(current_root); dns_name_init(&hash_name, NULL); hashagain: dns_name_getlabelsequence(search_name, nlabels - tlabels, tlabels, &hash_name); hash = HASHVAL(up_current) + dns_name_hashbylabel(&hash_name, ISC_FALSE); for (hnode = rbt->hashtable[hash % rbt->hashsize]; hnode != NULL; hnode = hnode->hashnext) { dns_name_t hnode_name; if (hash != HASHVAL(hnode)) continue; if (find_up(hnode) != up_current) continue; dns_name_init(&hnode_name, NULL); NODENAME(hnode, &hnode_name); if (dns_name_equal(&hnode_name, &hash_name)) break; } if (hnode != NULL) { current = hnode; /* * This is an optimization. If hashing found * the right node, the next call to * dns_name_fullcompare() would obviously * return _equal or _subdomain. Determine * which of those would be the case by * checking if the full name was hashed. Then * make it look like dns_name_fullcompare * was called and jump to the right place. */ if (tlabels == nlabels) { compared = dns_namereln_equal; break; } else { common_labels = tlabels; compared = dns_namereln_subdomain; goto subdomain; } } if (tlabels++ < nlabels) goto hashagain; /* * All of the labels have been tried against the hash * table. Since we dropped the support of bitstring * labels, the name isn't in the table. */ current = NULL; continue; nohash:#endif /* DNS_RBT_USEHASH */ /* * Standard binary search tree movement. */ if (order < 0) current = LEFT(current); else current = RIGHT(current); } else { /* * The names have some common suffix labels. * * If the number in common are equal in length to * the current node's name length, then follow the * down pointer and search in the new tree. */ if (compared == dns_namereln_subdomain) { subdomain: /* * Whack off the current node's common parts * for the name to search in the next level. */ dns_name_split(search_name, common_labels, search_name, NULL); /* * This might be the closest enclosing name. */ if (DATA(current) != NULL || (options & DNS_RBTFIND_EMPTYDATA) != 0) *node = current; /* * Point the chain to the next level. This * needs to be done before 'current' is pointed * there because the callback in the next * block of code needs the current 'current', * but in the event the callback requests that * the search be stopped then the * DNS_R_PARTIALMATCH code at the end of this * function needs the chain pointed to the * next level. */ ADD_LEVEL(chain, current); /* * The caller may want to interrupt the * downward search when certain special nodes * are traversed. If this is a special node, * the callback is used to learn what the * caller wants to do. */ if (callback != NULL && 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -