Browse Source

add document for rbtreenodechain

hanfeng 14 years ago
parent
commit
6031b9096d
2 changed files with 161 additions and 85 deletions
  1. 154 56
      src/lib/datasrc/rbtree.h
  2. 7 29
      src/lib/datasrc/tests/rbtree_unittest.cc

+ 154 - 56
src/lib/datasrc/rbtree.h

@@ -26,7 +26,7 @@
 #include <dns/name.h>
 #include <boost/utility.hpp>
 #include <boost/shared_ptr.hpp>
-#include <exception>
+#include <exceptions/exceptions.h>
 #include <ostream>
 #include <algorithm>
 #include <cassert>
@@ -55,6 +55,28 @@ operator-(const isc::dns::Name& super_name, const isc::dns::Name& sub_name) {
 }
 }
 
+/// \brief invalid RBTreeNodeChain exception
+///
+/// Normally, RBTreeNodeChain is initialized and manipuate by RBTRee,
+/// this is thrown when using one RBTreeNodeChain which is created by without
+/// initialized by RBTree
+struct InvalidNodeChain : public isc::Exception {
+    InvalidNodeChain(const char* file, size_t line, const char* what) :
+        Exception(file, line, what){}
+};
+
+/// \brief too long RBTreeNodeChain exception
+///
+/// RBTreeNodeChain has length limitation as 253, this exception is thrown
+/// when RBTreeNodeChain is longer than that limitation which is caused by
+/// too deep RBTree.
+struct TooLongNodeChain : public isc::Exception {
+    TooLongNodeChain(const char *file, size_t line, const char *what) :
+        Exception(file, line, what){}
+};
+
+template <typename T>
+class RBTree;
 /// \brief \c RBNode is used by RBTree to store any data related to one domain
 ///     name.
 ///
