BIND 10 master, updated. d5db8b95c963cb2d493b24dda0380f71a4df67f2 [master] Merge branch 'trac2107merge'

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Aug 29 04:47:02 UTC 2012


The branch, master has been updated
       via  d5db8b95c963cb2d493b24dda0380f71a4df67f2 (commit)
       via  e69183d61f5827e70f6ea47129d3cd6fee9d7561 (commit)
       via  0aeb26af21dc9828af28565e4da127b4d898bbdd (commit)
       via  558b2dbe02c6de2cac63ef44e1eeab12600aa99a (commit)
       via  7985ae6830540ce6f6f6c3d6d71b2876c4e7315e (commit)
       via  44f92aeb7817fec9534698ac659d898d1a35acbb (commit)
       via  9ebbd0cc6b9d20b1b67bf8d7161ba4b3239d5482 (commit)
       via  870aa95f8b1c608468021d7666f24078cc78d5be (commit)
       via  78eeb8706770caf3f25c166b0413c9a324be1de3 (commit)
       via  ee81b663d85fbc1a8f74e8206a1da8e0a54d604a (commit)
       via  8052c97a801dd4d46241ce48744b0f2a69edd801 (commit)
       via  7443b1d25391782d488ae94f3d36dd65f54d948f (commit)
       via  4527f307386bc991e9f3bef201d4a6f1c0ab0dfb (commit)
       via  22d7925d2a9a7de2544c02bec4096783597274be (commit)
       via  25db0405ba8aa808b9481b32c3c229029870ea28 (commit)
       via  4bd4bf928475f7fe9181fc71f0d87546b7b8a760 (commit)
       via  8c9a5e0ce95663f27736cacf432c5ff6ece35a06 (commit)
       via  5e463e914a5442a5ab72c1a7aed0d8eb103a7dda (commit)
       via  dd15647393e777419c3f0783ed4416cdfe907a82 (commit)
       via  6e2de8ba7a4a1582802ba3a03b02e286ecaf50d1 (commit)
       via  56de9af8c7a03c5daf92e7460191f6d95b1bf917 (commit)
       via  4ecc4e3295bddf87f14f2e60fca98ceac05fbf1d (commit)
       via  b93c6e1daacfdd32d6952987186576ec3b01a534 (commit)
       via  0b8a3ffb1876c789a1694b627eaf370bb66c19d5 (commit)
       via  43dd02a2bdbeef1d4c933d3b059dadc3d3ca0043 (commit)
       via  1cc1a42e7557a89a4a8e3e85f6ab0e37c0530ded (commit)
       via  a68e90427f5459d4995a3e8f26feea83804a7cc3 (commit)
       via  76c6361071fcb50a3ef06b7072f840ca8bbdbe26 (commit)
       via  1414b51d75e9f194f3df0d7f342d445fdab265ad (commit)
       via  3ba9a889ca3a866bd4602b2c5c30b62ba20793ee (commit)
       via  aacf7cb74062a0e752e407d49d891cbbfc44ed86 (commit)
       via  c6bdb4b4c1585232edf057e27b632cfeda4c38cd (commit)
       via  e21efca5f30a849289a3daa44d2f9b3526fd3e86 (commit)
       via  042e6179638fccfcc7ceaebbaf89ad830bfa3c56 (commit)
       via  af5cb72b4dccefd265437fd7e5b8fc9cb9ad1c1b (commit)
       via  495845ba4ffa7889c4439074958a62b0106b0923 (commit)
       via  c051d7d9e18ee85c249e4619a2f1caa65f9c55d4 (commit)
       via  e50a2637c7c63aa2417c6210012c2a07d5e4cbb9 (commit)
       via  6679f424fbcf59fee039531ef213eb1f8488fcff (commit)
       via  02c38b82542f7b8aaab6d5d5f957fdfb6de6a8f5 (commit)
       via  ea17ff0dcbe916339f7ad72b8125a8bc1d2543bf (commit)
       via  3128629ce0cf4ae59b249e5fb4cbc0873b7ace7f (commit)
       via  e7a479a7b022033fea15c1fb642ecbbc1b769e9b (commit)
       via  7923aa4c6b5f21d953d837c3373adfcb84ce5ea9 (commit)
       via  6ca7b0d6bcc6a0c72033636a460d96592f6b3dc7 (commit)
      from  307a588d6ec0aa2adf2063fa4943bba0c63fc2c7 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit d5db8b95c963cb2d493b24dda0380f71a4df67f2
Merge: 307a588 e69183d
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Aug 28 21:41:40 2012 -0700

    [master] Merge branch 'trac2107merge'

-----------------------------------------------------------------------

Summary of changes:
 src/lib/datasrc/memory/Makefile.am                 |    9 +-
 src/lib/datasrc/memory/domaintree.h                |  525 ++++++++++---------
 src/lib/datasrc/memory/rdataset.h                  |   66 +++
 src/lib/datasrc/memory/segment_object_holder.h     |   63 +++
 src/lib/datasrc/memory/tests/Makefile.am           |    3 +
 .../datasrc/memory/tests/domaintree_unittest.cc    |   51 +-
 src/lib/datasrc/memory/tests/memory_segment_test.h |   62 +++
 src/lib/datasrc/memory/tests/rdataset_unittest.cc  |   19 +
 .../memory/tests/segment_object_holder_unittest.cc |   67 +++
 src/lib/datasrc/memory/tests/zone_data_unittest.cc |  255 +++++++++
 .../datasrc/memory/tests/zone_table_unittest.cc    |   39 +-
 src/lib/datasrc/memory/zone_data.cc                |  170 ++++++
 src/lib/datasrc/memory/zone_data.h                 |  541 +++++++++++++++++++-
 src/lib/datasrc/memory/zone_table.cc               |   65 ++-
 src/lib/datasrc/memory/zone_table.h                |   32 +-
 src/lib/dns/labelsequence.h                        |   14 +-
 src/lib/dns/tests/labelsequence_unittest.cc        |    9 +
 17 files changed, 1640 insertions(+), 350 deletions(-)
 create mode 100644 src/lib/datasrc/memory/segment_object_holder.h
 create mode 100644 src/lib/datasrc/memory/tests/memory_segment_test.h
 create mode 100644 src/lib/datasrc/memory/tests/segment_object_holder_unittest.cc
 create mode 100644 src/lib/datasrc/memory/tests/zone_data_unittest.cc
 create mode 100644 src/lib/datasrc/memory/zone_data.cc

-----------------------------------------------------------------------
diff --git a/src/lib/datasrc/memory/Makefile.am b/src/lib/datasrc/memory/Makefile.am
index 7cdc736..b8307c0 100644
--- a/src/lib/datasrc/memory/Makefile.am
+++ b/src/lib/datasrc/memory/Makefile.am
@@ -10,10 +10,9 @@ CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
 
 noinst_LTLIBRARIES = libdatasrc_memory.la
 
-libdatasrc_memory_la_SOURCES = \
-	rdataset.h rdataset.cc \
-	rdata_serialization.h rdata_serialization.cc \
-	domaintree.h
-libdatasrc_memory_la_SOURCES += zone_data.h
+libdatasrc_memory_la_SOURCES = domaintree.h
+libdatasrc_memory_la_SOURCES += rdataset.h rdataset.cc
+libdatasrc_memory_la_SOURCES += rdata_serialization.h rdata_serialization.cc
+libdatasrc_memory_la_SOURCES += zone_data.h zone_data.cc
 libdatasrc_memory_la_SOURCES += zone_table.h zone_table.cc
 EXTRA_DIST  = rdata_serialization_priv.cc
diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h
index 202c1a9..3ee1552 100644
--- a/src/lib/datasrc/memory/domaintree.h
+++ b/src/lib/datasrc/memory/domaintree.h
@@ -44,7 +44,7 @@ namespace memory {
 
 /// Forward declare DomainTree class here is convinent for following
 /// friend class declare inside DomainTreeNode and DomainTreeNodeChain
-template <typename T, typename DT>
+template <typename T>
 class DomainTree;
 
 /// \brief \c DomainTreeNode is used by DomainTree to store any data
@@ -79,18 +79,18 @@ class DomainTree;
 /// immediately following the main node object.  The size of the
 /// allocated space for the labels data is encoded by borrowing some
 /// bits of the "flags" field.
-template <typename T, typename DT>
+template <typename T>
 class DomainTreeNode : public boost::noncopyable {
 private:
     /// The DomainTreeNode is meant for use from within DomainTree, so
     /// it has access to it.
-    friend class DomainTree<T, DT>;
+    friend class DomainTree<T>;
 
     /// \brief Just a type alias
     ///
     /// We are going to use a lot of these offset pointers here and they
     /// have a long name.
-    typedef boost::interprocess::offset_ptr<DomainTreeNode<T, DT> >
+    typedef boost::interprocess::offset_ptr<DomainTreeNode<T> >
         DomainTreeNodePtr;
 
     /// \name Constructors
@@ -131,12 +131,12 @@ private:
     ///
     /// \param mem_sgmt A \c MemorySegment from which memory for the new
     /// \c DomainTreeNode is allocated.
-    static DomainTreeNode<T, DT>* create(util::MemorySegment& mem_sgmt,
-                             const dns::LabelSequence& labels)
+    static DomainTreeNode<T>* create(util::MemorySegment& mem_sgmt,
+                                     const dns::LabelSequence& labels)
     {
         const size_t labels_len = labels.getSerializedLength();
-        void* p = mem_sgmt.allocate(sizeof(DomainTreeNode<T, DT>) + labels_len);
-        DomainTreeNode<T, DT>* node = new(p) DomainTreeNode<T, DT>(labels_len);
+        void* p = mem_sgmt.allocate(sizeof(DomainTreeNode<T>) + labels_len);
+        DomainTreeNode<T>* node = new(p) DomainTreeNode<T>(labels_len);
         labels.serialize(node->getLabelsData(), labels_len);
         return (node);
     }
@@ -151,11 +151,12 @@ private:
     /// that was originally created by the \c create() method (the behavior
     /// is undefined if this condition isn't met).
     static void destroy(util::MemorySegment& mem_sgmt,
-                        DomainTreeNode<T, DT>* node) {
+                        DomainTreeNode<T>* node)
+    {
         const size_t labels_capacity = node->labels_capacity_;
-        node->~DomainTreeNode<T, DT>();
+        node->~DomainTreeNode<T>();
         mem_sgmt.deallocate(node,
-                            sizeof(DomainTreeNode<T, DT>) + labels_capacity);
+                            sizeof(DomainTreeNode<T>) + labels_capacity);
     }
 
     /// \brief Reset node's label sequence to a new one.
@@ -247,17 +248,22 @@ public:
 
     /// \brief Set the data stored in the node.
     ///
-    /// Any old data is destroyed.
+    /// If there is old data, a pointer to the data will be returned;
+    /// otherwise NULL will be returned.  The caller is responsible for
+    /// releasing any resource for the old data if it's not needed any more.
+    /// See also the note about data ownership in the \c DomainTree
+    /// description.
     ///
-    /// \param mem_sgmt The \c MemorySegment that allocated memory for
-    ///                 the node data.
-    /// \param data The new data to set.
-    void setData(util::MemorySegment& mem_sgmt, T* data) {
-        if (data_) {
-            const DT deleter;
-            deleter(mem_sgmt, data_.get());
-        }
+    /// \c data can be NULL, in which case it effectively clears any existing
+    /// old data.
+    ///
+    /// \param data The new data to set.  It can be NULL.
+    /// \return A pointer to the old data or NULL if the node doesn't have
+    /// data.
+    T* setData(T* data) {
+        T* olddata = data_.get();
         data_ = data;
+        return (olddata);
     }
     //@}
 
@@ -309,7 +315,7 @@ public:
 private:
     /// \name Callback related methods
     ///
-    /// See the description of \c DomainTree<T, DT>::find() at \ref callback
+    /// See the description of \c DomainTree<T>::find() at \ref callback
     /// about callbacks.
     ///
     /// These methods never throw an exception.
@@ -360,7 +366,7 @@ public:
     /// (which should be absolute), it will return \c NULL.
     ///
     /// This method never throws an exception.
-    const DomainTreeNode<T, DT>* getUpperNode() const;
+    const DomainTreeNode<T>* getUpperNode() const;
 
 private:
     /// \brief return the next node which is bigger than current node
@@ -378,7 +384,7 @@ private:
     /// returns \c NULL.
     ///
     /// This method never throws an exception.
-    const DomainTreeNode<T, DT>* successor() const;
+    const DomainTreeNode<T>* successor() const;
 
     /// \brief return the next node which is smaller than current node
     /// in the same subtree
@@ -395,7 +401,7 @@ private:
     /// returns \c NULL.
     ///
     /// This method never throws an exception.
-    const DomainTreeNode<T, DT>* predecessor() const;
+    const DomainTreeNode<T>* predecessor() const;
 
     /// \brief private shared implementation of successor and predecessor
     ///
@@ -408,11 +414,11 @@ private:
     /// The overhead of the member pointers should be optimised out, as this
     /// will probably get completely inlined into predecessor and successor
     /// methods.
-    const DomainTreeNode<T, DT>*
-        abstractSuccessor(typename DomainTreeNode<T, DT>::DomainTreeNodePtr
-                              DomainTreeNode<T, DT>::*left,
-                          typename DomainTreeNode<T, DT>::DomainTreeNodePtr
-                              DomainTreeNode<T, DT>::*right)
+    const DomainTreeNode<T>*
+        abstractSuccessor(typename DomainTreeNode<T>::DomainTreeNodePtr
+                          DomainTreeNode<T>::*left,
+                          typename DomainTreeNode<T>::DomainTreeNodePtr
+                          DomainTreeNode<T>::*right)
         const;
 
     /// \name Data to maintain the rbtree structure.
@@ -425,29 +431,29 @@ private:
     //@{
     DomainTreeNodePtr parent_;
     /// \brief Access the parent_ as bare pointer.
-    DomainTreeNode<T, DT>* getParent() {
+    DomainTreeNode<T>* getParent() {
         return (parent_.get());
     }
     /// \brief Access the parent_ as bare pointer, const.
-    const DomainTreeNode<T, DT>* getParent() const {
+    const DomainTreeNode<T>* getParent() const {
         return (parent_.get());
     }
     DomainTreeNodePtr left_;
     /// \brief Access the left_ as bare pointer.
-    DomainTreeNode<T, DT>* getLeft() {
+    DomainTreeNode<T>* getLeft() {
         return (left_.get());
     }
     /// \brief Access the left_ as bare pointer, const.
-    const DomainTreeNode<T, DT>* getLeft() const {
+    const DomainTreeNode<T>* getLeft() const {
         return (left_.get());
     }
     DomainTreeNodePtr right_;
     /// \brief Access the right_ as bare pointer.
-    DomainTreeNode<T, DT>* getRight() {
+    DomainTreeNode<T>* getRight() {
         return (right_.get());
     }
     /// \brief Access the right_ as bare pointer, const.
-    const DomainTreeNode<T, DT>* getRight() const {
+    const DomainTreeNode<T>* getRight() const {
         return (right_.get());
     }
     //@}
@@ -463,11 +469,11 @@ private:
     ///     avoiding storage of the same domain labels multiple times.
     DomainTreeNodePtr down_;
     /// \brief Access the down_ as bare pointer.
-    DomainTreeNode<T, DT>* getDown() {
+    DomainTreeNode<T>* getDown() {
         return (down_.get());
     }
     /// \brief Access the down_ as bare pointer, const.
-    const DomainTreeNode<T, DT>* getDown() const {
+    const DomainTreeNode<T>* getDown() const {
         return (down_.get());
     }
 
@@ -494,8 +500,8 @@ private:
     BOOST_STATIC_ASSERT((1 << 9) > dns::LabelSequence::MAX_SERIALIZED_LENGTH);
 };
 
-template <typename T, typename DT>
-DomainTreeNode<T, DT>::DomainTreeNode(size_t labels_capacity) :
+template <typename T>
+DomainTreeNode<T>::DomainTreeNode(size_t labels_capacity) :
     parent_(NULL),
     left_(NULL),
     right_(NULL),
@@ -506,14 +512,14 @@ DomainTreeNode<T, DT>::DomainTreeNode(size_t labels_capacity) :
 {
 }
 
-template <typename T, typename DT>
-DomainTreeNode<T, DT>::~DomainTreeNode() {
+template <typename T>
+DomainTreeNode<T>::~DomainTreeNode() {
 }
 
-template <typename T, typename DT>
-const DomainTreeNode<T, DT>*
-DomainTreeNode<T, DT>::getUpperNode() const {
-    const DomainTreeNode<T, DT>* current = this;
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::getUpperNode() const {
+    const DomainTreeNode<T>* current = this;
 
     // current would never be equal to NULL here (in a correct tree
     // implementation)
@@ -524,12 +530,11 @@ DomainTreeNode<T, DT>::getUpperNode() const {
     return (current->getParent());
 }
 
-template <typename T, typename DT>
-const DomainTreeNode<T, DT>*
-DomainTreeNode<T, DT>::abstractSuccessor(typename DomainTreeNode<T, DT>::DomainTreeNodePtr
-                                            DomainTreeNode<T, DT>::*left,
-                                        typename DomainTreeNode<T, DT>::DomainTreeNodePtr
-                                            DomainTreeNode<T, DT>::*right)
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::abstractSuccessor(
+    typename DomainTreeNode<T>::DomainTreeNodePtr DomainTreeNode<T>::*left,
+    typename DomainTreeNode<T>::DomainTreeNodePtr DomainTreeNode<T>::*right)
     const
 {
     // This function is written as a successor. It becomes predecessor if
@@ -537,12 +542,12 @@ DomainTreeNode<T, DT>::abstractSuccessor(typename DomainTreeNode<T, DT>::DomainT
     // the left pointer points to right and vice versa. Don't get confused
     // by the idea, just imagine the pointers look into a mirror.
 
-    const DomainTreeNode<T, DT>* current = this;
+    const DomainTreeNode<T>* current = this;
     // If it has right node, the successor is the left-most node of the right
     // subtree.
     if ((current->*right).get() != NULL) {
         current = (current->*right).get();
-        const DomainTreeNode<T, DT>* left_n;
+        const DomainTreeNode<T>* left_n;
         while ((left_n = (current->*left).get()) != NULL) {
             current = left_n;
         }
@@ -552,7 +557,7 @@ DomainTreeNode<T, DT>::abstractSuccessor(typename DomainTreeNode<T, DT>::DomainT
     // Otherwise go up until we find the first left branch on our path to
     // root.  If found, the parent of the branch is the successor.
     // Otherwise, we return the null node
-    const DomainTreeNode<T, DT>* parent = current->getParent();
+    const DomainTreeNode<T>* parent = current->getParent();
     while ((!current->isSubTreeRoot()) &&
            (current == (parent->*right).get())) {
         current = parent;
@@ -566,19 +571,19 @@ DomainTreeNode<T, DT>::abstractSuccessor(typename DomainTreeNode<T, DT>::DomainT
     }
 }
 
-template <typename T, typename DT>
-const DomainTreeNode<T, DT>*
-DomainTreeNode<T, DT>::successor() const {
-    return (abstractSuccessor(&DomainTreeNode<T, DT>::left_,
-                              &DomainTreeNode<T, DT>::right_));
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::successor() const {
+    return (abstractSuccessor(&DomainTreeNode<T>::left_,
+                              &DomainTreeNode<T>::right_));
 }
 
-template <typename T, typename DT>
-const DomainTreeNode<T, DT>*
-DomainTreeNode<T, DT>::predecessor() const {
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::predecessor() const {
     // Swap the left and right pointers for the abstractSuccessor
-    return (abstractSuccessor(&DomainTreeNode<T, DT>::right_,
-                              &DomainTreeNode<T, DT>::left_));
+    return (abstractSuccessor(&DomainTreeNode<T>::right_,
+                              &DomainTreeNode<T>::left_));
 }
 
 /// \brief DomainTreeNodeChain stores detailed information of \c
@@ -615,11 +620,11 @@ DomainTreeNode<T, DT>::predecessor() const {
 /// DomainTree.
 /// This is the reason why manipulation methods such as \c push() and \c pop()
 /// are private (and not shown in the doxygen document).
-template <typename T, typename DT>
+template <typename T>
 class DomainTreeNodeChain {
     /// DomainTreeNodeChain is initialized by DomainTree, only DomainTree has
     /// knowledge to manipulate it.
-    friend class DomainTree<T, DT>;
+    friend class DomainTree<T>;
 public:
     /// \name Constructors and Assignment Operator.
     ///
@@ -639,8 +644,8 @@ public:
     {}
 
 private:
-    DomainTreeNodeChain(const DomainTreeNodeChain<T, DT>&);
-    DomainTreeNodeChain<T, DT>& operator=(const DomainTreeNodeChain<T, DT>&);
+    DomainTreeNodeChain(const DomainTreeNodeChain<T>&);
+    DomainTreeNodeChain<T>& operator=(const DomainTreeNodeChain<T>&);
     //@}
 
 public:
@@ -668,7 +673,7 @@ public:
     /// tree is empty), this method returns \c NULL.
     ///
     /// \exception None
-    const DomainTreeNode<T, DT>* getLastComparedNode() const {
+    const DomainTreeNode<T>* getLastComparedNode() const {
         return (last_compared_);
     }
 
@@ -708,7 +713,7 @@ public:
                       "called on an empty chain");
         }
 
-        const DomainTreeNode<T, DT>* top_node = top();
+        const DomainTreeNode<T>* top_node = top();
         isc::dns::Name absolute_name = top_node->getName();
         int node_count = node_count_ - 1;
         while (node_count > 0) {
@@ -736,7 +741,7 @@ private:
     /// root node of DomainTree
     ///
     /// \exception None
-    const DomainTreeNode<T, DT>* top() const {
+    const DomainTreeNode<T>* top() const {
         assert(!isEmpty());
         return (nodes_[node_count_ - 1]);
     }
@@ -759,7 +764,7 @@ private:
     /// otherwise the node should be the root node of DomainTree.
     ///
     /// \exception None
-    void push(const DomainTreeNode<T, DT>* node) {
+    void push(const DomainTreeNode<T>* node) {
         assert(node_count_ < RBT_MAX_LEVEL);
         nodes_[node_count_++] = node;
     }
@@ -771,8 +776,8 @@ private:
     const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS;
 
     int node_count_;
-    const DomainTreeNode<T, DT>* nodes_[RBT_MAX_LEVEL];
-    const DomainTreeNode<T, DT>* last_compared_;
+    const DomainTreeNode<T>* nodes_[RBT_MAX_LEVEL];
+    const DomainTreeNode<T>* last_compared_;
     isc::dns::NameComparisonResult last_comparison_;
 };
 
@@ -784,11 +789,21 @@ private:
  *  \brief \c DomainTree class represents all the domains with the same suffix.
  *      It can be used to store the domains in one zone, for example.
  *
- *  DomainTree is a generic map from domain names to any kind of
- *  data. Internally, it uses a red-black tree. However, it isn't one
- *  tree containing everything.  Subdomains are trees, so this structure
- *  is recursive - trees inside trees.  But, from the interface point of
- *  view, it is opaque data structure.
+ * DomainTree is a generic map from domain names to any kind of
+ * data. Internally, it uses a red-black tree. However, it isn't one
+ * tree containing everything.  Subdomains are trees, so this structure
+ * is recursive - trees inside trees.  But, from the interface point of
+ * view, it is opaque data structure.
+ *
+ * The data of DomainTree are set by the application via the
+ * DomainTreeNode::setData() method.  The ownership of the data isn't
+ * transferred to the DomainTree; if the application replaces existing
+ * data for a specific name in DomainTree by setData(), the application is
+ * responsible for releasing any resource for the old data.  When the
+ * application destroys the entire DomainTree by the \c destroy() method,
+ * it needs to pass a deleter object for any remained data in the DomainTree.
+ * The DomainTree will call that object with all the data in the tree so that
+ * the application complete the cleanup about the remaining data.
  *
  *  \c DomainTree splits the domain space into hierarchy red black trees; nodes
  * in one tree has the same base name. The benefit of this struct is that:
@@ -807,12 +822,8 @@ private:
  * the \c insert() method will still return \c ALREADYEXISTS regardless of
  * the search policy.
  *
- * The template parameters taken by \c DomainTree are \c T (the type of
- * data which is stored by the tree) and \c DT (a type whose instance is
- * used to destroy data stored in the tree). <code>operator()</code> is
- * called on a \c DT instance and passed a pointer to the data
- * (<code>T*</code>) to be destroyed. This method should be written to
- * accept \c NULL arguments.
+ * The template parameter taken by \c DomainTree is \c T (the type of
+ * data which is stored by the tree).
  *
  * \anchor diagram
  *
@@ -848,9 +859,9 @@ private:
  *  \todo
  *  - add remove interface
  */
-template <typename T, typename DT>
+template <typename T>
 class DomainTree : public boost::noncopyable {
-    friend class DomainTreeNode<T, DT>;
+    friend class DomainTreeNode<T>;
 public:
     /// \brief The return value for the \c find() and insert() methods
     enum Result {
@@ -876,8 +887,8 @@ public:
     static DomainTree* create(util::MemorySegment& mem_sgmt,
                               bool return_empty_node = false)
     {
-        void* p = mem_sgmt.allocate(sizeof(DomainTree<T, DT>));
-        return (new(p) DomainTree<T, DT>(return_empty_node));
+        void* p = mem_sgmt.allocate(sizeof(DomainTree<T>));
+        return (new(p) DomainTree<T>(return_empty_node));
     }
 
     /// \brief Destruct and deallocate \c DomainTree
@@ -885,6 +896,12 @@ public:
     /// This method also destroys and deallocates all nodes inserted to the
     /// tree.
     ///
+    /// The template parameter, \c DataDeleter, is a type whose instance is
+    /// used to destroy data stored in the tree nodes.  It must have a
+    /// <code>operator()</code> method, which is called on a \c DataDeleter
+    /// instance and passed a pointer to the data (<code>T*</code>) to be
+    /// destroyed. This method should be written to accept a \c NULL argument.
+    ///
     /// \note The memory segment (\c mem_sgmt) must be the same one that
     /// was originally used to allocate memory for the tree (and for all
     /// nodes inserted to the tree, due to the requirement of \c insert()),
@@ -905,11 +922,15 @@ public:
     /// \param tree A non NULL pointer to a valid \c DomainTree object
     /// that was originally created by the \c create() method (the behavior
     /// is undefined if this condition isn't met).
+    /// \param deleter A deleter functor or function to delete node data.
+    template <typename DataDeleter>
     static void destroy(util::MemorySegment& mem_sgmt,
-                        DomainTree<T, DT>* tree) {
-        tree->deleteAllNodes(mem_sgmt);
-        tree->~DomainTree<T, DT>();
-        mem_sgmt.deallocate(tree, sizeof(DomainTree<T, DT>));
+                        DomainTree<T>* tree,
+                        DataDeleter deleter)
+    {
+        tree->deleteAllNodes(mem_sgmt, deleter);
+        tree->~DomainTree<T>();
+        mem_sgmt.deallocate(tree, sizeof(DomainTree<T>));
     }
 
 private:
@@ -987,8 +1008,8 @@ public:
     ///
     /// Acts as described in the \ref find section.
     Result find(const isc::dns::Name& name,
-                DomainTreeNode<T, DT>** node) const {
-        DomainTreeNodeChain<T, DT> node_path;
+                DomainTreeNode<T>** node) const {
+        DomainTreeNodeChain<T> node_path;
         const isc::dns::LabelSequence ls(name);
         return (find<void*>(ls, node, node_path, NULL, NULL));
     }
@@ -998,9 +1019,9 @@ public:
     /// Acts as described in the \ref find section, but returns immutable node
     /// pointer.
     Result find(const isc::dns::Name& name,
-                const DomainTreeNode<T, DT>** node) const {
-        DomainTreeNodeChain<T, DT> node_path;
-        DomainTreeNode<T, DT> *target_node = NULL;
+                const DomainTreeNode<T>** node) const {
+        DomainTreeNodeChain<T> node_path;
+        DomainTreeNode<T> *target_node = NULL;
         const isc::dns::LabelSequence ls(name);
         Result ret = (find<void*>(ls, &target_node, node_path, NULL, NULL));
         if (ret != NOTFOUND) {
@@ -1012,8 +1033,8 @@ public:
     /// \brief Simple find, with node_path tracking
     ///
     /// Acts as described in the \ref find section.
-    Result find(const isc::dns::Name& name, DomainTreeNode<T, DT>** node,
-                DomainTreeNodeChain<T, DT>& node_path) const
+    Result find(const isc::dns::Name& name, DomainTreeNode<T>** node,
+                DomainTreeNodeChain<T>& node_path) const
     {
         const isc::dns::LabelSequence ls(name);
         return (find<void*>(ls, node, node_path, NULL, NULL));
@@ -1023,10 +1044,10 @@ public:
     ///
     /// Acts as described in the \ref find section, but returns immutable node
     /// pointer.
-    Result find(const isc::dns::Name& name, const DomainTreeNode<T, DT>** node,
-                DomainTreeNodeChain<T, DT>& node_path) const
+    Result find(const isc::dns::Name& name, const DomainTreeNode<T>** node,
+                DomainTreeNodeChain<T>& node_path) const
     {
-        DomainTreeNode<T, DT> *target_node = NULL;
+        DomainTreeNode<T> *target_node = NULL;
         const isc::dns::LabelSequence ls(name);
         Result ret = (find<void*>(ls, &target_node, node_path, NULL, NULL));
         if (ret != NOTFOUND) {
@@ -1041,12 +1062,12 @@ public:
     /// node pointer.
     template <typename CBARG>
     Result find(const isc::dns::Name& name,
-                const DomainTreeNode<T, DT>** node,
-                DomainTreeNodeChain<T, DT>& node_path,
-                bool (*callback)(const DomainTreeNode<T, DT>&, CBARG),
+                const DomainTreeNode<T>** node,
+                DomainTreeNodeChain<T>& node_path,
+                bool (*callback)(const DomainTreeNode<T>&, CBARG),
                 CBARG callback_arg) const
     {
-        DomainTreeNode<T, DT>* target_node = NULL;
+        DomainTreeNode<T>* target_node = NULL;
         const isc::dns::LabelSequence ls(name);
         Result ret = find(ls, &target_node, node_path, callback,
                           callback_arg);
@@ -1130,9 +1151,9 @@ public:
     ///     \c true, it returns immediately with the current node.
     template <typename CBARG>
     Result find(const isc::dns::LabelSequence& target_labels_orig,
-                DomainTreeNode<T, DT>** node,
-                DomainTreeNodeChain<T, DT>& node_path,
-                bool (*callback)(const DomainTreeNode<T, DT>&, CBARG),
+                DomainTreeNode<T>** node,
+                DomainTreeNodeChain<T>& node_path,
+                bool (*callback)(const DomainTreeNode<T>&, CBARG),
                 CBARG callback_arg) const;
 
     /// \brief Simple find returning immutable node.
@@ -1141,12 +1162,12 @@ public:
     /// node pointer.
     template <typename CBARG>
     Result find(const isc::dns::LabelSequence& target_labels,
-                const DomainTreeNode<T, DT>** node,
-                DomainTreeNodeChain<T, DT>& node_path,
-                bool (*callback)(const DomainTreeNode<T, DT>&, CBARG),
+                const DomainTreeNode<T>** node,
+                DomainTreeNodeChain<T>& node_path,
+                bool (*callback)(const DomainTreeNode<T>&, CBARG),
                 CBARG callback_arg) const
     {
-        DomainTreeNode<T, DT>* target_node = NULL;
+        DomainTreeNode<T>* target_node = NULL;
         Result ret = find(target_labels, &target_node, node_path,
                           callback, callback_arg);
         if (ret != NOTFOUND) {
@@ -1179,8 +1200,8 @@ public:
     ///
     /// \return An \c DomainTreeNode that is next bigger than \c node;
     /// if \c node is the largest, \c NULL will be returned.
-    const DomainTreeNode<T, DT>*
-    nextNode(DomainTreeNodeChain<T, DT>& node_path) const;
+    const DomainTreeNode<T>*
+    nextNode(DomainTreeNodeChain<T>& node_path) const;
 
     /// \brief return the next smaller node in DNSSEC order from a node
     ///     searched by DomainTree::find().
@@ -1204,8 +1225,8 @@ public:
     ///
     /// \return An \c DomainTreeNode that is next smaller than \c node;
     /// if \c node is the smallest, \c NULL will be returned.
-    const DomainTreeNode<T, DT>*
-    previousNode(DomainTreeNodeChain<T, DT>& node_path) const;
+    const DomainTreeNode<T>*
+    previousNode(DomainTreeNodeChain<T>& node_path) const;
 
     /// \brief Get the total number of nodes in the tree
     ///
@@ -1270,7 +1291,7 @@ public:
     ///  - ALREADYEXISTS There was already a node of that name, so it was not
     ///     added.
     Result insert(util::MemorySegment& mem_sgmt, const isc::dns::Name& name,
-                  DomainTreeNode<T, DT>** inserted_node);
+                  DomainTreeNode<T>** inserted_node);
 
     /// \brief Delete all tree nodes.
     ///
@@ -1279,7 +1300,8 @@ public:
     /// \param mem_sgmt The \c MemorySegment object used to insert the nodes
     /// (which was also used for creating the tree due to the requirement of
     /// \c inert()).
-    void deleteAllNodes(util::MemorySegment& mem_sgmt);
+    template <typename DataDeleter>
+    void deleteAllNodes(util::MemorySegment& mem_sgmt, DataDeleter deleter);
 
     /// \brief Swaps two tree's contents.
     ///
@@ -1289,7 +1311,7 @@ public:
     ///
     /// This acts the same as many std::*.swap functions, exchanges the
     /// contents. This doesn't throw anything.
-    void swap(DomainTree<T, DT>& other) {
+    void swap(DomainTree<T>& other) {
         std::swap(root_, other.root_);
         std::swap(node_count_, other.node_count_);
     }
@@ -1299,31 +1321,32 @@ private:
     /// \name DomainTree balance functions
     //@{
     void
-    insertRebalance(typename DomainTreeNode<T, DT>::DomainTreeNodePtr* root,
-                    DomainTreeNode<T, DT>* node);
+    insertRebalance(typename DomainTreeNode<T>::DomainTreeNodePtr* root,
+                    DomainTreeNode<T>* node);
 
-    DomainTreeNode<T, DT>*
-    rightRotate(typename DomainTreeNode<T, DT>::DomainTreeNodePtr* root,
-                DomainTreeNode<T, DT>* node);
+    DomainTreeNode<T>*
+    rightRotate(typename DomainTreeNode<T>::DomainTreeNodePtr* root,
+                DomainTreeNode<T>* node);
 
-    DomainTreeNode<T, DT>*
-    leftRotate(typename DomainTreeNode<T, DT>::DomainTreeNodePtr* root,
-               DomainTreeNode<T, DT>* node);
+    DomainTreeNode<T>*
+    leftRotate(typename DomainTreeNode<T>::DomainTreeNodePtr* root,
+               DomainTreeNode<T>* node);
     //@}
 
     /// \name Helper functions
     //@{
     /// \brief delete tree whose root is equal to node
+    template <typename DataDeleter>
     void deleteHelper(util::MemorySegment& mem_sgmt,
-                      DomainTreeNode<T, DT> *node,
-                      const DT& deleter);
+                      DomainTreeNode<T> *node,
+                      const DataDeleter& deleter);
 
     /// \brief Print the information of given DomainTreeNode.
-    void dumpTreeHelper(std::ostream& os, const DomainTreeNode<T, DT>* node,
+    void dumpTreeHelper(std::ostream& os, const DomainTreeNode<T>* node,
                         unsigned int depth) const;
 
     /// \brief Print the information of given DomainTreeNode for dot.
-    int dumpDotHelper(std::ostream& os, const DomainTreeNode<T, DT>* node,
+    int dumpDotHelper(std::ostream& os, const DomainTreeNode<T>* node,
                       int* nodecount, bool show_pointers) const;
 
     /// \brief Indentation helper function for dumpTree
@@ -1336,78 +1359,80 @@ private:
     /// the entire tree.  This ensures that a pointer to a node keeps its
     /// semantics even if the tree structure is changed (as long as the node
     /// itself remains valid).
-    void nodeFission(util::MemorySegment& mem_sgmt, DomainTreeNode<T, DT>& node,
+    void nodeFission(util::MemorySegment& mem_sgmt, DomainTreeNode<T>& node,
                      const isc::dns::LabelSequence& new_prefix,
                      const isc::dns::LabelSequence& new_suffix);
     //@}
 
-    typename DomainTreeNode<T, DT>::DomainTreeNodePtr root_;
+    typename DomainTreeNode<T>::DomainTreeNodePtr root_;
     /// the node count of current tree
     unsigned int node_count_;
     /// search policy for domaintree
     const bool needsReturnEmptyNode_;
 };
 
-template <typename T, typename DT>
-DomainTree<T, DT>::DomainTree(bool returnEmptyNode) :
+template <typename T>
+DomainTree<T>::DomainTree(bool returnEmptyNode) :
     root_(NULL),
     node_count_(0),
     needsReturnEmptyNode_(returnEmptyNode)
 {
 }
 
-template <typename T, typename DT>
-DomainTree<T, DT>::~DomainTree() {
+template <typename T>
+DomainTree<T>::~DomainTree() {
     assert(node_count_ == 0);
 }
 
-template <typename T, typename DT>
+template <typename T>
+template <typename DataDeleter>
 void
-DomainTree<T, DT>::deleteHelper(util::MemorySegment& mem_sgmt,
-                                DomainTreeNode<T, DT>* root,
-                                const DT& deleter) {
+DomainTree<T>::deleteHelper(util::MemorySegment& mem_sgmt,
+                            DomainTreeNode<T>* root,
+                            const DataDeleter& deleter)
+{
     while (root != NULL) {
         // If there is a left, right or down node, walk into it and
         // iterate.
         if (root->getLeft() != NULL) {
-            DomainTreeNode<T, DT>* node = root;
+            DomainTreeNode<T>* node = root;
             root = root->getLeft();
             node->left_ = NULL;
         } else if (root->getRight() != NULL) {
-            DomainTreeNode<T, DT>* node = root;
+            DomainTreeNode<T>* node = root;
             root = root->getRight();
             node->right_ = NULL;
         } else if (root->getDown() != NULL) {
-            DomainTreeNode<T, DT>* node = root;
+            DomainTreeNode<T>* node = root;
             root = root->getDown();
             node->down_ = NULL;
         } else {
             // There are no left, right or down nodes, so we can
             // free this one and go back to its parent.
-            DomainTreeNode<T, DT>* node = root;
+            DomainTreeNode<T>* node = root;
             root = root->getParent();
-            deleter(mem_sgmt, node->data_.get());
-            DomainTreeNode<T, DT>::destroy(mem_sgmt, node);
+            deleter(node->data_.get());
+            DomainTreeNode<T>::destroy(mem_sgmt, node);
             --node_count_;
         }
     }
 }
 
-template <typename T, typename DT>
+template <typename T>
 template <typename CBARG>
-typename DomainTree<T, DT>::Result
-DomainTree<T, DT>::find(const isc::dns::LabelSequence& target_labels_orig,
-                       DomainTreeNode<T, DT>** target,
-                       DomainTreeNodeChain<T, DT>& node_path,
-                       bool (*callback)(const DomainTreeNode<T, DT>&, CBARG),
-                       CBARG callback_arg) const
+typename DomainTree<T>::Result
+DomainTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
+                    DomainTreeNode<T>** target,
+                    DomainTreeNodeChain<T>& node_path,
+                    bool (*callback)(const DomainTreeNode<T>&, CBARG),
+                    CBARG callback_arg) const
 {
     if (!node_path.isEmpty()) {
         isc_throw(isc::BadValue,
                   "DomainTree::find is given a non empty chain");
     }
 
-    DomainTreeNode<T, DT>* node = root_.get();
+    DomainTreeNode<T>* node = root_.get();
     Result ret = NOTFOUND;
     dns::LabelSequence target_labels(target_labels_orig);
 
@@ -1435,7 +1460,7 @@ DomainTree<T, DT>::find(const isc::dns::LabelSequence& target_labels_orig,
                     ret = PARTIALMATCH;
                     *target = node;
                     if (callback != NULL &&
-                        node->getFlag(DomainTreeNode<T, DT>::FLAG_CALLBACK)) {
+                        node->getFlag(DomainTreeNode<T>::FLAG_CALLBACK)) {
                         if ((callback)(*node, callback_arg)) {
                             break;
                         }
@@ -1454,20 +1479,20 @@ DomainTree<T, DT>::find(const isc::dns::LabelSequence& target_labels_orig,
     return (ret);
 }
 
-template <typename T, typename DT>
-const DomainTreeNode<T, DT>*
-DomainTree<T, DT>::nextNode(DomainTreeNodeChain<T, DT>& node_path) const {
+template <typename T>
+const DomainTreeNode<T>*
+DomainTree<T>::nextNode(DomainTreeNodeChain<T>& node_path) const {
     if (node_path.isEmpty()) {
         isc_throw(isc::BadValue,
                   "DomainTree::nextNode is given an empty chain");
     }
 
-    const DomainTreeNode<T, DT>* node = node_path.top();
+    const DomainTreeNode<T>* node = node_path.top();
     // if node has sub domain, the next domain is the smallest
     // domain in sub domain tree
-    const DomainTreeNode<T, DT>* down = node->getDown();
+    const DomainTreeNode<T>* down = node->getDown();
     if (down != NULL) {
-        const DomainTreeNode<T, DT>* left_most = down;
+        const DomainTreeNode<T>* left_most = down;
         while (left_most->getLeft() != NULL) {
             left_most = left_most->getLeft();
         }
@@ -1481,7 +1506,7 @@ DomainTree<T, DT>::nextNode(DomainTreeNodeChain<T, DT>& node_path) const {
     // up node doesn't have successor we gonna keep moving to up
     // level
     while (!node_path.isEmpty()) {
-        const DomainTreeNode<T, DT>* up_node_successor =
+        const DomainTreeNode<T>* up_node_successor =
             node_path.top()->successor();
         node_path.pop();
         if (up_node_successor != NULL) {
@@ -1493,9 +1518,9 @@ DomainTree<T, DT>::nextNode(DomainTreeNodeChain<T, DT>& node_path) const {
     return (NULL);
 }
 
-template <typename T, typename DT>
-const DomainTreeNode<T, DT>*
-DomainTree<T, DT>::previousNode(DomainTreeNodeChain<T, DT>& node_path) const {
+template <typename T>
+const DomainTreeNode<T>*
+DomainTree<T>::previousNode(DomainTreeNodeChain<T>& node_path) const {
     if (getNodeCount() == 0) {
         // Special case for empty trees. It would look every time like
         // we didn't search, because the last compared is empty. This is
@@ -1535,13 +1560,13 @@ DomainTree<T, DT>::previousNode(DomainTreeNodeChain<T, DT>& node_path) const {
                 // compared one (it is either the compared one, or some
                 // subdomain of it). There probably is not an easy trick
                 // for this, so we just find the correct place.
-                const DomainTreeNode<T, DT>* current(node_path.last_compared_);
+                const DomainTreeNode<T>* current(node_path.last_compared_);
                 while (current != NULL) {
                     node_path.push(current);
                     // Go a level down and as much right there as possible
                     current = current->getDown();
                     if (current != NULL) {
-                        const DomainTreeNode<T, DT>* right;
+                        const DomainTreeNode<T>* right;
                         while ((right = current->getRight()) != NULL) {
                             current = right;
                         }
@@ -1592,7 +1617,7 @@ DomainTree<T, DT>::previousNode(DomainTreeNodeChain<T, DT>& node_path) const {
         return (NULL);
     }
 
-    const DomainTreeNode<T, DT>* node(node_path.top());
+    const DomainTreeNode<T>* node(node_path.top());
 
     // Try going left in this tree
     node = node->predecessor();
@@ -1615,13 +1640,13 @@ DomainTree<T, DT>::previousNode(DomainTreeNodeChain<T, DT>& node_path) const {
     node_path.push(node);
 
     // Try going as deep as possible, keeping on the right side of the trees
-    const DomainTreeNode<T, DT>* down;
+    const DomainTreeNode<T>* down;
     while ((down = node->getDown()) != NULL) {
         // Move to the tree below
         node = down;
         if (node != NULL) {
             // And get as much to the right of the tree as possible
-            const DomainTreeNode<T, DT>* right;
+            const DomainTreeNode<T>* right;
             while ((right = node->getRight()) != NULL) {
                 node = right;
             }
@@ -1636,15 +1661,15 @@ DomainTree<T, DT>::previousNode(DomainTreeNodeChain<T, DT>& node_path) const {
     return (node);
 }
 
-template <typename T, typename DT>
-typename DomainTree<T, DT>::Result
-DomainTree<T, DT>::insert(util::MemorySegment& mem_sgmt,
-                          const isc::dns::Name& target_name,
-                          DomainTreeNode<T, DT>** new_node)
+template <typename T>
+typename DomainTree<T>::Result
+DomainTree<T>::insert(util::MemorySegment& mem_sgmt,
+                      const isc::dns::Name& target_name,
+                      DomainTreeNode<T>** new_node)
 {
-    DomainTreeNode<T, DT>* parent = NULL;
-    DomainTreeNode<T, DT>* current = root_.get();
-    DomainTreeNode<T, DT>* up_node = NULL;
+    DomainTreeNode<T>* parent = NULL;
+    DomainTreeNode<T>* current = root_.get();
+    DomainTreeNode<T>* up_node = NULL;
     isc::dns::LabelSequence target_labels(target_name);
 
     int order = -1;
@@ -1688,17 +1713,17 @@ DomainTree<T, DT>::insert(util::MemorySegment& mem_sgmt,
         }
     }
 
-    typename DomainTreeNode<T, DT>::DomainTreeNodePtr* current_root =
+    typename DomainTreeNode<T>::DomainTreeNodePtr* current_root =
         (up_node != NULL) ? &(up_node->down_) : &root_;
     // Once a new node is created, no exception will be thrown until the end
     // of the function, so we can simply create and hold a new node pointer.
-    DomainTreeNode<T, DT>* node = DomainTreeNode<T, DT>::create(mem_sgmt,
-                                                              target_labels);
+    DomainTreeNode<T>* node = DomainTreeNode<T>::create(mem_sgmt,
+                                                        target_labels);
     node->parent_ = parent;
     if (parent == NULL) {
         *current_root = node;
         // node is the new root of sub tree, so its init color is BLACK
-        node->setColor(DomainTreeNode<T, DT>::BLACK);
+        node->setColor(DomainTreeNode<T>::BLACK);
         node->setSubTreeRoot(true);
         node->parent_ = up_node;
     } else if (order < 0) {
@@ -1717,28 +1742,30 @@ DomainTree<T, DT>::insert(util::MemorySegment& mem_sgmt,
     return (SUCCESS);
 }
 
-template <typename T, typename DT>
+template <typename T>
+template <typename DataDeleter>
 void
-DomainTree<T, DT>::deleteAllNodes(util::MemorySegment& mem_sgmt) {
-    const DT deleter;
+DomainTree<T>::deleteAllNodes(util::MemorySegment& mem_sgmt,
+                              DataDeleter deleter)
+{
     deleteHelper(mem_sgmt, root_.get(), deleter);
     root_ = NULL;
 }
 
-template <typename T, typename DT>
+template <typename T>
 void
-DomainTree<T, DT>::nodeFission(util::MemorySegment& mem_sgmt,
-                               DomainTreeNode<T, DT>& node,
-                               const isc::dns::LabelSequence& new_prefix,
-                               const isc::dns::LabelSequence& new_suffix)
+DomainTree<T>::nodeFission(util::MemorySegment& mem_sgmt,
+                           DomainTreeNode<T>& node,
+                           const isc::dns::LabelSequence& new_prefix,
+                           const isc::dns::LabelSequence& new_suffix)
 {
     // Create and reset the labels.
     // Once a new node is created, no exception will be thrown until
     // the end of the function, and it will keep consistent behavior
     // (i.e., a weak form of strong exception guarantee) even if code
     // after the call to this function throws an exception.
-    DomainTreeNode<T, DT>* up_node = DomainTreeNode<T, DT>::create(mem_sgmt,
-                                                                   new_suffix);
+    DomainTreeNode<T>* up_node = DomainTreeNode<T>::create(mem_sgmt,
+                                                           new_suffix);
     node.resetLabels(new_prefix);
 
     up_node->parent_ = node.getParent();
@@ -1772,7 +1799,7 @@ DomainTree<T, DT>::nodeFission(util::MemorySegment& mem_sgmt,
 
     // set color of both nodes; the initial subtree node color is BLACK
     up_node->setColor(node.getColor());
-    node.setColor(DomainTreeNode<T, DT>::BLACK);
+    node.setColor(DomainTreeNode<T>::BLACK);
 
     // set the subtree root flag of both nodes
     up_node->setSubTreeRoot(node.isSubTreeRoot());
@@ -1782,27 +1809,27 @@ DomainTree<T, DT>::nodeFission(util::MemorySegment& mem_sgmt,
 }
 
 
-template <typename T, typename DT>
+template <typename T>
 void
-DomainTree<T, DT>::insertRebalance
-    (typename DomainTreeNode<T, DT>::DomainTreeNodePtr* root,
-     DomainTreeNode<T, DT>* node)
+DomainTree<T>::insertRebalance
+    (typename DomainTreeNode<T>::DomainTreeNodePtr* root,
+     DomainTreeNode<T>* node)
 {
-    DomainTreeNode<T, DT>* uncle;
-    DomainTreeNode<T, DT>* parent;
+    DomainTreeNode<T>* uncle;
+    DomainTreeNode<T>* parent;
     while (node != (*root).get() &&
            ((parent = node->getParent())->getColor()) ==
-           DomainTreeNode<T, DT>::RED) {
+           DomainTreeNode<T>::RED) {
         // Here, node->parent_ is not NULL and it is also red, so
         // node->parent_->parent_ is also not NULL.
         if (parent == parent->getParent()->getLeft()) {
             uncle = parent->getParent()->getRight();
 
             if (uncle != NULL && uncle->getColor() ==
-                DomainTreeNode<T, DT>::RED) {
-                parent->setColor(DomainTreeNode<T, DT>::BLACK);
-                uncle->setColor(DomainTreeNode<T, DT>::BLACK);
-                parent->getParent()->setColor(DomainTreeNode<T, DT>::RED);
+                DomainTreeNode<T>::RED) {
+                parent->setColor(DomainTreeNode<T>::BLACK);
+                uncle->setColor(DomainTreeNode<T>::BLACK);
+                parent->getParent()->setColor(DomainTreeNode<T>::RED);
                 node = parent->getParent();
             } else {
                 if (node == parent->getRight()) {
@@ -1810,18 +1837,18 @@ DomainTree<T, DT>::insertRebalance
                     leftRotate(root, node);
                     parent = node->getParent();
                 }
-                parent->setColor(DomainTreeNode<T, DT>::BLACK);
-                parent->getParent()->setColor(DomainTreeNode<T, DT>::RED);
+                parent->setColor(DomainTreeNode<T>::BLACK);
+                parent->getParent()->setColor(DomainTreeNode<T>::RED);
                 rightRotate(root, parent->getParent());
             }
         } else {
             uncle = parent->getParent()->getLeft();
 
             if (uncle != NULL && uncle->getColor() ==
-                DomainTreeNode<T, DT>::RED) {
-                parent->setColor(DomainTreeNode<T, DT>::BLACK);
-                uncle->setColor(DomainTreeNode<T, DT>::BLACK);
-                parent->getParent()->setColor(DomainTreeNode<T, DT>::RED);
+                DomainTreeNode<T>::RED) {
+                parent->setColor(DomainTreeNode<T>::BLACK);
+                uncle->setColor(DomainTreeNode<T>::BLACK);
+                parent->getParent()->setColor(DomainTreeNode<T>::RED);
                 node = parent->getParent();
             } else {
                 if (node == parent->getLeft()) {
@@ -1829,31 +1856,31 @@ DomainTree<T, DT>::insertRebalance
                     rightRotate(root, node);
                     parent = node->getParent();
                 }
-                parent->setColor(DomainTreeNode<T, DT>::BLACK);
-                parent->getParent()->setColor(DomainTreeNode<T, DT>::RED);
+                parent->setColor(DomainTreeNode<T>::BLACK);
+                parent->getParent()->setColor(DomainTreeNode<T>::RED);
                 leftRotate(root, parent->getParent());
             }
         }
     }
 
-    (*root)->setColor(DomainTreeNode<T, DT>::BLACK);
+    (*root)->setColor(DomainTreeNode<T>::BLACK);
 }
 
 
-template <typename T, typename DT>
-DomainTreeNode<T, DT>*
-DomainTree<T, DT>::leftRotate
-    (typename DomainTreeNode<T, DT>::DomainTreeNodePtr* root,
-     DomainTreeNode<T, DT>* node)
+template <typename T>
+DomainTreeNode<T>*
+DomainTree<T>::leftRotate
+    (typename DomainTreeNode<T>::DomainTreeNodePtr* root,
+     DomainTreeNode<T>* node)
 {
-    DomainTreeNode<T, DT>* const right = node->getRight();
-    DomainTreeNode<T, DT>* const rleft = right->getLeft();
+    DomainTreeNode<T>* const right = node->getRight();
+    DomainTreeNode<T>* const rleft = right->getLeft();
     node->right_ = rleft;
     if (rleft != NULL) {
         rleft->parent_ = node;
     }
 
-    DomainTreeNode<T, DT>* const parent = node->getParent();
+    DomainTreeNode<T>* const parent = node->getParent();
     right->parent_ = parent;
 
     if (!node->isSubTreeRoot()) {
@@ -1874,20 +1901,20 @@ DomainTree<T, DT>::leftRotate
     return (node);
 }
 
-template <typename T, typename DT>
-DomainTreeNode<T, DT>*
-DomainTree<T, DT>::rightRotate
-    (typename DomainTreeNode<T, DT>::DomainTreeNodePtr* root,
-     DomainTreeNode<T, DT>* node)
+template <typename T>
+DomainTreeNode<T>*
+DomainTree<T>::rightRotate
+    (typename DomainTreeNode<T>::DomainTreeNodePtr* root,
+     DomainTreeNode<T>* node)
 {
-    DomainTreeNode<T, DT>* const left = node->getLeft();
-    DomainTreeNode<T, DT>* const lright = left->getRight();
+    DomainTreeNode<T>* const left = node->getLeft();
+    DomainTreeNode<T>* const lright = left->getRight();
     node->left_ = lright;
     if (lright != NULL) {
         lright->parent_ = node;
     }
 
-    DomainTreeNode<T, DT>* const parent = node->getParent();
+    DomainTreeNode<T>* const parent = node->getParent();
     left->parent_ = parent;
 
     if (!node->isSubTreeRoot()) {
@@ -1909,19 +1936,19 @@ DomainTree<T, DT>::rightRotate
 }
 
 
-template <typename T, typename DT>
+template <typename T>
 void
-DomainTree<T, DT>::dumpTree(std::ostream& os, unsigned int depth) const {
+DomainTree<T>::dumpTree(std::ostream& os, unsigned int depth) const {
     indent(os, depth);
     os << "tree has " << node_count_ << " node(s)\n";
     dumpTreeHelper(os, root_.get(), depth);
 }
 
-template <typename T, typename DT>
+template <typename T>
 void
-DomainTree<T, DT>::dumpTreeHelper(std::ostream& os,
-                                 const DomainTreeNode<T, DT>* node,
-                                 unsigned int depth) const
+DomainTree<T>::dumpTreeHelper(std::ostream& os,
+                              const DomainTreeNode<T>* node,
+                              unsigned int depth) const
 {
     if (node == NULL) {
         indent(os, depth);
@@ -1931,7 +1958,7 @@ DomainTree<T, DT>::dumpTreeHelper(std::ostream& os,
 
     indent(os, depth);
     os << node->getLabels() << " ("
-       << ((node->getColor() == DomainTreeNode<T, DT>::BLACK) ? "black" : "red")
+       << ((node->getColor() == DomainTreeNode<T>::BLACK) ? "black" : "red")
        << ")";
     if (node->isEmpty()) {
         os << " [invisible]";
@@ -1941,7 +1968,7 @@ DomainTree<T, DT>::dumpTreeHelper(std::ostream& os,
     }
     os << "\n";
 
-    const DomainTreeNode<T, DT>* down = node->getDown();
+    const DomainTreeNode<T>* down = node->getDown();
     if (down != NULL) {
         indent(os, depth + 1);
         os << "begin down from " << node->getLabels() << "\n";
@@ -1953,16 +1980,16 @@ DomainTree<T, DT>::dumpTreeHelper(std::ostream& os,
     dumpTreeHelper(os, node->getRight(), depth + 1);
 }
 
-template <typename T, typename DT>
+template <typename T>
 void
-DomainTree<T, DT>::indent(std::ostream& os, unsigned int depth) {
+DomainTree<T>::indent(std::ostream& os, unsigned int depth) {
     static const unsigned int INDENT_FOR_EACH_DEPTH = 5;
     os << std::string(depth * INDENT_FOR_EACH_DEPTH, ' ');
 }
 
-template <typename T, typename DT>
+template <typename T>
 void
-DomainTree<T, DT>::dumpDot(std::ostream& os, bool show_pointers) const {
+DomainTree<T>::dumpDot(std::ostream& os, bool show_pointers) const {
     int nodecount = 0;
 
     os << "digraph g {\n";
@@ -1971,11 +1998,11 @@ DomainTree<T, DT>::dumpDot(std::ostream& os, bool show_pointers) const {
     os << "}\n";
 }
 
-template <typename T, typename DT>
+template <typename T>
 int
-DomainTree<T, DT>::dumpDotHelper(std::ostream& os,
-                                const DomainTreeNode<T, DT>* node,
-                                int* nodecount, bool show_pointers) const
+DomainTree<T>::dumpDotHelper(std::ostream& os,
+                             const DomainTreeNode<T>* node,
+                             int* nodecount, bool show_pointers) const
 {
     if (node == NULL) {
         return 0;
@@ -1995,7 +2022,7 @@ DomainTree<T, DT>::dumpDotHelper(std::ostream& os,
     }
     os << "\"] [";
 
-    if (node->getColor() == DomainTreeNode<T, DT>::RED) {
+    if (node->getColor() == DomainTreeNode<T>::RED) {
         os << "color=red";
     } else {
         os << "color=black";
diff --git a/src/lib/datasrc/memory/rdataset.h b/src/lib/datasrc/memory/rdataset.h
index 70cf733..b0b3b48 100644
--- a/src/lib/datasrc/memory/rdataset.h
+++ b/src/lib/datasrc/memory/rdataset.h
@@ -194,6 +194,43 @@ public:
     static void destroy(util::MemorySegment& mem_sgmt, dns::RRClass rrclass,
                         RdataSet* rdataset);
 
+    /// \brief Find \c RdataSet of given RR type from a list (const version).
+    ///
+    /// This function is a convenient shortcut for commonly used operation of
+    /// finding a given type of \c RdataSet from a linked list of them.
+    ///
+    /// It follows the linked list of \c RdataSet objects (via their \c next
+    /// member) starting the given head, until it finds an object of the
+    /// given RR type.  If found, it returns a (bare) pointer to the object;
+    /// if not found in the entire list, it returns NULL.  The head pointer
+    /// can be NULL, in which case this function will simply return NULL.
+    ///
+    /// \note This function is defined as a (static) class method to
+    /// clarify its an operation for \c RdataSet objects and to make the
+    /// name shorter.  But its implementation does not depend on private
+    /// members of the class, and it should be kept if and when this method
+    /// needs to be extended, unless there's a reason other than simply
+    /// because it's already a member function.
+    ///
+    /// \param rdata_head A pointer to \c RdataSet from which the search
+    /// starts.  It can be NULL.
+    /// \param type The RRType of \c RdataSet to find.
+    /// \return A pointer to the found \c RdataSet or NULL if none found.
+    static const RdataSet*
+    find(const RdataSet* rdataset_head, const dns::RRType& type) {
+        return (find<const RdataSet>(rdataset_head, type));
+    }
+
+    /// \brief Find \c RdataSet of given RR type from a list (non const
+    /// version).
+    ///
+    /// This is similar to the const version, except it takes and returns non
+    /// const pointers.
+    static RdataSet*
+    find(RdataSet* rdataset_head, const dns::RRType& type) {
+        return (find<RdataSet>(rdataset_head, type));
+    }
+
     typedef boost::interprocess::offset_ptr<RdataSet> RdataSetPtr;
     typedef boost::interprocess::offset_ptr<const RdataSet> ConstRdataSetPtr;
 
@@ -228,6 +265,20 @@ private:
     static const size_t MANY_RRSIG_COUNT = (1 << 3) - 1;
 
 public:
+    /// \brief Return the bare pointer to the next node.
+    ///
+    /// In such an operation as iterating over a linked list of \c RdataSet
+    /// object, using this method is generally more efficient than using
+    /// the \c next member directly because it prevents unintentional
+    /// creation of offset pointer objects.  While the application can
+    /// get the same result by directly calling get() on \c next, it would
+    /// help encourage the use of more efficient usage if we provide an
+    /// explicit accessor.
+    const RdataSet* getNext() const { return (next.get()); }
+
+    /// \brief Return the bare pointer to the next node, mutable version.
+    RdataSet* getNext() { return (next.get()); }
+
     /// \brief Return the number of RDATAs stored in the \c RdataSet.
     size_t getRdataCount() const { return (rdata_count_); }
 
@@ -293,6 +344,21 @@ private:
         return (reinterpret_cast<uint16_t*>(this + 1));
     }
 
+    // Shared by both mutable and immutable versions of find()
+    template <typename RdataSetType>
+    static RdataSetType*
+    find(RdataSetType* rdataset_head, const dns::RRType& type) {
+        for (RdataSetType* rdataset = rdataset_head;
+             rdataset != NULL;
+             rdataset = rdataset->getNext()) // use getNext() for efficiency
+        {
+            if (rdataset->type == type) {
+                return (rdataset);
+            }
+        }
+        return (NULL);
+    }
+
     /// \brief The constructor.
     ///
     /// An object of this class is always expected to be created by the
diff --git a/src/lib/datasrc/memory/segment_object_holder.h b/src/lib/datasrc/memory/segment_object_holder.h
new file mode 100644
index 0000000..384f4ef
--- /dev/null
+++ b/src/lib/datasrc/memory/segment_object_holder.h
@@ -0,0 +1,63 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_MEMORY_SEGMENT_OBJECT_HOLDER_H
+#define DATASRC_MEMORY_SEGMENT_OBJECT_HOLDER_H 1
+
+#include <util/memory_segment.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+namespace detail {
+
+// A simple holder to create and use some objects in this implementation
+// in an exception safe manner.   It works like std::auto_ptr but much
+// more simplified.
+// template parameter T is the type of object allocated by mem_sgmt.
+// template parameter ARG_T is the type that will be passed to destroy()
+// (deleter functor, etc).  It must be copyable.
+template <typename T, typename ARG_T>
+class SegmentObjectHolder {
+public:
+    SegmentObjectHolder(util::MemorySegment& mem_sgmt, T* obj, ARG_T arg) :
+        mem_sgmt_(mem_sgmt), obj_(obj), arg_(arg)
+    {}
+    ~SegmentObjectHolder() {
+        if (obj_ != NULL) {
+            T::destroy(mem_sgmt_, obj_, arg_);
+        }
+    }
+    T* get() { return (obj_); }
+    T* release() {
+        T* ret = obj_;
+        obj_ = NULL;
+        return (ret);
+    }
+private:
+    util::MemorySegment& mem_sgmt_;
+    T* obj_;
+    ARG_T arg_;
+};
+
+} // detail
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_SEGMENT_OBJECT_HOLDER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/memory/tests/Makefile.am b/src/lib/datasrc/memory/tests/Makefile.am
index 128b8e7..22fd4bf 100644
--- a/src/lib/datasrc/memory/tests/Makefile.am
+++ b/src/lib/datasrc/memory/tests/Makefile.am
@@ -22,6 +22,9 @@ run_unittests_SOURCES += rdata_serialization_unittest.cc
 run_unittests_SOURCES += rdataset_unittest.cc
 run_unittests_SOURCES += domaintree_unittest.cc
 run_unittests_SOURCES += zone_table_unittest.cc
+run_unittests_SOURCES += zone_data_unittest.cc
+run_unittests_SOURCES += memory_segment_test.h
+run_unittests_SOURCES += segment_object_holder_unittest.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
diff --git a/src/lib/datasrc/memory/tests/domaintree_unittest.cc b/src/lib/datasrc/memory/tests/domaintree_unittest.cc
index cfb223a..ab6cb1e 100644
--- a/src/lib/datasrc/memory/tests/domaintree_unittest.cc
+++ b/src/lib/datasrc/memory/tests/domaintree_unittest.cc
@@ -59,18 +59,13 @@ const size_t Name::MAX_LABELS;
 
 namespace {
 
-class DeleterType {
-public:
-    DeleterType() {}
-
-    void operator()(util::MemorySegment&, int* i) const {
-        delete i;
-    }
-};
+void deleteData(int* i) {
+    delete i;
+}
 
-typedef DomainTree<int, DeleterType> TestDomainTree;
-typedef DomainTreeNode<int, DeleterType> TestDomainTreeNode;
-typedef DomainTreeNodeChain<int, DeleterType> TestDomainTreeNodeChain;
+typedef DomainTree<int> TestDomainTree;
+typedef DomainTreeNode<int> TestDomainTreeNode;
+typedef DomainTreeNodeChain<int> TestDomainTreeNodeChain;
 
 class TreeHolder {
 public:
@@ -78,7 +73,7 @@ public:
         mem_sgmt_(mem_sgmt), tree_(tree)
     {}
     ~TreeHolder() {
-        TestDomainTree::destroy(mem_sgmt_, tree_);
+        TestDomainTree::destroy(mem_sgmt_, tree_, deleteData);
     }
     TestDomainTree* get() { return (tree_); }
 private:
@@ -102,12 +97,13 @@ protected:
         int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
         for (int i = 0; i < name_count; ++i) {
             dtree.insert(mem_sgmt_, Name(domain_names[i]), &dtnode);
-            dtnode->setData(mem_sgmt_, new int(i + 1));
+            // Check the node doesn't have any data initially.
+            EXPECT_EQ(static_cast<int*>(NULL),
+                      dtnode->setData(new int(i + 1)));
 
             dtree_expose_empty_node.insert(mem_sgmt_, Name(domain_names[i]),
                                             &dtnode);
-            dtnode->setData(mem_sgmt_, new int(i + 1));
-
+            EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(i + 1)));
         }
     }
 
@@ -125,13 +121,22 @@ TEST_F(DomainTreeTest, nodeCount) {
 
     // Delete all nodes, then the count should be set to 0.  This also tests
     // the behavior of deleteAllNodes().
-    dtree.deleteAllNodes(mem_sgmt_);
+    dtree.deleteAllNodes(mem_sgmt_, deleteData);
     EXPECT_EQ(0, dtree.getNodeCount());
 }
 
 TEST_F(DomainTreeTest, setGetData) {
-    dtnode->setData(mem_sgmt_, new int(11));
+    // set new data to an existing node.  It should have some data.
+    int* newdata = new int(11);
+    int* olddata = dtnode->setData(newdata);
+    EXPECT_NE(static_cast<int*>(NULL), olddata);
+    deleteData(olddata);
     EXPECT_EQ(11, *(dtnode->getData()));
+
+    // clear the node.  we should get the new data back we just passed.
+    olddata = dtnode->setData(NULL);
+    EXPECT_EQ(newdata, olddata);
+    deleteData(olddata);
 }
 
 TEST_F(DomainTreeTest, insertNames) {
@@ -151,7 +156,9 @@ TEST_F(DomainTreeTest, insertNames) {
                                                   Name("example.com"),
                                                   &dtnode));
     EXPECT_EQ(17, dtree.getNodeCount());
-    dtnode->setData(mem_sgmt_, new int(12));
+    // add data to it; also make sure it doesn't have data right now
+    // (otherwise it would leak)
+    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(12)));
 
     // return ALREADYEXISTS, since node "example.com" already has
     // been explicitly inserted
@@ -381,7 +388,7 @@ performCallbackTest(TestDomainTree& dtree,
     EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt,
                                                   Name("callback.example"),
                                                   &dtnode));
-    dtnode->setData(mem_sgmt, new int(1));
+    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(1)));
     EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
 
     // enable/re-disable callback
@@ -397,7 +404,7 @@ performCallbackTest(TestDomainTree& dtree,
     EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt,
                                                   Name("sub.callback.example"),
                                                   &subdtnode));
-    subdtnode->setData(mem_sgmt, new int(2));
+    EXPECT_EQ(static_cast<int*>(NULL), subdtnode->setData(new int(2)));
     TestDomainTreeNode* parentdtnode;
     EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt,
                                                         Name("example"),
@@ -997,7 +1004,7 @@ TEST_F(DomainTreeTest, root) {
     TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
     TestDomainTree& root(*tree_holder.get());
     root.insert(mem_sgmt_, Name::ROOT_NAME(), &dtnode);
-    dtnode->setData(mem_sgmt_, new int(1));
+    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(1)));
 
     EXPECT_EQ(TestDomainTree::EXACTMATCH,
               root.find(Name::ROOT_NAME(), &cdtnode));
@@ -1009,7 +1016,7 @@ TEST_F(DomainTreeTest, root) {
     // Insert a new name that better matches the query name.  find() should
     // find the better one.
     root.insert(mem_sgmt_, Name("com"), &dtnode);
-    dtnode->setData(mem_sgmt_, new int(2));
+    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(2)));
     EXPECT_EQ(TestDomainTree::PARTIALMATCH,
               root.find(Name("example.com"), &cdtnode));
     EXPECT_EQ(dtnode, cdtnode);
diff --git a/src/lib/datasrc/memory/tests/memory_segment_test.h b/src/lib/datasrc/memory/tests/memory_segment_test.h
new file mode 100644
index 0000000..3195a9b
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/memory_segment_test.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_MEMORY_SEGMENT_TEST_H
+#define DATASRC_MEMORY_SEGMENT_TEST_H 1
+
+#include <util/memory_segment_local.h>
+
+#include <cstddef>              // for size_t
+#include <new>                  // for bad_alloc
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+namespace test {
+
+// A special memory segment that can be used for tests.  It normally behaves
+// like a "local" memory segment.  If "throw count" is set to non 0 via
+// setThrowCount(), it continues the normal behavior until the specified
+// number of calls to allocate(), exclusive, and throws an exception at the
+// next call.  For example, if count is set to 3, the next two calls to
+// allocate() will succeed, and the 3rd call will fail with an exception.
+// This segment object can be used after the exception is thrown, and the
+// count is internally reset to 0.
+class MemorySegmentTest : public isc::util::MemorySegmentLocal {
+public:
+    MemorySegmentTest() : throw_count_(0) {}
+    virtual void* allocate(std::size_t size) {
+        if (throw_count_ > 0) {
+            if (--throw_count_ == 0) {
+                throw std::bad_alloc();
+            }
+        }
+        return (isc::util::MemorySegmentLocal::allocate(size));
+    }
+    void setThrowCount(std::size_t count) { throw_count_ = count; }
+
+private:
+    std::size_t throw_count_;
+};
+
+} // namespace test
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_SEGMENT_TEST_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/memory/tests/rdataset_unittest.cc b/src/lib/datasrc/memory/tests/rdataset_unittest.cc
index 1a83089..897e53c 100644
--- a/src/lib/datasrc/memory/tests/rdataset_unittest.cc
+++ b/src/lib/datasrc/memory/tests/rdataset_unittest.cc
@@ -115,6 +115,25 @@ TEST_F(RdataSetTest, create) {
     RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
 }
 
+TEST_F(RdataSetTest, getNext) {
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                          ConstRRsetPtr());
+
+    // By default, the next pointer should be NULL (already tested in other
+    // test cases), which should be the case with getNext().  We test both
+    // mutable and immutable versions of getNext().
+    EXPECT_EQ(static_cast<RdataSet*>(NULL), rdataset->getNext());
+    EXPECT_EQ(static_cast<const RdataSet*>(NULL),
+              static_cast<const RdataSet*>(rdataset)->getNext());
+
+    // making a link (it would form an infinite loop, but it doesn't matter
+    // in this test), and check the pointer returned by getNext().
+    rdataset->next = rdataset;
+    EXPECT_EQ(rdataset, static_cast<const RdataSet*>(rdataset)->getNext());
+
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
 // A helper function to create an RRset containing the given number of
 // unique RDATAs.
 ConstRRsetPtr
diff --git a/src/lib/datasrc/memory/tests/segment_object_holder_unittest.cc b/src/lib/datasrc/memory/tests/segment_object_holder_unittest.cc
new file mode 100644
index 0000000..d27e364
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/segment_object_holder_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/memory_segment_local.h>
+
+#include <datasrc/memory/segment_object_holder.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::util;
+using namespace isc::datasrc::memory;
+using namespace isc::datasrc::memory::detail;
+
+namespace {
+const int TEST_ARG_VAL = 42;    // arbitrary chosen magic number
+
+class TestObject {
+public:
+    static void destroy(MemorySegment& sgmt, TestObject* obj, int arg) {
+        sgmt.deallocate(obj, sizeof(*obj));
+        EXPECT_EQ(TEST_ARG_VAL, arg);
+    }
+};
+
+void
+useHolder(MemorySegment& sgmt, TestObject* obj, bool release) {
+    // Create a holder object, check the return value of get(), and,
+    // if requested, release the held object.  At the end of function
+    // the holder is destructed, and if the object hasn't been released by
+    // then, it should be deallocated.  Passed argument is checked in its
+    // deallocate().
+
+    typedef SegmentObjectHolder<TestObject, int> HolderType;
+    HolderType holder(sgmt, obj, TEST_ARG_VAL);
+    EXPECT_EQ(obj, holder.get());
+    if (release) {
+        EXPECT_EQ(obj, holder.release());
+    }
+}
+
+TEST(SegmentObjectHolderTest, foo) {
+    MemorySegmentLocal sgmt;
+    void* p = sgmt.allocate(sizeof(TestObject));
+    TestObject* obj = new(p) TestObject;
+
+    // Use holder, and release the content.  The memory shouldn't be
+    // deallocated.
+    useHolder(sgmt, obj, true);
+    EXPECT_FALSE(sgmt.allMemoryDeallocated());
+
+    // Use holder, and let it deallocate the object.  The memory segment
+    // should now be empty.
+    useHolder(sgmt, obj, false);
+    EXPECT_TRUE(sgmt.allMemoryDeallocated());
+}
+}
diff --git a/src/lib/datasrc/memory/tests/zone_data_unittest.cc b/src/lib/datasrc/memory/tests/zone_data_unittest.cc
new file mode 100644
index 0000000..d15fe8b
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/zone_data_unittest.cc
@@ -0,0 +1,255 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "memory_segment_test.h"
+
+#include <dns/rdataclass.h>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/name.h>
+#include <dns/labelsequence.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/rdataset.h>
+#include <datasrc/memory/zone_data.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <new>                  // for bad_alloc
+#include <string>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+using namespace isc::datasrc::memory::test;
+using namespace isc::testutils;
+
+namespace {
+
+// With this single fixture we'll test both NSEC3Data and ZoneData
+class ZoneDataTest : public ::testing::Test {
+protected:
+    ZoneDataTest() :
+        nsec3_data_(NULL), param_rdata_("1 0 12 aabbccdd"),
+        param_rdata_nosalt_("1 1 10 -"),
+        param_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a')),
+        nsec3_rdata_("1 0 12 aabbccdd TDK23RP6 SOA"),
+        nsec3_rdata_nosalt_("1 1 10 - TDK23RP6 SOA"),
+        nsec3_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a') +
+                               " TDK23RP6 SOA"),
+        zname_("example.com"),
+        zone_data_(ZoneData::create(mem_sgmt_, zname_)),
+        a_rrset_(textToRRset("www.example.com. 3600 IN A 192.0.2.1")),
+        aaaa_rrset_(textToRRset("www.example.com. 3600 IN AAAA 2001:db8::1")),
+        nsec3_rrset_(textToRRset("TDK23RP6.example.com. 3600 IN NSEC3 "
+                                 "1 0 12 aabbccdd TDK23RP6 SOA"))
+    {}
+    void TearDown() {
+        if (nsec3_data_ != NULL) {
+            NSEC3Data::destroy(mem_sgmt_, nsec3_data_, RRClass::IN());
+        }
+        if (zone_data_ != NULL) {
+            ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN());
+        }
+        // detect any memory leak in the test memory segment
+        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+    }
+
+    MemorySegmentTest mem_sgmt_;
+    NSEC3Data* nsec3_data_;
+    const generic::NSEC3PARAM param_rdata_, param_rdata_nosalt_,
+        param_rdata_largesalt_;
+    const generic::NSEC3 nsec3_rdata_, nsec3_rdata_nosalt_,
+        nsec3_rdata_largesalt_;
+    const Name zname_;
+    ZoneData* zone_data_;
+    const ConstRRsetPtr a_rrset_, aaaa_rrset_, nsec3_rrset_;
+    RdataEncoder encoder_;
+};
+
+// Shared by both test cases using NSEC3 and NSEC3PARAM Rdata
+template <typename RdataType>
+void
+checkNSEC3Data(MemorySegmentTest& mem_sgmt, const RdataType& expect_rdata) {
+    NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt, expect_rdata);
+
+    // Internal tree should be created and empty.
+    EXPECT_EQ(0, nsec3_data->getNSEC3Tree().getNodeCount());
+
+    EXPECT_EQ(expect_rdata.getHashalg(), nsec3_data->hashalg);
+    EXPECT_EQ(expect_rdata.getFlags(), nsec3_data->flags);
+    EXPECT_EQ(expect_rdata.getIterations(), nsec3_data->iterations);
+    EXPECT_EQ(expect_rdata.getSalt().size(), nsec3_data->getSaltLen());
+    if (expect_rdata.getSalt().size() > 0) {
+        EXPECT_EQ(0, memcmp(&expect_rdata.getSalt()[0],
+                            nsec3_data->getSaltData(),
+                            expect_rdata.getSalt().size()));
+    }
+
+    NSEC3Data::destroy(mem_sgmt, nsec3_data, RRClass::IN());
+}
+
+void
+checkFindRdataSet(const ZoneTree& tree, const Name& name, RRType type,
+                  const RdataSet* expected_set)
+{
+    ZoneNode* node = NULL;
+    tree.find(name, &node);
+    ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+    EXPECT_EQ(expected_set, RdataSet::find(node->getData(), type));
+}
+
+TEST_F(ZoneDataTest, createNSEC3Data) {
+    // Create an NSEC3Data object from various types of RDATA (of NSEC3PARAM
+    // and of NSEC3), check if the resulting parameters match.
+    checkNSEC3Data(mem_sgmt_, param_rdata_); // one 'usual' form of params
+    checkNSEC3Data(mem_sgmt_, param_rdata_nosalt_); // empty salt
+    checkNSEC3Data(mem_sgmt_, param_rdata_largesalt_); // max-len salt
+
+    // Same concepts of the tests, using NSEC3 RDATA.
+    checkNSEC3Data(mem_sgmt_, nsec3_rdata_);
+    checkNSEC3Data(mem_sgmt_, nsec3_rdata_nosalt_);
+    checkNSEC3Data(mem_sgmt_, nsec3_rdata_largesalt_);
+}
+
+TEST_F(ZoneDataTest, addNSEC3) {
+    nsec3_data_ = NSEC3Data::create(mem_sgmt_, param_rdata_);
+
+    ZoneNode* node = NULL;
+    nsec3_data_->insertName(mem_sgmt_, nsec3_rrset_->getName(), &node);
+    ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+    EXPECT_TRUE(node->isEmpty()); // initially it should be empty
+
+    RdataSet* rdataset_nsec3 =
+        RdataSet::create(mem_sgmt_, encoder_, nsec3_rrset_, ConstRRsetPtr());
+    node->setData(rdataset_nsec3);
+
+    // Confirm we can find the added ones from the zone data.
+    checkFindRdataSet(nsec3_data_->getNSEC3Tree(), nsec3_rrset_->getName(),
+                      RRType::NSEC3(), rdataset_nsec3);
+
+    // TearDown() will confirm there's no leak on destroy
+}
+
+TEST_F(ZoneDataTest, getOriginNode) {
+    EXPECT_EQ(LabelSequence(zname_), zone_data_->getOriginNode()->getLabels());
+}
+
+TEST_F(ZoneDataTest, exceptionSafetyOnCreate) {
+    // Note: below, we use our knowledge of how memory allocation happens
+    // within the NSEC3Data, the zone data and the underlying domain tree
+    // implementation.  We'll emulate rare situations where allocate() fails
+    // with an exception, and confirm it doesn't cause any harsh disruption
+    // or leak.
+
+    // Creating internal NSEC3 tree will succeed, but allocation of NSEC3Data
+    // will fail due to bad_alloc.  It shouldn't cause memory leak
+    // (that would be caught in TearDown()).
+    mem_sgmt_.setThrowCount(2);
+    EXPECT_THROW(NSEC3Data::create(mem_sgmt_, param_rdata_), std::bad_alloc);
+
+    // allocate() will throw on the insertion of the origin node.
+    mem_sgmt_.setThrowCount(2);
+    EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
+
+    // allocate() will throw on creating the zone data.
+    mem_sgmt_.setThrowCount(3);
+    EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
+
+    // These incomplete create() attempts shouldn't cause memory leak
+    // (that would be caught in TearDown()).
+}
+
+TEST_F(ZoneDataTest, addRdataSets) {
+    // Insert a name to the zone, and add a couple the data (RdataSet) objects
+    // to the corresponding node.
+
+    ZoneNode* node = NULL;
+    zone_data_->insertName(mem_sgmt_, a_rrset_->getName(), &node);
+    ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+    EXPECT_TRUE(node->isEmpty()); // initially it should be empty
+
+    RdataSet* rdataset_a =
+        RdataSet::create(mem_sgmt_, encoder_, a_rrset_, ConstRRsetPtr());
+    node->setData(rdataset_a);
+
+    RdataSet* rdataset_aaaa =
+        RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset_, ConstRRsetPtr());
+    // make a linked list and replace the list head
+    rdataset_aaaa->next = rdataset_a;
+    node->setData(rdataset_aaaa);
+
+    // Confirm we can find the added ones from the zone data.
+    checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+                      RRType::A(), rdataset_a);
+    checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+                      RRType::AAAA(), rdataset_aaaa);
+    // There's no NS (or anything other than AAAA or A) RdataSet in the list
+    checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+                      RRType::NS(), NULL);
+
+    // TearDown() will confirm there's no leak on destroy
+}
+
+TEST_F(ZoneDataTest, getSetNSEC3Data) {
+    // Initially there's no NSEC3 data
+    EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
+    // isNSEC3Signed is true iff zone data has non NULL NSEC3 data
+    EXPECT_FALSE(zone_data_->isNSEC3Signed());
+
+    // Set a new one.  The set method should return NULL.  The get method
+    // should return the new one.
+    NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt_, param_rdata_);
+    NSEC3Data* old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data);
+    EXPECT_EQ(static_cast<NSEC3Data*>(NULL), old_nsec3_data);
+    EXPECT_EQ(nsec3_data, zone_data_->getNSEC3Data());
+    EXPECT_TRUE(zone_data_->isNSEC3Signed());
+
+    // Replace an existing one with a yet another one.
+    // We're responsible for destroying the old one.
+    NSEC3Data* nsec3_data2 = NSEC3Data::create(mem_sgmt_, nsec3_rdata_);
+    old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data2);
+    EXPECT_EQ(nsec3_data, old_nsec3_data);
+    EXPECT_EQ(nsec3_data2, zone_data_->getNSEC3Data());
+    EXPECT_TRUE(zone_data_->isNSEC3Signed());
+    NSEC3Data::destroy(mem_sgmt_, old_nsec3_data, RRClass::IN());
+
+    // Setting NULL clears any existing one.
+    old_nsec3_data = zone_data_->setNSEC3Data(NULL);
+    EXPECT_EQ(nsec3_data2, old_nsec3_data);
+    EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
+    EXPECT_FALSE(zone_data_->isNSEC3Signed());
+
+    // Then set it again.  The zone data should destroy it on its own
+    // destruction.
+    zone_data_->setNSEC3Data(old_nsec3_data);
+}
+
+TEST_F(ZoneDataTest, isSigned) {
+    // By default it's considered unsigned
+    EXPECT_FALSE(zone_data_->isSigned());
+
+    // declare it's signed, the isSigned() says so too
+    zone_data_->setSigned(true);
+    EXPECT_TRUE(zone_data_->isSigned());
+
+    // change it to unsigned again
+    zone_data_->setSigned(false);
+    EXPECT_FALSE(zone_data_->isSigned());
+}
+}
diff --git a/src/lib/datasrc/memory/tests/zone_table_unittest.cc b/src/lib/datasrc/memory/tests/zone_table_unittest.cc
index 4f0af5e..359df49 100644
--- a/src/lib/datasrc/memory/tests/zone_table_unittest.cc
+++ b/src/lib/datasrc/memory/tests/zone_table_unittest.cc
@@ -55,21 +55,23 @@ private:
 
 class ZoneTableTest : public ::testing::Test {
 protected:
-    ZoneTableTest() : zname1(Name("example.com")),
+    ZoneTableTest() : zclass_(RRClass::IN()),
+                      zname1(Name("example.com")),
                       zname2(Name("example.net")),
                       zname3(Name("example")),
-                      zone_table(ZoneTable::create(mem_sgmt_))
+                      zone_table(ZoneTable::create(mem_sgmt_, zclass_))
     {}
     ~ZoneTableTest() {
         if (zone_table != NULL) {
-            ZoneTable::destroy(mem_sgmt_, zone_table);
+            ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
         }
     }
     void TearDown() {
-        ZoneTable::destroy(mem_sgmt_, zone_table);
+        ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
         zone_table = NULL;
         EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
     }
+    const RRClass zclass_;
     const Name zname1, zname2, zname3;
     TestMemorySegment mem_sgmt_;
     ZoneTable* zone_table;
@@ -80,41 +82,46 @@ TEST_F(ZoneTableTest, create) {
     // tests.  We only check exception safety by letting the test memory
     // segment throw.
     mem_sgmt_.setThrowCount(2);
-    EXPECT_THROW(ZoneTable::create(mem_sgmt_), std::bad_alloc);
+    EXPECT_THROW(ZoneTable::create(mem_sgmt_, zclass_), std::bad_alloc);
     // This shouldn't cause memory leak (that would be caught in TearDown()).
 }
 
 TEST_F(ZoneTableTest, addZone) {
     // Normal successful case.
     const ZoneTable::AddResult result1 =
-        zone_table->addZone(mem_sgmt_, zname1);
+        zone_table->addZone(mem_sgmt_, zclass_, zname1);
     EXPECT_EQ(result::SUCCESS, result1.code);
 
     // Duplicate add doesn't replace the existing data.
-    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zname1).code);
+    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
+                                                 zname1).code);
     EXPECT_EQ(result1.zone_data,
-              zone_table->addZone(mem_sgmt_, zname1).zone_data);
+              zone_table->addZone(mem_sgmt_, zclass_, zname1).zone_data);
     // names are compared in a case insensitive manner.
-    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_,
+    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
                                                  Name("EXAMPLE.COM")).code);
     // Add some more different ones.  Should just succeed.
-    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname2).code);
-    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname3).code);
+    EXPECT_EQ(result::SUCCESS,
+              zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
+    EXPECT_EQ(result::SUCCESS,
+              zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
 
     // Have the memory segment throw an exception in extending the internal
     // tree.  It still shouldn't cause memory leak (which would be detected
     // in TearDown()).
     mem_sgmt_.setThrowCount(2);
-    EXPECT_THROW(zone_table->addZone(mem_sgmt_, Name("example.org")),
+    EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org")),
                  std::bad_alloc);
 }
 
 TEST_F(ZoneTableTest, findZone) {
     const ZoneTable::AddResult add_result1 =
-        zone_table->addZone(mem_sgmt_, zname1);
+        zone_table->addZone(mem_sgmt_, zclass_, zname1);
     EXPECT_EQ(result::SUCCESS, add_result1.code);
-    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname2).code);
-    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname3).code);
+    EXPECT_EQ(result::SUCCESS,
+              zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
+    EXPECT_EQ(result::SUCCESS,
+              zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
 
     const ZoneTable::FindResult find_result1 =
         zone_table->findZone(Name("example.com"));
@@ -135,7 +142,7 @@ TEST_F(ZoneTableTest, findZone) {
 
     // make sure the partial match is indeed the longest match by adding
     // a zone with a shorter origin and query again.
-    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_,
+    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_,
                                                    Name("com")).code);
     EXPECT_EQ(add_result1.zone_data,
               zone_table->findZone(Name("www.example.com")).zone_data);
diff --git a/src/lib/datasrc/memory/zone_data.cc b/src/lib/datasrc/memory/zone_data.cc
new file mode 100644
index 0000000..033af15
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_data.cc
@@ -0,0 +1,170 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/memory_segment.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rdataclass.h>
+
+#include "rdataset.h"
+#include "rdata_serialization.h"
+#include "zone_data.h"
+#include "segment_object_holder.h"
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
+#include <cassert>
+#include <new>                  // for the placement new
+#include <vector>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+namespace {
+void
+rdataSetDeleter(RRClass rrclass, util::MemorySegment* mem_sgmt,
+                RdataSet* rdataset_head)
+{
+    RdataSet* rdataset_next;
+    for (RdataSet* rdataset = rdataset_head;
+         rdataset != NULL;
+         rdataset = rdataset_next)
+    {
+        rdataset_next = rdataset->getNext();
+        RdataSet::destroy(*mem_sgmt, rrclass, rdataset);
+    }
+}
+
+void
+nullDeleter(RdataSet* rdataset_head) {
+    assert(rdataset_head == NULL);
+}
+}
+
+NSEC3Data*
+NSEC3Data::create(util::MemorySegment& mem_sgmt,
+                  const generic::NSEC3PARAM& rdata)
+{
+    return (NSEC3Data::create(mem_sgmt, rdata.getHashalg(), rdata.getFlags(),
+                              rdata.getIterations(), rdata.getSalt()));
+}
+
+NSEC3Data*
+NSEC3Data::create(util::MemorySegment& mem_sgmt, const generic::NSEC3& rdata) {
+    return (NSEC3Data::create(mem_sgmt, rdata.getHashalg(), rdata.getFlags(),
+                              rdata.getIterations(), rdata.getSalt()));
+}
+
+NSEC3Data*
+NSEC3Data::create(util::MemorySegment& mem_sgmt, uint8_t hashalg,
+                  uint8_t flags, uint16_t iterations,
+                  const std::vector<uint8_t>& salt)
+{
+    // NSEC3Data allocation can throw.  To avoid leaking the tree, we manage
+    // it in the holder.
+    // Note: we won't add any RdataSet, so we use the NO-OP deleter
+    // (with an assertion check for that).
+    typedef boost::function<void(RdataSet*)> RdataSetDeleterType;
+    detail::SegmentObjectHolder<ZoneTree, RdataSetDeleterType> holder(
+        mem_sgmt, ZoneTree::create(mem_sgmt, true),
+        boost::bind(nullDeleter, _1));
+
+    const size_t salt_len = salt.size();
+
+    void* p = mem_sgmt.allocate(sizeof(NSEC3Data) + 1 + salt_len);
+    NSEC3Data* const param_data =
+        new(p) NSEC3Data(holder.release(), hashalg, flags, iterations);
+    uint8_t* dp = param_data->getSaltBuf();
+    *dp++ = salt_len;
+    if (salt_len > 0) {
+        memcpy(dp, &salt.at(0), salt_len); // use at for safety
+    }
+
+    return (param_data);
+}
+
+void
+NSEC3Data::destroy(util::MemorySegment& mem_sgmt, NSEC3Data* data,
+                   RRClass nsec3_class)
+{
+    ZoneTree::destroy(mem_sgmt, data->nsec3_tree_.get(),
+                      boost::bind(rdataSetDeleter, nsec3_class, &mem_sgmt,
+                                  _1));
+    mem_sgmt.deallocate(data, sizeof(NSEC3Data) + 1 + data->getSaltLen());
+}
+
+void
+NSEC3Data::insertName(util::MemorySegment& mem_sgmt, const Name& name,
+                      ZoneNode** node)
+{
+    const ZoneTree::Result result = nsec3_tree_->insert(mem_sgmt, name, node);
+
+    // This should be ensured by the API:
+    assert((result == ZoneTree::SUCCESS ||
+            result == ZoneTree::ALREADYEXISTS) && node != NULL);
+}
+
+ZoneData*
+ZoneData::create(util::MemorySegment& mem_sgmt, const Name& zone_origin) {
+    // ZoneTree::insert() and ZoneData allocation can throw.  See also
+    // NSEC3Data::create().
+    typedef boost::function<void(RdataSet*)> RdataSetDeleterType;
+    detail::SegmentObjectHolder<ZoneTree, RdataSetDeleterType> holder(
+        mem_sgmt, ZoneTree::create(mem_sgmt, true),
+        boost::bind(nullDeleter, _1));
+
+    ZoneTree* tree = holder.get();
+    ZoneNode* origin_node = NULL;
+    const ZoneTree::Result result =
+        tree->insert(mem_sgmt, zone_origin, &origin_node);
+    assert(result == ZoneTree::SUCCESS);
+    void* p = mem_sgmt.allocate(sizeof(ZoneData));
+    ZoneData* zone_data = new(p) ZoneData(holder.release(), origin_node);
+
+    return (zone_data);
+}
+
+void
+ZoneData::destroy(util::MemorySegment& mem_sgmt, ZoneData* zone_data,
+                  RRClass zone_class)
+{
+    ZoneTree::destroy(mem_sgmt, zone_data->zone_tree_.get(),
+                      boost::bind(rdataSetDeleter, zone_class, &mem_sgmt,
+                                  _1));
+    if (zone_data->nsec3_data_) {
+        NSEC3Data::destroy(mem_sgmt, zone_data->nsec3_data_.get(), zone_class);
+    }
+    mem_sgmt.deallocate(zone_data, sizeof(ZoneData));
+}
+
+void
+ZoneData::insertName(util::MemorySegment& mem_sgmt, const Name& name,
+                     ZoneNode** node)
+{
+    const ZoneTree::Result result = zone_tree_->insert(mem_sgmt, name, node);
+
+    // This should be ensured by the API:
+    assert((result == ZoneTree::SUCCESS ||
+            result == ZoneTree::ALREADYEXISTS) && node != NULL);
+}
+
+} // namespace memory
+} // namespace datasrc
+} // datasrc isc
diff --git a/src/lib/datasrc/memory/zone_data.h b/src/lib/datasrc/memory/zone_data.h
index 31c4520..f592680 100644
--- a/src/lib/datasrc/memory/zone_data.h
+++ b/src/lib/datasrc/memory/zone_data.h
@@ -17,29 +17,542 @@
 
 #include <util/memory_segment.h>
 
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/memory/domaintree.h>
+#include <datasrc/memory/rdataset.h>
+
+#include <boost/interprocess/offset_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <vector>
+
 namespace isc {
+namespace dns {
+namespace rdata {
+namespace generic {
+class NSEC3PARAM;
+class NSEC3;
+}
+}
+}
+
 namespace datasrc {
 namespace memory {
-/// \brief Data for a single zone.
+
+typedef DomainTree<RdataSet> ZoneTree;
+typedef DomainTreeNode<RdataSet> ZoneNode;
+
+/// \brief NSEC3 data for a DNS zone.
+///
+/// This class encapsulates a set of NSEC3 related data for a zone
+/// that is signed with NSEC3 RRs.  Specifically, it contains hash
+/// parameters as given in an NSEC3PARAM RDATA and all NSEC3 RRs of the zone.
 ///
-/// It's currently empty and is only provided for the implementation of
-/// ZoneTable.  The actual implementation of this class is the subject of
-/// Trac #2107.
-class ZoneData {
+/// The main concept of the class is generally the same as that of
+/// \c ZoneData (see its description for details), but the related data
+//// are encapsulated in a more straightforward way in this class.
+///
+/// The NSEC3 RRs (which should normally have RRSIGs) are stored in a
+/// \c DomainTree object whose data type is (a list of) \c RdataSet.
+/// This tree is expected to store NSEC3 RRs only, so the RR type of
+/// \c RdataSet should be NSEC3.  But this class itself doesn't guarantee
+/// this condition.  It's the caller's responsibility.
+///
+/// Read-only access to the tree is possible via the \c getNSEC3Tree() method.
+/// Modifying the tree must be done by a specific method (in the initial
+/// implementation, it's \c insertName().  There may be some more as we
+/// see the need); the application cannot directly change the content of the
+/// tree in an arbitrary way.  This class does not have a strong reason to be
+/// that strict, but is defined this way mainly to be consistent with the
+/// \c ZoneData class.
+///
+/// Most of the hash parameters are maintained in the form of straightforward
+/// member variables, which can be directly referenced by the application.
+/// The exception is the salt, which is encapsulated as opaque data
+/// immediately following the main class object, and should be accessible
+/// via the \c getSaltLen() and \c getSaltData() method.
+///
+/// \note The fact that the this class couples one set of hash parameters
+/// and the set of NSEC3 RRs implicitly means a zone is assumed to have
+/// only one set of NSEC3 parameters.  When we support multiple sets of
+/// parameters the design should be revised accordingly.
+class NSEC3Data : boost::noncopyable {
+public:
+    /// \brief Allocate and construct \c NSEC3Data from NSEC3PARAM Rdata.
+    ///
+    /// The NSEC3 parameters are extracted and stored within the created
+    /// \c NSEC3Data object.
+    ///
+    /// \throw std::bad_alloc Memory allocation fails.
+    ///
+    /// \param mem_sgmt A \c MemorySegment from which memory for the new
+    /// \c NSEC3Data is allocated.
+    /// \param rdata An NSEC3PARAM RDATA that specifies the NSEC3 parameters
+    /// to be stored.
+    static NSEC3Data* create(util::MemorySegment& mem_sgmt,
+                             const dns::rdata::generic::NSEC3PARAM& rdata);
+
+    /// \brief Allocate and construct \c NSEC3Data from NSEC3 Rdata.
+    ///
+    /// The NSEC3 hash parameters are extracted and stored within the created
+    /// \c NSEC3Data object.
+    ///
+    /// \throw std::bad_alloc Memory allocation fails.
+    ///
+    /// \param mem_sgmt A \c MemorySegment from which memory for the new
+    /// \c NSEC3Data is allocated.
+    /// \param rdata An NSEC3 RDATA that specifies the NSEC3 parameters
+    /// to be stored.
+    static NSEC3Data* create(util::MemorySegment& mem_sgmt,
+                             const dns::rdata::generic::NSEC3& rdata);
+
+    /// \brief Destruct and deallocate \c NSEC3Data.
+    ///
+    /// It releases all resources allocated for the internal NSEC3 name space
+    /// including NSEC3 RdataSet.  It assumes \c RdataSets objects stored
+    /// in the space were allocated using the same memory segment as
+    /// \c mem_sgmt.  The caller must ensure this assumption.
+    ///
+    /// Note that an \c RRClass object must be passed to this method.
+    /// It's necessary to destroy the stored \c RdataSet objects
+    /// (see its class description).  This class doesn't hold this information;
+    /// it's the caller's responsibility to associate an \c NSEC3Data
+    /// class object with its expected RR class, and pass it to
+    /// \c destroy().  (In practice, it will be passed via
+    /// \c ZoneData::destroy().)
+    ///
+    /// \throw none
+    ///
+    /// \param mem_sgmt The \c MemorySegment that allocated memory for
+    /// \c data.
+    /// \param data A non-NULL pointer to a valid NSEC3Data object
+    /// that was originally created by the \c create() method (the behavior
+    /// is undefined if this condition isn't met).
+    /// \param nsec3_class The RR class of the \c RdataSet stored in the NSEC3
+    /// name space to be destroyed.
+    static void destroy(util::MemorySegment& mem_sgmt, NSEC3Data* data,
+                        dns::RRClass nsec3_class);
+
 private:
-    ZoneData() {}
-    ~ZoneData() {}
+    // Domain tree for the Internal NSEC3 name space.  Access to it is
+    // limited only via public methods.
+    const boost::interprocess::offset_ptr<ZoneTree> nsec3_tree_;
 public:
-    static ZoneData* create(util::MemorySegment& mem_sgmt) {
-        void* p = mem_sgmt.allocate(sizeof(ZoneData));
-        ZoneData* zone_data = new(p) ZoneData();
-        return (zone_data);
+    const uint8_t hashalg;      ///< Hash algorithm
+    const uint8_t flags;        ///< NSEC3 parameter flags
+    const uint16_t iterations;  ///< Hash iterations
+    // For 64-bit machines there'll be padding space here, but since
+    // only at most one instance (or a few in very rare cases) will be
+    // created per zone, the overhead should be acceptable.
+
+    /// \brief Return \c ZoneTree for the NSEC3 name space.
+    ///
+    /// \throw none
+    const ZoneTree& getNSEC3Tree() const { return (*nsec3_tree_); }
+
+    /// \brief Return the size of NSEC3 salt.
+    ///
+    /// \throw none
+    ///
+    /// The return value must be in the range between 0 and 255 (inclusive).
+    size_t getSaltLen() const { return (*getSaltBuf()); }
+
+    /// \brief Return a pointer to the salt data.
+    ///
+    /// \throw none
+    ///
+    /// The valid range is up to the \c getSaltLen() bytes from the
+    /// returned value.  If \c getSaltLen() returns 0, the return value
+    /// of this method is invalid and must not be used.
+    const uint8_t* getSaltData() const { return (getSaltBuf() + 1); }
+
+    /// \brief Insert a name to the NSEC3 name space.
+    ///
+    /// It allocates resource for the given name in the internal NSEC3 name
+    /// space, and returns an access point to it in the form of \c ZoneNode
+    /// pointer via the given \c node variable.  If the name already exists
+    /// in the name space, it returns a pointer to the existing node.
+    ///
+    /// This method does not perform any semantics check on the given name
+    /// (e.g., whether the first label is a valid encoded string for an NSEC3
+    /// owner name).
+    ///
+    /// \throw std::bad_alloc Memory allocation fails
+    ///
+    /// \param mem_sgmt Memory segment in which resource for the new memory
+    /// is to be allocated.
+    /// \param name The name to be inserted.
+    /// \param node A pointer to \c ZoneNode pointer in which the created or
+    /// found node for the name is stored.  Must not be NULL (the method does
+    /// not check that condition).
+    void insertName(util::MemorySegment& mem_sgmt, const dns::Name& name,
+                    ZoneNode** node);
+
+private:
+    // Common subroutine for the public versions of create().
+    static NSEC3Data* create(util::MemorySegment& mem_sgmt, uint8_t hashalg,
+                             uint8_t flags, uint16_t iterations,
+                             const std::vector<uint8_t>& salt);
+
+    /// \brief The constructor.
+    ///
+    /// An object of this class is always expected to be created by the
+    /// allocator (\c create()), so the constructor is hidden as private.
+    ///
+    /// It never throws an exception.
+    NSEC3Data(ZoneTree* nsec3_tree_param, uint8_t hashalg_param,
+              uint8_t flags_param, uint16_t iterations_param) :
+        nsec3_tree_(nsec3_tree_param), hashalg(hashalg_param),
+        flags(flags_param), iterations(iterations_param)
+    {}
+
+    const uint8_t* getSaltBuf() const {
+        return (reinterpret_cast<const uint8_t*>(this + 1));
     }
-    static void destroy(util::MemorySegment& mem_sgmt, ZoneData* zone_data) {
-        zone_data->~ZoneData();
-        mem_sgmt.deallocate(zone_data, sizeof(ZoneData));
+    uint8_t* getSaltBuf() {
+        return (reinterpret_cast<uint8_t*>(this + 1));
     }
 };
+
+/// \brief DNS zone data.
+///
+/// This class encapsulates the content of a DNS zone (which is essentially a
+/// set of RRs) in a memory efficient way and provides accessor interfaces
+/// to it.
+///
+/// The primary goal of this class is to provide a packed structure of the
+/// data for memory efficiency.  Basically, this class should be considered
+/// a private part of some other classes within this module and should not
+/// be used directly from normal applications.  So it's not intended to hide
+/// much of the underlying implementation details; rather, it tries
+/// to keep the representation simple.
+///
+/// The RRs are stored in a \c DomainTree object whose data type is
+/// (a list of) \c RdataSet.  The tree nodes correspond to owner names,
+/// and the \c RdataSet objects (forming a linked list) set in the node
+/// represent the rest of the RR parameters except the RR class: type,
+/// TTL, and RDATA.  This class does not have any knowledge of the RR class
+/// of the zone; since it's quite likely that the application maintains
+/// a set of zones of the same RR class, and the number of such zones can be
+/// huge, it makes more sense to have the application maintain the class value
+/// in a unified way to minimize memory footprint.
+///
+/// The \c DomainTree object in this class is not expected to hold NSEC3
+/// RRs when the zone is signed with NSEC3; they should be maintained
+/// in an associated \c NSEC3Data object.  But this class does not prevent
+/// the unexpected usage of adding an NSEC3 RdataSet directly in the tree.
+/// It's the caller's responsibility to ensure this assumption.
+///
+/// This class maintains some other meta data and additional zone related
+/// content.  First, it automatically creates a \c DomainTree node for the
+/// zone's origin name on initialization and keeps a reference to it
+/// throughout its lifetime.  This is the case even if the zone doesn't have
+/// any RRs (such as in the case before initial loading).  Any valid zone
+/// to be served should have an RR at the origin node (at least SOA, for
+/// example), so this assumption should be reasonable.  But the application
+/// must ensure that any \c ZoneData object in actual use should have an
+/// RR at the origin; otherwise the inconsistency between the internal state
+/// and the actual zone content could lead to unexpected disruption.
+/// In particular, it must be careful when it supports dynamic updates
+/// to an existing zone so an update attempt doesn't result in deleting
+/// the origin node.
+///
+/// To ensure integrity regarding the reference to the origin, write
+/// access to the tree node can be done only by public methods; the member
+/// variable for the tree is hidden as private.  On the other hand, read-only
+/// access to the tree is allowed via the const version of \c getZoneTree()
+/// method for the convenience of the application.  So, it's intentional
+/// that there's no non-const version of this method.  Do not add one
+/// when this class is to be extended.
+///
+/// Another type of meta data is parameters and records of NSEC3 RRs
+/// when the zone is signed with NSEC3.  It's represented in the form of
+/// an \c NSEC3Data object, and a \c ZoneData object may be associated with
+/// 0 or 1 \c NSEC3Data objects using the \c setNSEC3Data() method, which
+/// can be retrieved by the \c getNSEC3Data() method.  If the \c ZoneData
+/// object is not associated with an \c NSEC3Data object, it's considered not
+/// signed with NSEC3 RRs; otherwise it's considered to be signed with
+/// NSEC3 RRs and with the parameters stored in the \c NSEC3Data object.
+///
+/// \note This interpretation may change in the future when we support migration
+/// from NSEC to NSEC3 or vice versa, support incremental signing, or support
+/// multiple sets of NSEC3 parameters.
+///
+/// One last type of meta data is the status of the zone in terms of DNSSEC
+/// signing.  This class supports the following concepts:
+/// - Whether the zone is signed or not, either with NSEC records or NSEC3
+///   records.
+/// - Whether the zone has a complete set of NSEC3 records.
+///
+/// The former status can be accessed via the \c isSigned() and \c setSigned()
+/// methods; the latter can be retrieved via the \c isNSEC3Signed() method.
+///
+/// This class does not actually relate the status of signed-or-not to
+/// any of its other attributes; it's up to the application how to set or
+/// use this status and maintain it in a reasonable way.  One possible
+/// definition is to set this status if and only if the zone has a
+/// DNSKEY RR at the zone origin (which is BIND 9's definition of signed
+/// zone).  When the application adopts this definition, it's the
+/// application's responsibility to keep the status consistent with the
+/// actual existence or non-existence of a DNSKEY RR.
+///
+/// In the current implementation, a zone is considered to have a complete
+/// set of NSEC3 records if and only if it's associated with an \c NSEC3Data
+/// object (as noted above, these concepts may be separated in future).
+/// For this reason there is no "set" method for the latter; setting
+/// an \c NSEC3Data effectively enables the latter status.  \c isNSEC3Signed()
+/// method is still provided (even though it's a kind of trivial wrapper to
+/// \c getNSEC3Data()) partly for a more intuitive shortcut, and partly
+/// because we won't have to change the application code when we implement
+/// the future separation.
+///
+/// The intended usage of these two status concepts is to implement the
+/// \c ZoneFinder::Context::isNSECSigned() and
+/// \c ZoneFinder::Context::isNSEC3Signed() methods.  A possible implementation
+/// is as follows:
+/// - \c ZoneFinder::Context::isNSECSigned() returns true iff \c isSigned()
+///   is true and \c isNSEC3Signed() is false.
+/// - \c ZoneFinder::Context::isNSEC3Signed() returns true iff \c isSigned()
+///   is true and \c isNSEC3Signed() is true.
+///
+/// Note that even though \c isNSEC3Signed() being true should indicate
+/// \c isSigned() is true too in practice, the interfaces do not
+/// automatically ensure that, so we'd need to check both conditions
+/// explicitly.  And, in fact, if we adopt the above definition of
+/// \c isSigned(), it's possible that a zone has a complete set of NSEC3
+/// RRs but no DNSKEY (although it's effectively a broken zone unless we
+/// support incremental signing).
+///
+/// This class is designed so an instance can be stored in a shared
+/// memory region.  So the pointer member variables (the initial
+/// implementation only contains pointer member variables) are defined
+/// as offset pointers.  When this class is extended these properties must
+/// be preserved, and must also meet other requirements so it can be stored
+/// in a shared memory region (see, for example, \c RdataSet description).
+/// Future extensions must also be conscious of placing the member variables
+/// so that they will not accidentally cause padding and increase memory
+/// footprint.
+class ZoneData : boost::noncopyable {
+private:
+    /// \brief The constructor.
+    ///
+    /// An object of this class is always expected to be created by the
+    /// allocator (\c create()), so the constructor is hidden as private.
+    ///
+    /// It never throws an exception.
+    ZoneData(ZoneTree* zone_tree, ZoneNode* origin_node) :
+        zone_tree_(zone_tree), origin_node_(origin_node)
+    {}
+
+    // Zone node flags.
+private:
+    // Set in the origin node (which always exists at the same address)
+    // to indicate whether the zone is signed or not.  Internal use,
+    // so defined as private.
+    static const ZoneNode::Flags DNSSEC_SIGNED = ZoneNode::FLAG_USER1;
+public:
+    /// \brief Node flag indicating it is at a "wildcard level"
+    ///
+    /// This means one of the node's immediate children is a wildcard.
+    static const ZoneNode::Flags WILDCARD_NODE = ZoneNode::FLAG_USER2;
+
+public:
+    /// \brief Allocate and construct \c ZoneData.
+    ///
+    /// \throw std::bad_alloc Memory allocation fails.
+    ///
+    /// \param mem_sgmt A \c MemorySegment from which memory for the new
+    /// \c ZoneData is allocated.
+    /// \param name The zone name.
+    static ZoneData* create(util::MemorySegment& mem_sgmt,
+                            const dns::Name& zone_name);
+
+    /// \brief Destruct and deallocate \c ZoneData.
+    ///
+    /// It releases all resource allocated in the internal storage NSEC3 for
+    /// zone names and RdataSet objects, and if associated, the \c NSEC3Data.
+    /// It assumes \c RdataSets objects stored in the space and the
+    /// associated \c NSEC3Data object were allocated using the same memory
+    /// segment as \c mem_sgmt.  The caller must ensure this assumption.
+    ///
+    /// Note that an \c RRClass object must be passed to this method.
+    /// It's used to destroy the stored \c RdataSet objects
+    /// (see its class description).  This class doesn't hold this information;
+    /// it's the caller's responsibility to associate a \c ZoneData class
+    /// object with its expected RR class, and pass it to \c destroy().
+    ///
+    /// \throw none
+    ///
+    /// \param mem_sgmt The \c MemorySegment that allocated memory for
+    /// \c zone_data.
+    /// \param zone_data A non-NULL pointer to a valid ZoneData object
+    /// that was originally created by the \c create() method (the behavior
+    /// is undefined if this condition isn't met).
+    /// \param zone_class The RR class of the \c RdataSet stored in the
+    /// internal tree.
+    static void destroy(util::MemorySegment& mem_sgmt, ZoneData* zone_data,
+                        dns::RRClass zone_class);
+
+    ///
+    /// \name Getter methods
+    ///
+    //@{
+    /// \brief Return zone's origin node.
+    ///
+    /// This is a convenience and efficient short cut to get access to the
+    /// zone origin in the form of \c ZoneNode object.
+    ///
+    /// The class encapsulation ensures that the origin node always exists at
+    /// the same address, so this method always returns a non-NULL valid
+    /// valid pointer.
+    ///
+    /// \throw none
+    const ZoneNode* getOriginNode() const {
+        return (origin_node_.get());
+    }
+
+    /// \brief Return the zone's name space in the form of \c ZoneTree
+    ///
+    /// \note It's intentional that non-const version of this method
+    /// isn't provided.  See the class description.
+    ///
+    /// \throw none
+    const ZoneTree& getZoneTree() const { return (*zone_tree_); }
+
+    /// \brief Return whether or not the zone is signed in terms of DNSSEC.
+    ///
+    /// Note that this class does not care about what "signed" means.
+    /// This method simply returns the last value set by \c setSigned()
+    /// (or the default, which is \c false).  The caller is expected to
+    /// use this method and \c setSigned() in a reasonable, consistent way.
+    ///
+    /// \throw none
+    bool isSigned() const { return (origin_node_->getFlag(DNSSEC_SIGNED)); }
+
+    /// \brief Return whether or not the zone is signed with NSEC3 RRs.
+    ///
+    /// In the current implementation, the zone is considered signed with
+    /// NSEC3 if and only if it has non-NULL NSEC3 data.
+    ///
+    /// This also means it's not considered NSEC3 signed by default.
+    ///
+    /// \throw none
+    bool isNSEC3Signed() const { return (nsec3_data_); }
+
+    /// \brief Return NSEC3Data of the zone.
+    ///
+    /// This method returns non-NULL valid pointer to \c NSEC3Data object
+    /// associated to the \c ZoneData if it was set by \c setNSEC3Data();
+    /// otherwise it returns NULL.
+    ///
+    /// \throw none
+    const NSEC3Data* getNSEC3Data() const { return (nsec3_data_.get()); }
+    //@}
+
+    ///
+    /// \name Methods for modifying the tree
+    ///
+    //@{
+    /// \brief Insert a name to the zone.
+    ///
+    /// It allocates resource for the given name in the internal storage
+    /// for zone data, and returns an access point to it in the form of
+    /// \c ZoneNode pointer via the given \c node variable.  If the name
+    /// already exists in the name space, it returns a pointer to the existing
+    /// node.
+    ///
+    /// The name to be inserted by this method is expected to belong to
+    /// zone's "normal" (i.e., non-NSEÇ3) name space.  If it's a name for
+    /// an NSEC3 RR, it must be set in the corresponding \c NSEC3Data for
+    /// this zone data (if it doesn't exist it must be created and set
+    /// by \c setNSEC3Data()).
+    ///
+    /// The name is also expected to be a subdomain of, or equal to the
+    /// zone's origin name (specified on creation in \c create()), but
+    /// this method does not check that condition.  The caller is responsible
+    /// for ensuring this assumption.
+    ///
+    /// Since this method doesn't perform any semantics check, it always
+    /// succeeds (except for the rare case where memory allocation
+    /// fails) and \c node will be set to a valid pointer.
+    ///
+    /// \note We may want to differentiate between the case where the name is
+    /// newly created and the case where it already existed.  Right now it's
+    /// unclear, so it doesn't return this information.  If we see the need
+    /// for it, this method can be extended that way.
+    ///
+    /// \throw std::bad_alloc Memory allocation fails
+    ///
+    /// \param mem_sgmt Memory segment in which resource for the new memory
+    /// is to be allocated.
+    /// \param name The name to be inserted.
+    /// \param node A pointer to \c ZoneNode pointer in which the created or
+    /// found node for the name is stored.  Must not be NULL (the method does
+    /// not check that condition).
+    void insertName(util::MemorySegment& mem_sgmt, const dns::Name& name,
+                    ZoneNode** node);
+
+    /// \brief Specify whether or not the zone is signed in terms of DNSSEC.
+    ///
+    /// The zone will be considered "signed" (in that subsequent calls to
+    /// \c isSigned() will return \c true) iff the parameter \c on is \c true.
+    ///
+    /// This class does not care what "signed" actually means; it does not
+    /// check any zone RRs to verify if the given state makes sense (e.g.
+    /// whether the zone has a DNSKEY RR at the origin).  The caller is
+    /// expected to use this method and \c isSigned() in a reasonable,
+    /// consistent way.
+    ///
+    /// \throw none
+    void setSigned(bool on) {
+        origin_node_->setFlag(DNSSEC_SIGNED, on);
+    }
+
+    /// \brief Return NSEC3Data of the zone, non-const version.
+    ///
+    /// This is similar to the const version, but return a non-const pointer
+    /// so the caller can modify the content.
+    ///
+    /// \throw none
+    NSEC3Data* getNSEC3Data() { return (nsec3_data_.get()); }
+
+    /// \brief Associate \c NSEC3Data to the zone.
+    ///
+    /// This method associates the given \c NSEC3Data object with the zone
+    /// data.  If there was already associated \c NSEC3Data object, it will
+    /// be returned.  If no \c NSEC3Data object was associated before,
+    /// a NULL pointer will be returned.  \c nsec3_data can be NULL, in which
+    /// case the zone will be disassociated with a \c NSEC3Data.
+    ///
+    /// In general, if a non-NULL pointer is passed, it's assumed that
+    /// the \c NSEC3Data object was allocated in the same \c MemorySegment
+    /// as that for the zone data, so the \c destroy() method can destroy
+    /// both with the same memory segment.  If this condition is not met,
+    /// the caller must extract the associated \c NSEC3Data by calling
+    /// this method with NULL and release any resource for it by itself
+    /// before destroying this zone data.
+    ///
+    /// \throw none
+    ///
+    /// \param nsec3_data A pointer to \c NSEC3Data object to be associated
+    /// with the zone.  Can be NULL.
+    /// \return Previously associated \c NSEC3Data object in the zone.  This
+    /// can be NULL.
+    NSEC3Data* setNSEC3Data(NSEC3Data* nsec3_data) {
+        NSEC3Data* old = nsec3_data_.get();
+        nsec3_data_ = nsec3_data;
+        return (old);
+    }
+    //@}
+
+private:
+    const boost::interprocess::offset_ptr<ZoneTree> zone_tree_;
+    const boost::interprocess::offset_ptr<ZoneNode> origin_node_;
+    boost::interprocess::offset_ptr<NSEC3Data> nsec3_data_;
+};
+
 } // namespace memory
 } // namespace datasrc
 } // namespace isc
diff --git a/src/lib/datasrc/memory/zone_table.cc b/src/lib/datasrc/memory/zone_table.cc
index 9c410d0..f9b2768 100644
--- a/src/lib/datasrc/memory/zone_table.cc
+++ b/src/lib/datasrc/memory/zone_table.cc
@@ -19,6 +19,10 @@
 #include <datasrc/memory/zone_data.h>
 #include <datasrc/memory/zone_table.h>
 #include <datasrc/memory/domaintree.h>
+#include <datasrc/memory/segment_object_holder.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
 
 #include <cassert>
 
@@ -28,43 +32,25 @@ using namespace isc::dns;
 namespace isc {
 namespace datasrc {
 namespace memory {
-namespace {
-// A simple holder to create and use some objects in this implementation
-// in an exception safe manner.   It works like std::auto_ptr but much
-// more simplified.
-template <typename T>
-class Holder {
-public:
-    Holder(util::MemorySegment& mem_sgmt, T* obj) :
-        mem_sgmt_(mem_sgmt), obj_(obj)
-    {}
-    ~Holder() {
-        if (obj_ != NULL) {
-            T::destroy(mem_sgmt_, obj_);
-        }
-    }
-    T* get() { return (obj_); }
-    T* release() {
-        T* ret = obj_;
-        obj_ = NULL;
-        return (ret);
-    }
-private:
-    util::MemorySegment& mem_sgmt_;
-    T* obj_;
-};
-}
+using detail::SegmentObjectHolder;
 
+namespace {
 void
-ZoneTable::ZoneDataDeleter::operator()(util::MemorySegment& mem_sgmt,
-                                       ZoneData* zone_data) const
+deleteZoneData(util::MemorySegment* mem_sgmt, ZoneData* zone_data,
+               RRClass rrclass)
 {
-    ZoneData::destroy(mem_sgmt, zone_data);
+    if (zone_data != NULL) {
+        ZoneData::destroy(*mem_sgmt, zone_data, rrclass);
+    }
+}
+typedef boost::function<void(ZoneData*)> ZoneDataDeleterType;
 }
 
 ZoneTable*
-ZoneTable::create(util::MemorySegment& mem_sgmt) {
-    Holder<ZoneTableTree> holder(mem_sgmt, ZoneTableTree::create(mem_sgmt));
+ZoneTable::create(util::MemorySegment& mem_sgmt, RRClass zone_class) {
+    SegmentObjectHolder<ZoneTableTree, ZoneDataDeleterType> holder(
+        mem_sgmt, ZoneTableTree::create(mem_sgmt),
+        boost::bind(deleteZoneData, &mem_sgmt, _1, zone_class));
     void* p = mem_sgmt.allocate(sizeof(ZoneTable));
     ZoneTable* zone_table = new(p) ZoneTable(holder.get());
     holder.release();
@@ -72,20 +58,27 @@ ZoneTable::create(util::MemorySegment& mem_sgmt) {
 }
 
 void
-ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable) {
-    ZoneTableTree::destroy(mem_sgmt, ztable->zones_.get());
+ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable,
+                   RRClass zone_class)
+{
+    ZoneTableTree::destroy(mem_sgmt, ztable->zones_.get(),
+                           boost::bind(deleteZoneData, &mem_sgmt, _1,
+                                       zone_class));
     mem_sgmt.deallocate(ztable, sizeof(ZoneTable));
 }
 
 ZoneTable::AddResult
-ZoneTable::addZone(util::MemorySegment& mem_sgmt, const Name& zone_name) {
+ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class,
+                   const Name& zone_name)
+{
     // Create a new ZoneData instance first.  If the specified name already
     // exists in the table, the new data will soon be destroyed, but we want
     // to make sure if this allocation fails the tree won't be changed to
     // provide as strong guarantee as possible.  In practice, we generally
     // expect the caller tries to add a zone only when it's a new one, so
     // this should be a minor concern.
-    Holder<ZoneData> holder(mem_sgmt, ZoneData::create(mem_sgmt));
+    SegmentObjectHolder<ZoneData, RRClass> holder(
+        mem_sgmt, ZoneData::create(mem_sgmt, zone_name), zone_class);
 
     // Get the node where we put the zone
     ZoneTableNode* node(NULL);
@@ -103,7 +96,7 @@ ZoneTable::addZone(util::MemorySegment& mem_sgmt, const Name& zone_name) {
 
     // Is it empty? We either just created it or it might be nonterminal
     if (node->isEmpty()) {
-        node->setData(mem_sgmt, holder.get());
+        node->setData(holder.get());
         return (AddResult(result::SUCCESS, holder.release()));
     } else { // There's something there already
         return (AddResult(result::EXIST, node->getData()));
diff --git a/src/lib/datasrc/memory/zone_table.h b/src/lib/datasrc/memory/zone_table.h
index b5da957..2faf606 100644
--- a/src/lib/datasrc/memory/zone_table.h
+++ b/src/lib/datasrc/memory/zone_table.h
@@ -17,6 +17,8 @@
 
 #include <util/memory_segment.h>
 
+#include <dns/rrclass.h>
+
 #include <datasrc/result.h>
 #include <datasrc/memory/domaintree.h>
 
@@ -68,8 +70,8 @@ private:
     };
 
     // Type aliases to make it shorter
-    typedef DomainTree<ZoneData, ZoneDataDeleter> ZoneTableTree;
-    typedef DomainTreeNode<ZoneData, ZoneDataDeleter> ZoneTableNode;
+    typedef DomainTree<ZoneData> ZoneTableTree;
+    typedef DomainTreeNode<ZoneData> ZoneTableNode;
 
 public:
     /// \brief Result data of addZone() method.
@@ -114,18 +116,27 @@ public:
     ///
     /// \param mem_sgmt A \c MemorySegment from which memory for the new
     /// \c ZoneTable is allocated.
-    static ZoneTable* create(util::MemorySegment& mem_sgmt);
+    /// \param zone_class The RR class of the zone.  It must be the RR class
+    /// that is supposed to be associated to the zone table.
+    static ZoneTable* create(util::MemorySegment& mem_sgmt,
+                             dns::RRClass zone_class);
 
     /// \brief Destruct and deallocate \c ZoneTable
     ///
+    /// This method releases all internal resources including all zone data
+    /// created via \c addZone() calls.
+    ///
     /// \throw none
     ///
     /// \param mem_sgmt The \c MemorySegment that allocated memory for
-    /// \c ztable.
+    /// \c ztable and used for prior calls to \c addZone().
+    /// \param zone_class The RR class of the zone.  It must be the RR class
+    /// that is supposed to be associated to the zone table.
     /// \param ztable A non NULL pointer to a valid \c ZoneTable object
     /// that was originally created by the \c create() method (the behavior
     /// is undefined if this condition isn't met).
-    static void destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable);
+    static void destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable,
+                        dns::RRClass zone_class);
 
     /// Add a new zone to the \c ZoneTable.
     ///
@@ -135,14 +146,23 @@ public:
     /// zone name already exists in the table, a new data object won't be
     /// created; instead, the existing corresponding data will be returned.
     ///
+    /// The zone table keeps the ownership of the created zone data; the
+    /// caller must not try to destroy it directly.  (We'll eventually
+    /// add an interface to delete specific zone data from the table).
+    ///
     /// \throw std::bad_alloc Internal resource allocation fails.
     ///
+    /// \param mem_sgmt The \c MemorySegment to allocate zone data to be
+    /// created.  It must be the same segment that was used to create
+    /// the zone table at the time of create().
     /// \param zone_name The name of the zone to be added.
+    /// \param zone_class The RR class of the zone.  It must be the RR class
+    /// that is supposed to be associated to the zone table.
     /// \return \c result::SUCCESS If the zone is successfully
     /// added to the zone table.
     /// \return \c result::EXIST The zone table already contains
     /// zone of the same origin.
-    AddResult addZone(util::MemorySegment& mem_sgmt,
+    AddResult addZone(util::MemorySegment& mem_sgmt, dns::RRClass zone_class,
                       const dns::Name& zone_name);
 
     /// Find a zone that best matches the given name in the \c ZoneTable.
diff --git a/src/lib/dns/labelsequence.h b/src/lib/dns/labelsequence.h
index caadd90..545ce12 100644
--- a/src/lib/dns/labelsequence.h
+++ b/src/lib/dns/labelsequence.h
@@ -229,15 +229,25 @@ public:
 
     /// \brief Compares two label sequences for equality.
     ///
-    /// Performs a (optionally case-insensitive) comparison between this
+    /// Performs a (optionally case-sensitive) comparison between this
     /// LabelSequence and another LabelSequence for equality.
     ///
     /// \param other The LabelSequence to compare with
-    /// \param case_sensitive If true, comparison is case-insensitive
+    /// \param case_sensitive If true, comparison is case-sensitive
     /// \return true if The label sequences consist are the same length,
     ///         and contain the same data.
     bool equals(const LabelSequence& other, bool case_sensitive = false) const;
 
+    /// \brief Compares two label sequences for equality (case ignored).
+    ///
+    /// This is equivalent to <code>this->equals(other)</code>.
+    ///
+    /// The operator version is convenient some specific cases such as in
+    /// unit tests.
+    bool operator==(const LabelSequence& other) const {
+        return (equals(other));
+    }
+
     /// \brief Compares two label sequences.
     ///
     /// Performs a (optionally case-insensitive) comparison between this
diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc
index 2ce1ef7..5eb4941 100644
--- a/src/lib/dns/tests/labelsequence_unittest.cc
+++ b/src/lib/dns/tests/labelsequence_unittest.cc
@@ -177,6 +177,15 @@ TEST_F(LabelSequenceTest, equals_insensitive) {
     EXPECT_TRUE(ls11.equals(ls12));
 }
 
+// operator==().  This is mostly trivial wrapper, so it should suffice to
+// check some basic cases.
+TEST_F(LabelSequenceTest, operatorEqual) {
+    EXPECT_TRUE(ls1 == ls1);      // self equivalence
+    EXPECT_TRUE(ls1 == LabelSequence(n1)); // equivalent two different objects
+    EXPECT_FALSE(ls1 == ls2);      // non equivalent objects
+    EXPECT_TRUE(ls1 == ls5);       // it's always case insensitive
+}
+
 // Compare tests
 TEST_F(LabelSequenceTest, compare) {
     // "example.org." and "example.org.", case sensitive



More information about the bind10-changes mailing list