Browse Source

[1803] Implement the previousNode for found nodes

Slight change was done in the test, as the test can't find the "hidden"
nodes using find. Also, we need to clear the node chain manually between
reusing it.
Michal 'vorner' Vaner 13 years ago
parent
commit
7287394dba
2 changed files with 125 additions and 37 deletions
  1. 89 0
      src/lib/datasrc/rbtree.h
  2. 36 37
      src/lib/datasrc/tests/rbtree_unittest.cc

+ 89 - 0
src/lib/datasrc/rbtree.h

@@ -263,6 +263,23 @@ private:
     /// This method never throws an exception.
     const RBNode<T>* successor() const;
 
+    /// \brief return the next node which is smaller than current node
+    /// in the same subtree
+    ///
+    /// The predecessor for this node is the next smaller node in terms of
+    /// the DNSSEC order relation within the same single subtree.
+    /// Note that it may NOT be the next smaller node in the entire RBTree;
+    /// RBTree is a tree in tree, and the real next node may reside in
+    /// an upper or lower subtree of the subtree where this node belongs.
+    /// For example, if the predecessor node has a sub domain, the real next
+    /// node is the largest node in the sub domain tree.
+    ///
+    /// If this node is the smallest node within the subtree, this method
+    /// returns \c NULL_NODE().
+    ///
+    /// This method never throws an exception.
+    const RBNode<T>* predecessor() const;
+
     /// \name Data to maintain the rbtree structure.
     //@{
     RBNode<T>*  parent_;
@@ -357,6 +374,32 @@ RBNode<T>::successor() const {
     return (parent);
 }
 
+// This is just mirror image of the above. It works the same way.
+// Any idea how to reuse the code?
+template <typename T>
+const RBNode<T>*
+RBNode<T>::predecessor() const {
+    const RBNode<T>* current = this;
+    // If it has left node, the predecessor is the right-most node of the left
+    // subtree.
+    if (left_ != NULL_NODE()) {
+        current = left_;
+        while (current->right_ != NULL_NODE()) {
+            current = current->right_;
+        }
+        return (current);
+    }
+
+    // Otherwise go up until we find the first right branch on our path to
+    // root.  If found, the parent of the branch is the predecessor.
+    // Otherwise, we return the null node
+    const RBNode<T>* parent = current->parent_;
+    while (parent != NULL_NODE() && current == parent->left_) {
+        current = parent;
+        parent = parent->parent_;
+    }
+    return (parent);
+}
 
 /// \brief RBTreeNodeChain stores detailed information of \c RBTree::find()
 /// result.
@@ -1106,6 +1149,52 @@ RBTree<T>::nextNode(RBTreeNodeChain<T>& node_path) const {
     return (NULL);
 }
 
+template <typename T>
+const RBNode<T>*
+RBTree<T>::previousNode(RBTreeNodeChain<T>& node_path) const {
+    if (node_path.isEmpty()) {
+        isc_throw(isc::BadValue, "RBTree::nextNode is given an empty chain");
+    }
+
+    const RBNode<T>* node(node_path.top());
+
+    // Try going left in this tree
+    node = node->predecessor();
+    if (node == NULLNODE) {
+        // We are the smallest ones in this tree. We go one level
+        // up. That one is the smaller one than us.
+
+        if (node_path.getLevelCount() == 1) {
+            // This was the root of the tree. We can't go up, sorry, we end.
+            return (NULL);
+        }
+
+        node_path.pop();
+        return (node_path.top());
+    }
+
+    // Exchange the node at the top of the path, as we move horizontaly
+    // through the domain tree
+    node_path.pop();
+    node_path.push(node);
+
+    // Try going as deep as possible, keeping on the right side of the trees
+    while (node->down_ != NULLNODE) {
+        // Move to the tree below
+        node = node->down_;
+        // And get as much to the right of the tree as possible
+        while (node->right_ != NULLNODE) {
+            node = node->right_;
+        }
+        // Now, we found the right-most node in the sub-tree, we need to
+        // include it in the path
+        node_path.push(node);
+    }
+
+    // Now, if the current node has no down_ pointer any more, it's the
+    // correct one.
+    return (node);
+}
 
 template <typename T>
 typename RBTree<T>::Result

+ 36 - 37
src/lib/datasrc/tests/rbtree_unittest.cc