@@ -80,8 +102,7 @@ class RBNode : public boost::noncopyable {
 private:
     /// The RBNode is meant for use from within RBTree, so it has access to
     /// it.
-    template <typename U>
-    friend class RBTree;
+    friend class RBTree<T>;
 
     /// \name Constructors
     ///
@@ -282,54 +303,127 @@ RBNode<T>::successor() const {
 }
 
 
-/// A chain is used to keep track of the sequence of nodes to reach any
-/// given node from the root of RBTree.  RBNode did not have parent
-/// pointers in them (for memory usage reasons) so there was no way to find
-/// the path back to the root from any given node.
+/// \brief RBTreeNodeChain is used to keep track of the sequence of 
+/// nodes to reach any given node from the root of RBTree.  
+///
+/// RBNode did not have "up" pointers in them (for memory usage reasons) 
+/// so there was no way to find the path back to the root from any given 
+/// RBNode. 
+///
+/// RBTreeNodeChain is constructed and manipulate only by \c RBTree. 
+/// RBTree use it as a inner data struct to iterator the whole RBTree.
+/// This is the reason why only construct function and getAbsoluteName
+/// function is public and others are private
 template <typename T>
-class NodeChain {
-    public:
-        NodeChain() : node_count_(0) {}
-        NodeChain(const NodeChain<T>& node_path) {
-            node_count_ = node_path.node_count_;
-            if (node_count_ > 0) {
-                memcpy(nodes_, node_path.nodes_,
-                       node_count_ * sizeof(RBNode<T>*));
-            }
+class RBTreeNodeChain {
+    /// RBTreeNodeChain is initialized by RBTree, only RBTree has 
+    /// knowledge to manipuate it.
+    friend class RBTree<T>;
+public:
+    /// \name Constructors
+    /// 
+    /// \note empty RBTreeNodeChain isn't meaningful, use it
+    /// as parameter for functions like getAbsoluteName or
+    /// nextNode in \c RBTree will throw InvalidNodeChain exception
+    /// empty RBTreeNodeChain has to be initialized by RBTree, through
+    /// \c find function call.
+    //{@
+    RBTreeNodeChain() : node_count_(0) {}
+    RBTreeNodeChain(const RBTreeNodeChain<T>& node_path) {
+        node_count_ = node_path.node_count_;
+        if (node_count_ > 0) {
+            memcpy(nodes_, node_path.nodes_,
+                    node_count_ * sizeof(RBNode<T>*));
         }
+    }
 
-        NodeChain<T>& operator=(const NodeChain<T>& node_path) {
-            node_count_ = node_path.node_count_;
-            if (node_count_ > 0) {
-                memcpy(nodes_, node_path.nodes_,
-                       node_count_ * sizeof(RBNode<T>*));
-            }
-            return (*this);
+    RBTreeNodeChain<T>& 
+    operator=(const RBTreeNodeChain<T>& node_path) {
+        node_count_ = node_path.node_count_;
+        if (node_count_ > 0) {
+            memcpy(nodes_, node_path.nodes_,
+                    node_count_ * sizeof(RBNode<T>*));
+        }
+        return (*this);
+    }
+    //@}
+    
+    /// \brief return the absolute name for the node which current 
+    /// RBTreeNodeChain traces.
+    ///
+    /// \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();
+        isc::dns::Name absolute_name = top_node->getName();
+        int node_count = node_count_ - 1;
+        while (node_count > 0) {
+            top_node = nodes_[node_count - 1];
+            absolute_name = absolute_name.concatenate(top_node->getName());
+            --node_count;
         }
+        return (absolute_name);
+    }
 
-        bool isEmpty() const { return (node_count_ == 0); }
+private:
 
-        const RBNode<T>* top() const {
-            assert(node_count_ > 0);
-            return (nodes_[node_count_ - 1]);
-        }
+    /// \brief return whther node chain has node in it.
+    ///
+    /// \exception None
+    bool isEmpty() const { return (node_count_ == 0); }
 
-        void pop() {
-            assert(node_count_ > 0);
-            --node_count_;
+    /// \brief return the top node for the node chain
+    /// 
+    /// RBTreeNodeChain store all the nodes along top node to
+    /// root node of RBTree
+    ///
+    /// \exception If RBTreeNodeChain isn't initialized by RBTree
+    /// InvalidNodeChain exception will be thrown
+    const RBNode<T>* top() const {
+        if (isEmpty()) {
+            isc_throw(InvalidNodeChain, "empty node chain");
         }
+        return (nodes_[node_count_ - 1]);
+    }
 
-        void push(const RBNode<T>* node) {
-            assert(node_count_ < RBT_MAX_LEVEL);
-            nodes_[node_count_++] = node;
+    /// \brief pop the top node from the node chain
+    ///
+    /// After pop, up/super node of original top node will be
+    /// the top node
+    ///
+    /// \exception If RBTreeNodeChain isn't initialized by RBTree
+    /// InvalidNodeChain exception will be thrown
+    void pop() {
+        if (isEmpty()) {
+            isc_throw(InvalidNodeChain, "empty node chain");
         }
+        --node_count_;
+    }
+   
+    /// \brief add the node into the node chain
+    ///
+    /// If the node chain isn't empty, the node should be
+    /// the sub domain of the original top node in node chain
+    /// otherwise the node should be the root node of RBTree.
+    ///
+    /// \exception If RBTreeNodeChain is initialized by RBTree who 
+    /// is too deep with level bigger than RBT_MAX_LEVEL, the node 
+    /// chain for leaf node will longer than RBT_MAX_LEVEL then
+    /// exception TooLongNodeChain will be thrown
+    void push(const RBNode<T>* node) {
+        if (node_count_ >= RBT_MAX_LEVEL) {
+            isc_throw(TooLongNodeChain, "node chain is too long");
+        }
+        nodes_[node_count_++] = node;
+    }
 
-    private:
-        /// DNSSEC restricts the number of "logical" labels in a name to
-        /// 255, meaning only 254 pointers are needed in the worst case.
-        enum {RBT_MAX_LEVEL = 254};
-        const RBNode<T>* nodes_[RBT_MAX_LEVEL];
-        int node_count_;
+private:
+    /// the max lable count for one domain name is 128
+    /// since each node in rbtree at least store one label
+    /// so the max node count for one node chain is 128
+    const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS;
+    const RBNode<T>* nodes_[RBT_MAX_LEVEL];
+    int node_count_;
 };
 
 
@@ -472,7 +566,7 @@ public:
     ///
     /// Acts as described in the \ref find section.
     Result find(const isc::dns::Name& name, RBNode<T>** node) const {
-        NodeChain<T> node_path;
+        RBTreeNodeChain<T> node_path;
         return (find<void*>(name, node, node_path, NULL, NULL));
     }
 
@@ -481,7 +575,7 @@ public:
     /// Acts as described in the \ref find section, but returns immutable node
     /// pointer.
     Result find(const isc::dns::Name& name, const RBNode<T>** node) const {
-        NodeChain<T> node_path;
+        RBTreeNodeChain<T> node_path;
         RBNode<T> *target_node = NULL;
         Result ret = (find<void*>(name, &target_node, node_path, NULL, NULL));
         if (ret != NOTFOUND) {
@@ -521,7 +615,7 @@ public:
     /// node towards the root.
     /// For example, if we look for o.w.y.d.e.f in the example \ref diagram,
     /// \c node_path will contain w.y and d.e.f; the \c top() node of the
-    /// chain will be w.f, and d.e.f will be stored below it.
+    /// chain will be o, w.f and d.e.f will be stored below it.
     ///
     /// This feature can be used to get the absolute name for a node;
     /// to do so, we need to travel upside from the node toward the root,
@@ -536,8 +630,7 @@ public:
     /// \param node On success (either \c EXACTMATCH or \c PARTIALMATCH)
     ///     it will store a pointer to the matching node
     /// \param node_path It will store all the ancestor nodes in the RBTree
-    ///        from the found node to the root.  The found node won't be
-    ///        stored.
+    ///        from the found node to the root.  The found node is stored.
     /// \param callback If non \c NULL, a call back function to be called
     ///     at marked nodes (see above).
     /// \param callback_arg A caller supplied argument to be passed to
@@ -548,7 +641,7 @@ public:
     template <typename CBARG>
     Result find(const isc::dns::Name& name,
                 RBNode<T>** node,
-                NodeChain<T>& node_path,
+                RBTreeNodeChain<T>& node_path,
                 bool (*callback)(const RBNode<T>&, CBARG),
                 CBARG callback_arg) const;
 
@@ -559,7 +652,7 @@ public:
     template <typename CBARG>
     Result find(const isc::dns::Name& name,
                 const RBNode<T>** node,
-                NodeChain<T>& node_path,
+                RBTreeNodeChain<T>& node_path,
                 bool (*callback)(const RBNode<T>&, CBARG),
                 CBARG callback_arg) const
     {
@@ -581,16 +674,16 @@ public:
     /// of \c RBTree; we can do this by calling this method repeatedly
     /// starting from the root node.
     ///
-    /// \exception None
+    /// \exception If the node_path isn't initalized by find function and not
+    /// get from previous nextNode function call, InvalidNodeChain exception
+    /// will be thrown
     ///
-    /// \param node An \c RBNode for which the next node should be returned
     /// \param node_path A node chain that stores all the nodes along the path
     /// from root to node.
     ///
     /// \return An \c RBNode that is next bigger than \c node; if \c node is
     /// the largest, \c NULL_NODE() will be returned.
-    const RBNode<T>* nextNode(const RBNode<T>* node,
-                              NodeChain<T>& node_path) const;
+    const RBNode<T>* nextNode(RBTreeNodeChain<T>& node_path) const;
 
     /// \brief Get the total number of nodes in the tree
     ///
@@ -742,7 +835,7 @@ template <typename CBARG>
 typename RBTree<T>::Result
 RBTree<T>::find(const isc::dns::Name& target_name,
                 RBNode<T>** target,
-                NodeChain<T>& node_path,
+                RBTreeNodeChain<T>& node_path,
                 bool (*callback)(const RBNode<T>&, CBARG),
                 CBARG callback_arg) const
 {
@@ -759,6 +852,7 @@ RBTree<T>::find(const isc::dns::Name& target_name,
             compare_result.getRelation();
         if (relation == isc::dns::NameComparisonResult::EQUAL) {
             if (needsReturnEmptyNode_ || !node->isEmpty()) {
+                node_path.push(node);
                 *target = node;
                 ret = EXACTMATCH;
             }
@@ -794,23 +888,26 @@ RBTree<T>::find(const isc::dns::Name& target_name,
 
 template <typename T>
 const RBNode<T>*
-RBTree<T>::nextNode(const RBNode<T>* node,
-                    NodeChain<T>& node_path) const
+RBTree<T>::nextNode(RBTreeNodeChain<T>& node_path) const
 {
+    const RBNode<T> *node = node_path.top();
     // if node has sub domain, the next domain is the smallest
     // domain in sub domain tree
     if (node->down_ != NULLNODE) {
-        node_path.push(node);
         const RBNode<T>* left_most = node->down_;
         while (left_most->left_ != NULLNODE) {
             left_most = left_most->left_;
         }
+        node_path.push(left_most);
         return (left_most);
     }
 
+    // node_path go to up level
+    node_path.pop();
     // otherwise found the successor node in current level
     const RBNode<T>* successor = node->successor();
     if (successor != NULLNODE) {
+        node_path.push(successor);
         return (successor);
     }
 
@@ -822,11 +919,12 @@ RBTree<T>::nextNode(const RBNode<T>* node,
         const RBNode<T>* up_node_successor = node_path.top()->successor();
         node_path.pop();
         if (up_node_successor != NULLNODE) {
+            node_path.push(up_node_successor);
             return (up_node_successor);
         }
     }
 
-    return (NULLNODE);
+    return (NULL);
 }
 
 

+ 7 - 29
src/lib/datasrc/tests/rbtree_unittest.cc

@@ -188,7 +188,7 @@ testCallback(const RBNode<int>&, bool* callack_checker) {
 }
 
 TEST_F(RBTreeTest, callback) {
-    NodeChain<int> node_path;
+    RBTreeNodeChain<int> node_path;
     // by default callback isn't enabled
     EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("callback.example"),
                                                   &rbtnode));
@@ -255,39 +255,18 @@ TEST_F(RBTreeTest, callback) {
  *               /   \
  *              o     q
  */
-Name
-nodeAbsoluteName(const RBNode<int>* node,
-                 const NodeChain<int>& node_path)
-{
-    isc::dns::Name absoluteName = node->getName();
-    NodeChain<int> node_path_copy = node_path;
-    while (!node_path_copy.isEmpty()) {
-        absoluteName = absoluteName.concatenate(node_path_copy.top()->getName());
-        node_path_copy.pop();
-    }
-    return (absoluteName);
-}
-
-void
-testNodeAdjacentHelper(const RBTree<int>& tree, const Name& currentDomain,
-                       const Name& nextDomain)
-{
-    NodeChain<int> node_path;
-    const RBNode<int>* node = NULL;
-    EXPECT_EQ(RBTree<int>::EXACTMATCH,
-              tree.find<void*>(currentDomain, &node, node_path, NULL, NULL));
-    node = tree.nextNode(node, node_path);
-    EXPECT_EQ(nextDomain, nodeAbsoluteName(node, node_path));
-}
-
 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",
         "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 int name_count = sizeof(names) / sizeof(names[0]);
+    RBTreeNodeChain<int> node_path;
+    const RBNode<int>* node = NULL;
+    EXPECT_EQ(RBTree<int>::EXACTMATCH,
+              rbtree.find<void*>(Name(names[0]), &node, node_path, NULL, NULL));
     for (int i = 0; i < name_count - 1; ++i) {
-        testNodeAdjacentHelper(rbtree_expose_empty_node, Name(names[i]),
-                               Name(names[i + 1]));
+        EXPECT_EQ(Name(names[i]), node_path.getAbsoluteName());
+        rbtree.nextNode(node_path);
     }
 }
 
@@ -327,5 +306,4 @@ TEST_F(RBTreeTest, swap) {
     tree2.dumpTree(out);
     ASSERT_EQ(str1.str(), out.str());
 }
-
 }