Browse Source

[trac517] added more tests and a new method for RBTreeNodeChain.

 - the new tests confirm the maximum possible levels of RBTree/node chain.
 - the new method: getLevelCount() helps the tests.

Also added some more documentation.
JINMEI Tatuya 14 years ago
parent
commit
873ab3f9db
2 changed files with 79 additions and 11 deletions
  1. 28 10
      src/lib/datasrc/rbtree.h
  2. 51 1
      src/lib/datasrc/tests/rbtree_unittest.cc

+ 28 - 10
src/lib/datasrc/rbtree.h

@@ -309,15 +309,21 @@ RBNode<T>::successor() const {
 /// \brief RBTreeNodeChain is used to keep track of the sequence of
 /// nodes to reach any given node from the root of RBTree.
 ///
-/// RBNode does not have "up" pointers in them (for memory usage reasons)
-/// so there is no way to find the path back to the root from any given
-/// RBNode.
+/// Currently, RBNode does not have "up" pointers in them (i.e., back pointers
+/// from the root of one level of tree of trees to the node in the parent
+/// tree whose down pointer points to that root node) for memory usage
+/// reasons, so there is no way to find the path back to the root from any
+/// given RBNode.
+/// Note: this design may change in future versions.  In particular, it's
+/// quite likely we want to have that pointer if we want to optimize name
+/// compression by exploiting the structure of the zone.  If and when that
+/// happens we should also revisit the need for the chaining.
 ///
 /// RBTreeNodeChain is constructed and manipulate only by \c RBTree.
 /// \c RBTree uses it as an inner data structure to iterate over the whole
 /// RBTree.
-/// This is the reason why only construct function and \c getAbsoluteName
-/// function is public and others are private.
+/// This is the reason why manipulation methods such as \c push() and \c pop()
+/// are private.
 template <typename T>
 class RBTreeNodeChain {
     /// RBTreeNodeChain is initialized by RBTree, only RBTree has
@@ -350,10 +356,18 @@ public:
     }
     //@}
 
+    /// \brief Return the number of levels stored in the chain.
+    ///
+    /// It's equal to the number of nodes in the chain; for an empty
+    /// chain, 0 will be returned.
+    ///
+    /// \exception None
+    unsigned int getLevelCount() const { return (node_count_); }
+
     /// \brief return the absolute name for the node which current
     /// RBTreeNodeChain traces.
     ///
-    /// \exception RBTreeNodeChain has to be initialized by RBtree,
+    /// \exception RBTreeNodeChain has to be initialized by RBTree,
     /// otherwise InvalidNodeChain exception will be thrown
     isc::dns::Name getAbsoluteName() const {
         const RBNode<T>* top_node = top();
@@ -426,10 +440,14 @@ private:
     }
 
 private:
-    /// the max label count for one domain name is 128
-    /// since each node in rbtree stores at least one label
-    /// so the max node count for one node chain is 128
-    const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS;
+    // The max label count for one domain name is Name::MAX_LABELS (128).
+    // Since each node in rbtree stores at least one label, and the root
+    // name always shares the same level with some others (which means
+    // all top level nodes except the one for the root name contain at least
+    // two labels), the possible maximum level is MAX_LABELS - 1.
+    // It's also the possible maximum nodes stored in a chain.
+    const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS - 1;
+
     const RBNode<T>* nodes_[RBT_MAX_LEVEL];
     int node_count_;
 };

+ 51 - 1
src/lib/datasrc/tests/rbtree_unittest.cc

@@ -30,6 +30,10 @@ using namespace isc::dns;
 using isc::UnitTestUtil;
 using namespace isc::datasrc;
 
+// XXX: some compilers cannot find class static constants used in
+// EXPECT_xxx macros, for which we need an explicit empty definition.
+const size_t Name::MAX_LABELS;
+
 /* The initial structure of rbtree
  *
  *             b
@@ -151,7 +155,6 @@ TEST_F(RBTreeTest, insertNames) {
     EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("n"), &rbtnode));
 }
 
-
 TEST_F(RBTreeTest, findName) {
     // find const rbtnode
     // exact match
@@ -237,6 +240,53 @@ TEST_F(RBTreeTest, callback) {
     EXPECT_FALSE(callback_called);
 }
 
+TEST_F(RBTreeTest, chainLevel) {
+    RBTreeNodeChain<int> chain;
+
+    // by default there should be no level in the chain.
+    EXPECT_EQ(0, chain.getLevelCount());
+
+    // insert one node to the tree and find it.  there should be exactly
+    // one level in the chain.
+    RBTree<int> tree(true);
+    Name node_name(Name::ROOT_NAME());
+    EXPECT_EQ(RBTree<int>::SUCCESS, tree.insert(node_name, &rbtnode));
+    EXPECT_EQ(RBTree<int>::EXACTMATCH,
+              tree.find<void*>(node_name, &crbtnode, chain, NULL, NULL));
+    EXPECT_EQ(1, chain.getLevelCount());
+
+    /*
+     * Now creating a possibly deepest tree with MAX_LABELS - 1 levels.
+     * it should look like:
+     *            a
+     *           /|
+     *         (.)a
+     *            |
+     *            a
+     *            : (MAX_LABELS - 1) "a"'s
+     *
+     * then confirm that find() for the deepest name succeeds without any
+     * disruption, and the resulting chain has the expected level.
+     * Note that "a." and the root name (".") belong to the same level.
+     * So the possible maximum level is MAX_LABELS - 1, not MAX_LABELS.
+     */
+    for (unsigned int i = 1; i < Name::MAX_LABELS; ++i) {
+        node_name = Name("a.").concatenate(node_name);
+        EXPECT_EQ(RBTree<int>::SUCCESS, tree.insert(node_name, &rbtnode));
+        RBTreeNodeChain<int> found_chain;
+        EXPECT_EQ(RBTree<int>::EXACTMATCH,
+                  tree.find<void*>(node_name, &crbtnode, found_chain,
+                                   NULL, NULL));
+        EXPECT_EQ(i, found_chain.getLevelCount());
+    }
+
+    // Confirm the last inserted name has the possible maximum length with
+    // maximum label count.  This confirms the rbtree and chain level cannot
+    // be larger.
+    EXPECT_EQ(Name::MAX_LABELS, node_name.getLabelCount());
+    EXPECT_THROW(node_name.concatenate(Name("a.")), TooLongName);
+}
+
 /*
  *the domain order should be:
  * 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,