@@ -339,6 +339,11 @@ TEST_F(RBTreeTest, getAbsoluteNameError) {
  *               /   \
  *              o     q
  */
+const char* const names[] = {
+    "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
+    "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f", "g.h", "i.g.h"};
+const size_t name_count(sizeof(names) / sizeof(*names));
+
 TEST_F(RBTreeTest, nextNode) {
     const char* const names[] = {
         "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
@@ -359,29 +364,26 @@ TEST_F(RBTreeTest, nextNode) {
     EXPECT_EQ(static_cast<void*>(NULL), node);
 }
 
-// Check the previousNode
-TEST_F(RBTreeTest, previousNode) {
-    // First, iterate the whole tree from the end to the beginning.
-    const char* const names[] = {
-        "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
-        "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f", "g.h", "i.g.h"};
-    const size_t name_count(sizeof(names) / sizeof(*names));
-    RBTreeNodeChain<int> node_path;
-    const RBNode<int>* node(NULL);
-    EXPECT_EQ(RBTree<int>::EXACTMATCH,
-              rbtree.find<void*>(Name(names[name_count - 1]), &node, node_path,
-                                 NULL, NULL));
-    for (size_t i = name_count; i > 0; --i) {
+// Just walk until the beginning of the tree and check it is OK
+void
+previousWalk(RBTree<int>& rbtree, const RBNode<int>* node,
+             RBTreeNodeChain<int>& node_path, size_t position)
+{
+    for (size_t i(position); i > 0; --i) {
         EXPECT_NE(static_cast<void*>(NULL), node);
         EXPECT_EQ(Name(names[i - 1]), node_path.getAbsoluteName());
         // Find the node at the path and check the value is the same
         // (that it really returns the correct corresponding node)
-        const RBNode<int>* node2(NULL);
-        RBTreeNodeChain<int> node_path2;
-        EXPECT_EQ(RBTree<int>::EXACTMATCH,
-                  rbtree.find<void*>(Name(names[i - 1]), &node2, node_path2,
-                                     NULL, NULL));
-        EXPECT_EQ(node, node2);
+        //
+        // The "hidden" nodes can not be found
+        if (node->getData()) {
+            const RBNode<int>* node2(NULL);
+            RBTreeNodeChain<int> node_path2;
+            EXPECT_EQ(RBTree<int>::EXACTMATCH,
+                      rbtree.find<void*>(Name(names[i - 1]), &node2,
+                                         node_path2, NULL, NULL));
+            EXPECT_EQ(node, node2);
+        }
         node = rbtree.previousNode(node_path);
     }
 
@@ -389,28 +391,25 @@ TEST_F(RBTreeTest, previousNode) {
     EXPECT_EQ(static_cast<void*>(NULL), node);
     // This is all the same then
     EXPECT_EQ(static_cast<void*>(NULL), node);
+}
 
-    // Now, start somewhere in the middle, but within the real node.
+// Check the previousNode
+TEST_F(RBTreeTest, previousNode) {
+    // First, iterate the whole tree from the end to the beginning.
+    RBTreeNodeChain<int> node_path;
+    const RBNode<int>* node(NULL);
     EXPECT_EQ(RBTree<int>::EXACTMATCH,
-              rbtree.find<void*>(Name(names[3]), &node, node_path,
+              rbtree.find<void*>(Name(names[name_count - 1]), &node, node_path,
                                  NULL, NULL));
+    previousWalk(rbtree, node, node_path, name_count);
+    node_path.clear();
 
-    for (size_t i = 4; i > 0; --i) {
-        EXPECT_NE(static_cast<void*>(NULL), node);
-        EXPECT_EQ(Name(names[i - 1]), node_path.getAbsoluteName());
-        // Find the node at the path and check the value is the same
-        // (that it really returns the correct corresponding node)
-        const RBNode<int>* node2(NULL);
-        RBTreeNodeChain<int> node_path2;
-        EXPECT_EQ(RBTree<int>::EXACTMATCH,
-                  rbtree.find<void*>(Name(names[i - 1]), &node2, node_path2,
-                                     NULL, NULL));
-        EXPECT_EQ(node, node2);
-        node = rbtree.previousNode(node_path);
-    }
-
-    // We should have reached the end of the tree.
-    EXPECT_EQ(static_cast<void*>(NULL), node);
+    // Now, start somewhere in the middle, but within the real node.
+    EXPECT_EQ(RBTree<int>::EXACTMATCH,
+              rbtree.find<void*>(Name(names[4]), &node, node_path,
+                                 NULL, NULL));
+    previousWalk(rbtree, node, node_path, 5);
+    node_path.clear();
 
     // TODO: The tests where we start in between of the nodes somewhere
 }