Browse Source

[master] Merge branch 'trac2098'

JINMEI Tatuya 12 years ago
parent
commit
b372a33a10

+ 1 - 0
src/lib/datasrc/memory/Makefile.am

@@ -12,6 +12,7 @@ noinst_LTLIBRARIES = libdatasrc_memory.la
 
 libdatasrc_memory_la_SOURCES = domaintree.h
 libdatasrc_memory_la_SOURCES += rdataset.h rdataset.cc
+libdatasrc_memory_la_SOURCES += treenode_rrset.h treenode_rrset.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 += segment_object_holder.h

+ 5 - 2
src/lib/datasrc/memory/benchmarks/Makefile.am

@@ -9,10 +9,13 @@ endif
 
 CLEANFILES = *.gcno *.gcda
 
-noinst_PROGRAMS = rdata_reader_bench
+noinst_PROGRAMS = rdata_reader_bench rrset_render_bench
 
 rdata_reader_bench_SOURCES = rdata_reader_bench.cc
-
 rdata_reader_bench_LDADD = $(top_builddir)/src/lib/datasrc/memory/libdatasrc_memory.la
 rdata_reader_bench_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 rdata_reader_bench_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
+
+rrset_render_bench_SOURCES = rrset_render_bench.cc
+rrset_render_bench_LDADD = $(top_builddir)/src/lib/datasrc/memory/libdatasrc_memory.la
+rrset_render_bench_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la

+ 247 - 0
src/lib/datasrc/memory/benchmarks/rrset_render_bench.cc

@@ -0,0 +1,247 @@
+// 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 <bench/benchmark.h>
+
+#include <util/buffer.h>
+#include <util/memory_segment_local.h>
+
+#include <dns/name.h>
+#include <dns/messagerenderer.h>
+#include <dns/rrset.h>
+#include <dns/rrclass.h>
+#include <dns/masterload.h>
+
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/rdataset.h>
+#include <datasrc/memory/treenode_rrset.h>
+#include <datasrc/memory/zone_data.h>
+
+#include <boost/bind.hpp>
+
+#include <cassert>
+#include <vector>
+#include <sstream>
+
+#include <unistd.h>
+
+using std::vector;
+using namespace isc::bench;
+using namespace isc::datasrc::memory;
+using namespace isc::dns;
+
+namespace {
+// A simple benchmark just repeating rendering a given set of RRsets.
+class RRsetRenderBenchMark {
+public:
+    RRsetRenderBenchMark(const vector<ConstRRsetPtr>& rrsets,
+                         MessageRenderer& renderer) :
+        rrsets_(rrsets), renderer_(renderer)
+    {}
+    unsigned int run() {
+        renderer_.clear();
+        vector<ConstRRsetPtr>::const_iterator it;
+        const vector<ConstRRsetPtr>::const_iterator it_end = rrsets_.end();
+        for (it = rrsets_.begin(); it != it_end; ++it) {
+            (*it)->toWire(renderer_);
+        }
+        return (1);
+    }
+private:
+    const vector<ConstRRsetPtr>& rrsets_;
+    MessageRenderer& renderer_;
+};
+
+// Builtin benchmark data.  This is a list of RDATA (of RRs) in a response
+// from a root server for the query for "www.example.com" (as of this
+// implementation).  We use a real world example to make the case practical.
+const char* const delegation_rrsets_txt =
+    // AUTHORITY SECTION (NS)
+    "com. 172800 IN NS a.gtld-servers.net.\n"
+    "com. 172800 IN NS b.gtld-servers.net.\n"
+    "com. 172800 IN NS c.gtld-servers.net.\n"
+    "com. 172800 IN NS d.gtld-servers.net.\n"
+    "com. 172800 IN NS e.gtld-servers.net.\n"
+    "com. 172800 IN NS f.gtld-servers.net.\n"
+    "com. 172800 IN NS g.gtld-servers.net.\n"
+    "com. 172800 IN NS h.gtld-servers.net.\n"
+    "com. 172800 IN NS i.gtld-servers.net.\n"
+    "com. 172800 IN NS j.gtld-servers.net.\n"
+    "com. 172800 IN NS k.gtld-servers.net.\n"
+    "com. 172800 IN NS l.gtld-servers.net.\n"
+    "com. 172800 IN NS m.gtld-servers.net.\n"
+    // AUTHORITY SECTION (DS)
+    "com. 86400 IN DS 30909 8 2 "
+    "E2D3C916F6DEEAC73294E8268FB5885044A833FC5459588F4A9184CFC41A5766\n"
+    // AUTHORITY SECTION (RRSIG for DS)
+    "com. 86400 IN RRSIG DS 8 1 86400 20120822000000 20120814230000 50398 . "
+    "lcIpLRq4s91Fh1FihDXiDvVMMRqgy2jjlpiP4Y6sSjIrLue6Boi7xraj"
+    "Ouka34ubpl4KuIcopWe99LI/7Npvq0MYr9DaqfnX9dTW6Vc2C7/hKSsz"
+    "POYjraZZA3SCApgfNVzq+AscYlShi56f1vm7DQWw1eh1wHLdatidrQwNyDo=\n"
+    // ADDITIONAL SECTION
+    "a.gtld-servers.net. 172800 IN A 192.5.6.30\n"
+    "b.gtld-servers.net. 172800 IN A 192.33.14.30\n"
+    "c.gtld-servers.net. 172800 IN A 192.26.92.30\n"
+    "d.gtld-servers.net. 172800 IN A 192.31.80.30\n"
+    "e.gtld-servers.net. 172800 IN A 192.12.94.30\n"
+    "f.gtld-servers.net. 172800 IN A 192.35.51.30\n"
+    "g.gtld-servers.net. 172800 IN A 192.42.93.30\n"
+    "h.gtld-servers.net. 172800 IN A 192.54.112.30\n"
+    "i.gtld-servers.net. 172800 IN A 192.43.172.30\n"
+    "j.gtld-servers.net. 172800 IN A 192.48.79.30\n"
+    "k.gtld-servers.net. 172800 IN A 192.52.178.30\n"
+    "l.gtld-servers.net. 172800 IN A 192.41.162.30\n"
+    "m.gtld-servers.net. 172800 IN A 192.55.83.30\n"
+    "a.gtld-servers.net. 172800 IN AAAA 2001:503:a83e::2:30\n"
+    "b.gtld-servers.net. 172800 IN AAAA 2001:503:231d::2:30";
+
+// Likewise, it's a response from a root server for www.example.notexistent.
+// An example that involves a few more RRSIGs.
+const char* const nxdomain_rrsets_txt =
+    // AUTHORITY SECTION (NS)
+    ". 86400 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2012082901 1800 900 604800 86400\n"
+    ". 86400 IN RRSIG SOA 8 0 86400 20120905000000 20120828230000 50398 . Wj6NgkA2TyJug9XfWYG/Cjh4d1JI99/Dv9S1TClbrhLdeMI4s9BEcBSA xdlPJN6L4uaupcYeEyRWGx3p0iq7VWFyjL4xy/+jXpUoHCgj0NlWa6eu C2litcnfcumTI97XJ5iod3ET+6vWnhwZQHDnz1fj4FuS8PPEJJ+Oy3M5 mcM=\n"
+    ". 86400 IN NSEC ac. NS SOA RRSIG NSEC DNSKEY\n"
+    ". 86400 IN RRSIG NSEC 8 0 86400 20120905000000 20120828230000 50398 . f8FEG1HzQtn/shZCdDgkB7TIpHAmH2hGlp5RubczukzD8XI3EGMVNoaf 2cUNPLpyhoBQkO1Rz+hANzI6Jkqq8PzhiQnLcW+y2Wl/BU2SaBQ2Otb2 5+VOOXux1veKaCZsRRU1VJnTUYguDayAgcS7BtB/rc7ez4nokkK98MA9 zcw=\n"
+    "no. 86400 IN NSEC np. NS RRSIG NSEC\n"
+    "no. 86400 IN RRSIG NSEC 8 1 86400 20120905000000 20120828230000 50398 . Z/E4hb6MMSpueGGjGoCWwnN2uKsQf88HPS1gbwVHBEhR+5eSn0BGqExs fQsjYL47SF/6pwMXjzmXCt8NPXXf9ImY/93GUuFL6j5OuL2MDIxt24MS u6hfxxiYTn+zF0dM8cn+UK5n1VEBB5JVJXf7FOr3OmaRLMD33Gl6yZJ/ l1E=";
+
+void
+usage() {
+    std::cerr << "Usage: rrset_render_bench [-n iterations]" << std::endl;
+    exit (1);
+}
+
+// Helper callback for masterLoad() used in main() to build test data.
+void
+setRRset(vector<ConstRRsetPtr>* rrsets, ConstRRsetPtr rrset) {
+    rrsets->push_back(rrset);
+}
+
+void
+buildZone(isc::util::MemorySegmentLocal& mem_sgmt,
+          ZoneData* zone_data, const vector<ConstRRsetPtr>& rrsets,
+          vector<ConstRRsetPtr>& rrsets_build)
+{
+    RdataEncoder encoder;
+
+    for (vector<ConstRRsetPtr>::const_iterator it = rrsets.begin();
+         it != rrsets.end();
+         ++it)
+    {
+        ConstRRsetPtr rrset = *it;
+        ZoneNode* node;
+        zone_data->insertName(mem_sgmt, rrset->getName(), &node);
+
+        // For simplicity, we assume that if the current RRset has a next
+        // one that is of type RRSIG, that RRSIG should be associated with
+        // the current RRset (which should be the case for our test data).
+        ConstRRsetPtr sig_rrset;
+        if ((it + 1) != rrsets.end() &&
+            (*(it + 1))->getType() == RRType::RRSIG()) {
+            sig_rrset = *(++it);
+            assert(it != rrsets.end()); // to be safe, and silence cppcheck
+        }
+        RdataSet* rdataset =
+            RdataSet::create(mem_sgmt, encoder, rrset, sig_rrset);
+        rdataset->next = node->getData();
+        node->setData(rdataset);
+
+        rrsets_build.push_back(
+            ConstRRsetPtr(new TreeNodeRRset(rrset->getClass(), node, rdataset,
+                                            true)));
+    }
+}
+}
+
+int
+main(int argc, char* argv[]) {
+    int ch;
+    int iteration = 100000;
+    while ((ch = getopt(argc, argv, "n:")) != -1) {
+        switch (ch) {
+        case 'n':
+            iteration = atoi(optarg);
+            break;
+        default:
+            usage();
+        }
+    }
+    argc -= optind;
+    argv += optind;
+    if (argc != 0) {
+        usage();
+    }
+
+    // Build test data.  rrsets will consist of a list of RRsets corresponding
+    // to the text defined above.
+    vector<ConstRRsetPtr> delegation_rrsets;
+    std::stringstream rrsets_stream(delegation_rrsets_txt);
+    masterLoad(rrsets_stream, Name::ROOT_NAME(), RRClass::IN(),
+               boost::bind(setRRset, &delegation_rrsets, _1));
+
+    vector<ConstRRsetPtr> nxdomain_rrsets;
+    std::stringstream rrsets_stream2(nxdomain_rrsets_txt);
+    masterLoad(rrsets_stream2, Name::ROOT_NAME(), RRClass::IN(),
+               boost::bind(setRRset, &nxdomain_rrsets, _1));
+
+    // Build in-memory zone using RRsets constructed above, storing
+    // the same set of RRsets as TreeNodeRRsets in separate vectors.
+    // This code below is not 100% exception safe (for simplicity), but at
+    // least it shouldn't leak memory in normal cases.
+    isc::util::MemorySegmentLocal mem_sgmt;
+    ZoneData* zone_data = ZoneData::create(mem_sgmt, Name::ROOT_NAME());
+    vector<ConstRRsetPtr> delegation_treenode_rrsets;
+    buildZone(mem_sgmt, zone_data, delegation_rrsets,
+              delegation_treenode_rrsets);
+    vector<ConstRRsetPtr> nxdomain_treenode_rrsets;
+    buildZone(mem_sgmt, zone_data, nxdomain_rrsets,
+              nxdomain_treenode_rrsets);
+
+    // The benchmark test uses a message renderer.  Create it now and keep
+    // using it throughout the test.
+    isc::util::OutputBuffer buffer(4096); // 4096 should be sufficiently large
+    MessageRenderer renderer;
+    renderer.setBuffer(&buffer);
+
+    std::cout << "Benchmark for rendering basic RRsets (delegation)"
+              << std::endl;
+    BenchMark<RRsetRenderBenchMark>(iteration,
+                                    RRsetRenderBenchMark(delegation_rrsets,
+                                                         renderer));
+
+    std::cout << "Benchmark for rendering tree node RRsets (delegation)"
+              << std::endl;
+    BenchMark<RRsetRenderBenchMark>(iteration,
+                                    RRsetRenderBenchMark(
+                                        delegation_treenode_rrsets, renderer));
+
+    std::cout << "Benchmark for rendering basic RRsets (nxdomain)"
+              << std::endl;
+    BenchMark<RRsetRenderBenchMark>(iteration,
+                                    RRsetRenderBenchMark(nxdomain_rrsets,
+                                                         renderer));
+
+    std::cout << "Benchmark for rendering tree node RRsets (nxdomain)"
+              << std::endl;
+    BenchMark<RRsetRenderBenchMark>(iteration,
+                                    RRsetRenderBenchMark(
+                                        nxdomain_treenode_rrsets, renderer));
+
+    // Cleanup, and memory leak check
+    ZoneData::destroy(mem_sgmt, zone_data, RRClass::IN());
+    assert(mem_sgmt.allMemoryDeallocated());
+
+    return (0);
+}

+ 35 - 0
src/lib/datasrc/memory/domaintree.h

@@ -226,6 +226,26 @@ public:
         return (dns::LabelSequence(getLabelsData()));
     }
 
+    /// \brief Return the absolute label sequence of the node.
+    ///
+    /// This method returns the label sequence corresponding to the full
+    /// name of the node; i.e. the entire name as it appears in the zone.
+    ///
+    /// It takes the (partial) name of the node itself, and extends it
+    /// with all upper nodes.
+    ///
+    /// \note Care must be taken with the buffer that is used here; this
+    /// method overwrites its data, so it should not be associated with
+    /// any other LabelSequence during the lifetime of the LabelSequence
+    /// returned by this method. See LabelSequence::extend(), which is used
+    /// by this method.
+    ///
+    /// \param buf A data buffer where the label sequence will be built.
+    ///            The data in this buffer will be overwritten by this call.
+    /// \return A LabelSequence with the absolute name of this node.
+    isc::dns::LabelSequence getAbsoluteLabels(
+        uint8_t buf[isc::dns::LabelSequence::MAX_SERIALIZED_LENGTH]) const;
+
     /// \brief Return the data stored in this node.
     ///
     /// You should not delete the data, it is deleted when the tree is
@@ -531,6 +551,21 @@ DomainTreeNode<T>::getUpperNode() const {
 }
 
 template <typename T>
+isc::dns::LabelSequence
+DomainTreeNode<T>::getAbsoluteLabels(
+    uint8_t buf[isc::dns::LabelSequence::MAX_SERIALIZED_LENGTH]) const
+{
+    isc::dns::LabelSequence result(getLabels(), buf);
+    const DomainTreeNode<T>* upper = getUpperNode();
+    while (upper != NULL) {
+        result.extend(upper->getLabels(), buf);
+        upper = upper->getUpperNode();
+    }
+
+    return (result);
+}
+
+template <typename T>
 const DomainTreeNode<T>*
 DomainTreeNode<T>::abstractSuccessor(
     typename DomainTreeNode<T>::DomainTreeNodePtr DomainTreeNode<T>::*left,

+ 4 - 0
src/lib/datasrc/memory/rdata_serialization.cc

@@ -248,6 +248,10 @@ public:
     virtual void setTruncated() {}
     virtual void setLengthLimit(size_t) {}
     virtual void setCompressMode(CompressMode) {}
+    virtual void writeName(const LabelSequence&, bool) {
+        // We don't need this version of writeName
+        isc_throw(Unexpected, "unexpected version of writeName is called");
+    }
 
     // Called for each domain name in the RDATA, from the RDATA's toWire()
     // implementation.

+ 1 - 0
src/lib/datasrc/memory/tests/Makefile.am

@@ -21,6 +21,7 @@ run_unittests_SOURCES = run_unittests.cc
 run_unittests_SOURCES += rdata_serialization_unittest.cc
 run_unittests_SOURCES += rdataset_unittest.cc
 run_unittests_SOURCES += domaintree_unittest.cc
+run_unittests_SOURCES += treenode_rrset_unittest.cc
 run_unittests_SOURCES += zone_table_unittest.cc
 run_unittests_SOURCES += zone_data_unittest.cc
 run_unittests_SOURCES += memory_segment_test.h

+ 52 - 0
src/lib/datasrc/memory/tests/domaintree_unittest.cc

@@ -114,6 +114,7 @@ protected:
     TestDomainTree& dtree_expose_empty_node;
     TestDomainTreeNode* dtnode;
     const TestDomainTreeNode* cdtnode;
+    uint8_t buf[LabelSequence::MAX_SERIALIZED_LENGTH];
 };
 
 TEST_F(DomainTreeTest, nodeCount) {
@@ -470,6 +471,11 @@ TEST_F(DomainTreeTest, chainLevel) {
               tree.find(node_name, &cdtnode, chain));
     EXPECT_EQ(1, chain.getLevelCount());
 
+    // Check the name of the found node (should have '.' as both non-absolute
+    // and absolute name
+    EXPECT_EQ(".", cdtnode->getLabels().toText());
+    EXPECT_EQ(".", cdtnode->getAbsoluteLabels(buf).toText());
+
     /*
      * Now creating a possibly deepest tree with MAX_LABELS levels.
      * it should look like:
@@ -493,6 +499,12 @@ TEST_F(DomainTreeTest, chainLevel) {
         EXPECT_EQ(TestDomainTree::EXACTMATCH,
                   tree.find(node_name, &cdtnode, found_chain));
         EXPECT_EQ(i, found_chain.getLevelCount());
+
+        // The non-absolute name should only have the first label
+        EXPECT_EQ("a", cdtnode->getLabels().toText());
+        // But the absolute name should have all labels
+        EXPECT_EQ(node_name.toText(),
+                  cdtnode->getAbsoluteLabels(buf).toText());
     }
 
     // Confirm the last inserted name has the possible maximum length with
@@ -1039,4 +1051,44 @@ TEST_F(DomainTreeTest, root) {
               root.find(Name("example.com"), &cdtnode));
     EXPECT_EQ(dtnode, cdtnode);
 }
+
+TEST_F(DomainTreeTest, getAbsoluteLabels) {
+    // The full absolute names of the nodes in the tree
+    // with the addition of the explicit root node
+    const char* const domain_names[] = {
+        "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
+        "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
+    // The names of the nodes themselves, as they end up in the tree
+    const char* const first_labels[] = {
+        "c", "b", "a", "x", "z", "g.h", "i", "o",
+        "j", "p", "q", "k"};
+
+    const int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
+    for (int i = 0; i < name_count; ++i) {
+        EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name(domain_names[i]),
+                  &cdtnode));
+
+        // First make sure the names themselves are not absolute
+        const LabelSequence ls(cdtnode->getLabels());
+        EXPECT_EQ(first_labels[i], ls.toText());
+        EXPECT_FALSE(ls.isAbsolute());
+
+        // Now check the absolute names
+        const LabelSequence abs_ls(cdtnode->getAbsoluteLabels(buf));
+        EXPECT_EQ(Name(domain_names[i]).toText(), abs_ls.toText());
+        EXPECT_TRUE(abs_ls.isAbsolute());
+    }
+
+    // Explicitly add and find a root node, to see that getAbsoluteLabels
+    // also works when getLabels() already returns an absolute LabelSequence
+    dtree.insert(mem_sgmt_, Name("."), &dtnode);
+    dtnode->setData(new int(1));
+
+    EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("."), &cdtnode));
+
+    EXPECT_TRUE(cdtnode->getLabels().isAbsolute());
+    EXPECT_EQ(".", cdtnode->getLabels().toText());
+    EXPECT_TRUE(cdtnode->getAbsoluteLabels(buf).isAbsolute());
+    EXPECT_EQ(".", cdtnode->getAbsoluteLabels(buf).toText());
+}
 }

+ 568 - 0
src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc

@@ -0,0 +1,568 @@
+// 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/buffer.h>
+#include <util/memory_segment_local.h>
+
+#include <datasrc/memory/treenode_rrset.h>
+#include <datasrc/memory/rdataset.h>
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/zone_data.h>
+
+#include <util/unittests/wiredata.h>
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+using std::vector;
+using std::string;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+using namespace isc::testutils;
+using isc::util::unittests::matchWireData;
+using isc::util::OutputBuffer;
+
+namespace {
+
+class TreeNodeRRsetTest : public ::testing::Test {
+protected:
+    TreeNodeRRsetTest() :
+        rrclass_(RRClass::IN()),
+        origin_name_("example.com"), www_name_("www.example.com"),
+        wildcard_name_("*.example.com"), match_name_("match.example.com"),
+        ns_rrset_(textToRRset("example.com. 3600 IN NS ns.example.com.")),
+        a_rrset_(textToRRset("www.example.com. 3600 IN A 192.0.2.1\n"
+                             "www.example.com. 3600 IN A 192.0.2.2")),
+        aaaa_rrset_(textToRRset("www.example.com. 3600 IN AAAA "
+                                "2001:db8::1\n")),
+        dname_rrset_(textToRRset("example.com. 3600 IN DNAME d.example.org.")),
+        a_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG "
+                                   "A 5 2 3600 20120814220826 20120715220826 "
+                                   "1234 example.com. FAKE")),
+        aaaa_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG AAAA 5 2"
+                                      " 3600 20120814220826 20120715220826 "
+                                      "1234 example.com. FAKE\n"
+                                      "www.example.com. 3600 IN RRSIG AAAA 5 2"
+                                      " 3600 20120814220826 20120715220826 "
+                                      "4321 example.com. FAKE\n")),
+        txt_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG TXT 5 2"
+                                     " 3600 20120814220826 20120715220826 "
+                                     "1234 example.com. FAKE\n")),
+        wildmatch_rrset_(textToRRset(
+                             "match.example.com. 3600 IN A 192.0.2.1\n"
+                             "match.example.com. 3600 IN A 192.0.2.2")),
+        wildmatch_rrsig_rrset_(textToRRset(
+                                   "match.example.com. 3600 IN RRSIG "
+                                   "A 5 2 3600 20120814220826 20120715220826 "
+                                   "1234 example.com. FAKE")),
+        zone_data_(NULL), origin_node_(NULL), www_node_(NULL),
+        wildcard_node_(NULL), ns_rdataset_(NULL), dname_rdataset_(NULL),
+        a_rdataset_(NULL), aaaa_rdataset_(NULL), rrsig_only_rdataset_(NULL),
+        wildcard_rdataset_(NULL)
+    {}
+    void SetUp() {
+        // We create some common test data here in SetUp() so it will be
+        // as exception safe as possible.
+
+        zone_data_ = ZoneData::create(mem_sgmt_, origin_name_);
+
+        zone_data_->insertName(mem_sgmt_, origin_name_, &origin_node_);
+        ns_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, ns_rrset_,
+                                        ConstRRsetPtr());
+        origin_node_->setData(ns_rdataset_);
+        dname_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, dname_rrset_,
+                                           ConstRRsetPtr());
+        ns_rdataset_->next = dname_rdataset_;
+
+        zone_data_->insertName(mem_sgmt_, www_name_, &www_node_);
+        a_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                       a_rrsig_rrset_);
+        www_node_->setData(a_rdataset_);
+
+        aaaa_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset_,
+                                          aaaa_rrsig_rrset_);
+        a_rdataset_->next = aaaa_rdataset_;
+
+        // A rare (half broken) case of RRSIG-only set
+        rrsig_only_rdataset_ = RdataSet::create(mem_sgmt_, encoder_,
+                                                ConstRRsetPtr(),
+                                                txt_rrsig_rrset_);
+        aaaa_rdataset_->next = rrsig_only_rdataset_;
+
+        zone_data_->insertName(mem_sgmt_, wildcard_name_, &wildcard_node_);
+        wildcard_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                              a_rrsig_rrset_);
+        wildcard_node_->setData(wildcard_rdataset_);
+    }
+    void TearDown() {
+        ZoneData::destroy(mem_sgmt_, zone_data_, rrclass_);
+        // detect any memory leak
+        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+    }
+
+    const RRClass rrclass_;
+    const Name origin_name_, www_name_, wildcard_name_, match_name_;
+    isc::util::MemorySegmentLocal mem_sgmt_;
+    RdataEncoder encoder_;
+    MessageRenderer renderer_, renderer_expected_;
+    ConstRRsetPtr ns_rrset_, a_rrset_, aaaa_rrset_, dname_rrset_,
+        a_rrsig_rrset_, aaaa_rrsig_rrset_, txt_rrsig_rrset_,
+        wildmatch_rrset_, wildmatch_rrsig_rrset_;
+    ZoneData* zone_data_;
+    ZoneNode* origin_node_;
+    ZoneNode* www_node_;
+    ZoneNode* wildcard_node_;
+    RdataSet* ns_rdataset_;
+    RdataSet* dname_rdataset_;
+    RdataSet* a_rdataset_;
+    RdataSet* aaaa_rdataset_;
+    RdataSet* rrsig_only_rdataset_;
+    RdataSet* wildcard_rdataset_; // for wildcard (type doesn't matter much)
+};
+
+// Check some trivial fields of a constructed TreeNodeRRset (passed as
+// AbstractRRset as we'd normally use it in polymorphic way).
+// Other complicated fields are checked through rendering tests.
+void
+checkBasicFields(const AbstractRRset& actual_rrset, const Name& expected_name,
+                 const RRClass& expected_class, const RRType& expected_type,
+                 size_t expected_rdatacount, size_t expected_sigcount)
+{
+    EXPECT_EQ(expected_name, actual_rrset.getName());
+    EXPECT_EQ(expected_class, actual_rrset.getClass());
+    EXPECT_EQ(expected_type, actual_rrset.getType());
+    EXPECT_EQ(expected_rdatacount, actual_rrset.getRdataCount());
+    EXPECT_EQ(expected_sigcount, actual_rrset.getRRsigDataCount());
+}
+
+TEST_F(TreeNodeRRsetTest, create) {
+    // Constructed with RRSIG, and it should be visible.
+    checkBasicFields(TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true),
+                     www_name_, rrclass_, RRType::A(), 2, 1);
+    // Constructed with RRSIG, and it should be invisible.
+    checkBasicFields(TreeNodeRRset(rrclass_, www_node_, a_rdataset_, false),
+                     www_name_, rrclass_, RRType::A(), 2, 0);
+    // Constructed without RRSIG, and it would be visible (but of course won't)
+    checkBasicFields(TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_, true),
+                     origin_name_, rrclass_, RRType::NS(), 1, 0);
+    // Constructed without RRSIG, and it should be visible
+    checkBasicFields(TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_,
+                                   false),
+                     origin_name_, rrclass_, RRType::NS(), 1, 0);
+    // RRSIG-only case (note the RRset's type is covered type)
+    checkBasicFields(TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+                                   true),
+                     www_name_, rrclass_, RRType::TXT(), 0, 1);
+    // RRSIG-only case (note the RRset's type is covered type), but it's
+    // invisible
+    checkBasicFields(TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+                                   false),
+                     www_name_, rrclass_, RRType::TXT(), 0, 0);
+    // Wildcard substitution
+    checkBasicFields(TreeNodeRRset(match_name_, rrclass_,
+                                   wildcard_node_, wildcard_rdataset_,
+                                   true),
+                     match_name_, rrclass_, RRType::A(), 2, 1);
+}
+
+// The following two templated functions are helper to encapsulate the
+// concept truncation and handle MessageRenderer and OutputBuffer transparently
+// in templated test cases.
+template <typename OutputType>
+void
+setOutputLengthLimit(OutputType& output, size_t len_limit) {
+    output.setLengthLimit(len_limit);
+}
+template <>
+void
+setOutputLengthLimit<OutputBuffer>(OutputBuffer&, size_t) {
+}
+
+template <typename OutputType>
+bool
+isOutputTruncated(OutputType& output) {
+    return (output.isTruncated());
+}
+template <>
+bool
+isOutputTruncated<OutputBuffer>(OutputBuffer&) {
+    return (false);
+}
+
+// Templated so we so can support OutputBuffer version of toWire().
+// We use the above helper templated functions for some renderer only methods.
+// We test two sets of cases: normal rendering case and case when truncation
+// is expected.  The latter is effectively for MessageRenderer only.
+// If len_limit == 0, we consider it the normal case; otherwise it's for
+// truncation.  prepended_name isn't used for the truncation case.
+template <typename OutputType>
+void
+checkToWireResult(OutputType& expected_output, OutputType& actual_output,
+                  const AbstractRRset& actual_rrset,
+                  const Name& prepended_name,
+                  ConstRRsetPtr rrset, ConstRRsetPtr rrsig_rrset,
+                  bool dnssec_ok,
+                  size_t len_limit = 0,
+                  size_t expected_result = 0)
+{
+    expected_output.clear();
+    actual_output.clear();
+
+    if (len_limit == 0) {       // normal rendering
+        // Prepare "actual" rendered data.  We prepend a name to confirm the
+        // owner name should be compressed in both cases.
+        prepended_name.toWire(actual_output);
+        const size_t rdata_count = rrset ? rrset->getRdataCount() : 0;
+        const int expected_ret = (dnssec_ok && rrsig_rrset) ?
+            rdata_count + rrsig_rrset->getRdataCount() : rdata_count;
+        EXPECT_EQ(expected_ret, actual_rrset.toWire(actual_output));
+    } else {                    // truncation
+        setOutputLengthLimit(actual_output, len_limit);
+        EXPECT_EQ(expected_result, actual_rrset.toWire(actual_output));
+        EXPECT_TRUE(isOutputTruncated(actual_output)); // always true here
+    }
+
+    // Prepare "expected" data.
+    if (len_limit == 0) {       // normal rendering
+        prepended_name.toWire(expected_output);
+    } else {                    // truncation
+        setOutputLengthLimit(expected_output, len_limit);
+    }
+    if (rrset) {
+        rrset->toWire(expected_output);
+    }
+    if (!isOutputTruncated(expected_output) && dnssec_ok && rrsig_rrset) {
+        rrsig_rrset->toWire(expected_output);
+    }
+
+    // Compare the two.
+    matchWireData(expected_output.getData(), expected_output.getLength(),
+                  actual_output.getData(), actual_output.getLength());
+}
+
+TEST_F(TreeNodeRRsetTest, toWire) {
+    MessageRenderer expected_renderer, actual_renderer;
+    OutputBuffer expected_buffer(0), actual_buffer(0);
+
+    {
+        SCOPED_TRACE("with RRSIG, DNSSEC OK");
+        const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
+        checkToWireResult(expected_renderer, actual_renderer, rrset,
+                          www_name_, a_rrset_, a_rrsig_rrset_, true);
+        // Currently the buffer version throws
+        EXPECT_THROW(
+            checkToWireResult(expected_buffer, actual_buffer, rrset,
+                              www_name_, a_rrset_, a_rrsig_rrset_, true),
+            isc::Unexpected);
+    }
+
+    {
+        SCOPED_TRACE("with RRSIG, DNSSEC not OK");
+        const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, false);
+        checkToWireResult(expected_renderer, actual_renderer, rrset,
+                          www_name_, a_rrset_, a_rrsig_rrset_, false);
+    }
+
+    {
+        SCOPED_TRACE("without RRSIG, DNSSEC OK");
+        const TreeNodeRRset rrset(rrclass_, origin_node_, ns_rdataset_, true);
+        checkToWireResult(expected_renderer, actual_renderer, rrset,
+                          origin_name_, ns_rrset_, ConstRRsetPtr(), true);
+    }
+
+    {
+        SCOPED_TRACE("without RRSIG, DNSSEC not OK");
+        const TreeNodeRRset rrset(rrclass_, origin_node_, ns_rdataset_,
+                                  false);
+        checkToWireResult(expected_renderer, actual_renderer, rrset,
+                          origin_name_, ns_rrset_, ConstRRsetPtr(), false);
+    }
+
+    {
+        // RDATA of DNAME DR shouldn't be compressed.  Prepending "example.org"
+        // will check that.
+        SCOPED_TRACE("uncompressed RDATA");
+        const TreeNodeRRset rrset(rrclass_, origin_node_, dname_rdataset_,
+                                  false);
+        checkToWireResult(expected_renderer, actual_renderer, rrset,
+                          Name("example.org"), dname_rrset_, ConstRRsetPtr(),
+                          false);
+    }
+
+    {
+        SCOPED_TRACE("wildcard with RRSIG");
+        checkToWireResult(expected_renderer, actual_renderer,
+                          TreeNodeRRset(match_name_, rrclass_, wildcard_node_,
+                                        wildcard_rdataset_, true),
+                          origin_name_, wildmatch_rrset_,
+                          wildmatch_rrsig_rrset_, true);
+    }
+
+    {
+        SCOPED_TRACE("wildcard without RRSIG");
+        checkToWireResult(expected_renderer, actual_renderer,
+                          TreeNodeRRset(match_name_, rrclass_, wildcard_node_,
+                                        wildcard_rdataset_, false),
+                          origin_name_, wildmatch_rrset_,
+                          wildmatch_rrsig_rrset_, false);
+    }
+
+    {
+        // Very unusual case: the set only contains RRSIG (already rare)
+        // and it's requested to be dumped to wire (can only happen in
+        // ANY or type-RRSIG queries, which are rare also).  But can still
+        // happen.
+        SCOPED_TRACE("RRSIG only, DNSSEC OK");
+        const TreeNodeRRset rrset(rrclass_, www_node_, rrsig_only_rdataset_,
+                                  true);
+        checkToWireResult(expected_renderer, actual_renderer, rrset,
+                          www_name_, ConstRRsetPtr(), txt_rrsig_rrset_,true);
+    }
+
+    {
+        // Similar to the previous case, but DNSSEC records aren't requested.
+        // In practice this case wouldn't happen, but API-wise possible, so
+        // we test it explicitly.
+        SCOPED_TRACE("RRSIG only, DNSSEC not OK");
+        const TreeNodeRRset rrset(rrclass_, www_node_, rrsig_only_rdataset_,
+                                  false);
+        checkToWireResult(expected_renderer, actual_renderer, rrset,
+                          www_name_, ConstRRsetPtr(), txt_rrsig_rrset_,false);
+    }
+}
+
+TEST_F(TreeNodeRRsetTest, toWireTruncated) {
+    MessageRenderer expected_renderer, actual_renderer;
+    // dummy parameter to checkToWireResult (unused for the this test case)
+    const Name& name = Name::ROOT_NAME();
+
+    // Set the truncation limit to name len + 14 bytes of fixed data for A RR
+    // (type, class, TTL, rdlen, and 4-byte IPv4 address).  Then we can only
+    // render just one RR, without any garbage trailing data.
+    checkToWireResult(expected_renderer, actual_renderer,
+                      TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true),
+                      name, a_rrset_, a_rrsig_rrset_, true,
+                      www_name_.getLength() + 14,
+                      1);   // 1 main RR, no RRSIG
+
+    // The first main RRs should fit in the renderer (the name will be
+    // fully compressed, so its size is 2 bytes), but the RRSIG doesn't.
+    checkToWireResult(expected_renderer, actual_renderer,
+                      TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true),
+                      name, a_rrset_, a_rrsig_rrset_, true,
+                      www_name_.getLength() + 14 + 2 + 14,
+                      2);   // 2 main RR, no RRSIG
+
+    // This RRset has one main RR and two RRSIGs.  Rendering the second RRSIG
+    // causes truncation.
+    // First, compute the rendered length for the main RR and a single RRSIG.
+    // The length of the RRSIG should be the same if we "accidentally"
+    // rendered the RRSIG for the A RR (which only contains one RRSIG).
+    expected_renderer.clear();
+    aaaa_rrset_->toWire(expected_renderer);
+    a_rrsig_rrset_->toWire(expected_renderer);
+    const size_t limit_len = expected_renderer.getLength();
+    // Then perform the test
+    checkToWireResult(expected_renderer, actual_renderer,
+                      TreeNodeRRset(rrclass_, www_node_, aaaa_rdataset_, true),
+                      name, aaaa_rrset_, aaaa_rrsig_rrset_, true, limit_len,
+                      2);   // 1 main RR, 1 RRSIG
+
+    // RRSIG only case.  Render length limit being 1, so it won't fit,
+    // and will cause truncation.
+    checkToWireResult(expected_renderer, actual_renderer,
+                      TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+                                    true),
+                      name, ConstRRsetPtr(), txt_rrsig_rrset_, true, 1,
+                      0);   // no RR
+}
+
+void
+checkRdataIterator(const vector<string>& expected, RdataIteratorPtr rit) {
+    for (vector<string>::const_iterator it = expected.begin();
+         it != expected.end();
+         ++it)
+    {
+        ASSERT_FALSE(rit->isLast());
+        EXPECT_EQ(*it, rit->getCurrent().toText());
+        rit->next();
+    }
+    // We should have reached the end of RDATA
+    EXPECT_TRUE(rit->isLast());
+
+    // move to the first RDATA again, and check the value.
+    rit->first();
+    if (!expected.empty()) {
+        EXPECT_EQ(expected[0], rit->getCurrent().toText());
+    } else {
+        EXPECT_TRUE(rit->isLast());
+    }
+}
+
+TEST_F(TreeNodeRRsetTest, getRdataIterator) {
+    // This RRset should have 2 A RDATAs
+    vector<string> expected;
+    expected.push_back("192.0.2.1");
+    expected.push_back("192.0.2.2");
+    checkRdataIterator(expected,
+                       TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true).
+                       getRdataIterator());
+
+    // The iterator shouldn't work different with or without RRSIG
+    checkRdataIterator(expected,
+                       TreeNodeRRset(rrclass_, www_node_, a_rdataset_, false).
+                       getRdataIterator());
+
+    // This RRset should have 1 NS RDATA (containing name field)
+    expected.clear();
+    expected.push_back("ns.example.com.");
+    checkRdataIterator(expected,
+                       TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_,
+                                     false).getRdataIterator());
+
+    // RRSIG only.  Iterator will be empty and shouldn't cause any disruption.
+    expected.clear();
+    checkRdataIterator(expected,
+                       TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+                                     true).getRdataIterator());
+}
+
+void
+checkToText(const AbstractRRset& actual_rrset,
+            ConstRRsetPtr expected_rrset, ConstRRsetPtr expected_sig_rrset)
+{
+    const string actual_text = actual_rrset.toText();
+    const string expected_text =
+        (expected_rrset ? expected_rrset->toText() : "") +
+        (expected_sig_rrset ? expected_sig_rrset->toText() : "");
+    EXPECT_EQ(expected_text, actual_text);
+}
+
+TEST_F(TreeNodeRRsetTest, toText) {
+    // Constructed with RRSIG, and it should be visible.
+    checkToText(TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true),
+                a_rrset_, a_rrsig_rrset_);
+    // Constructed with RRSIG, and it should be invisible.
+    checkToText(TreeNodeRRset(rrclass_, www_node_, a_rdataset_, false),
+                a_rrset_, ConstRRsetPtr());
+    // Constructed without RRSIG, and it would be visible (but of course won't)
+    checkToText(TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_, true),
+                ns_rrset_, ConstRRsetPtr());
+    // Constructed without RRSIG, and it should be visible
+    checkToText(TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_, false),
+                ns_rrset_, ConstRRsetPtr());
+    // Wildcard expanded name with RRSIG
+    checkToText(TreeNodeRRset(match_name_, rrclass_, wildcard_node_,
+                              wildcard_rdataset_, true),
+                wildmatch_rrset_, wildmatch_rrsig_rrset_);
+    // Wildcard expanded name without RRSIG
+    checkToText(TreeNodeRRset(match_name_, rrclass_, wildcard_node_,
+                              wildcard_rdataset_, false),
+                wildmatch_rrset_, ConstRRsetPtr());
+    // RRSIG case
+    checkToText(TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+                              true),
+                ConstRRsetPtr(), txt_rrsig_rrset_);
+    // Similar to the previous case, but completely empty.
+    checkToText(TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+                              false),
+                ConstRRsetPtr(), ConstRRsetPtr());
+}
+
+TEST_F(TreeNodeRRsetTest, isSameKind) {
+    const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
+
+    // Same name (node), same type (rdataset) => same kind
+    EXPECT_TRUE(rrset.isSameKind(TreeNodeRRset(rrclass_, www_node_,
+                                               a_rdataset_, true)));
+
+    // Same name (node), different type (rdataset) => not same kind
+    EXPECT_FALSE(rrset.isSameKind(TreeNodeRRset(rrclass_, www_node_,
+                                                aaaa_rdataset_, true)));
+
+    // Different name, different type => not same kind
+    EXPECT_FALSE(rrset.isSameKind(TreeNodeRRset(rrclass_, origin_node_,
+                                                ns_rdataset_, true)));
+
+    // Different name, same type => not same kind.
+    // Note: this shouldn't happen in our in-memory data source implementation,
+    // but API doesn't prohibit it.
+    EXPECT_FALSE(rrset.isSameKind(TreeNodeRRset(rrclass_, origin_node_,
+                                                a_rdataset_, true)));
+
+    // Wildcard and expanded RRset
+    const TreeNodeRRset wildcard_rrset(rrclass_, wildcard_node_,
+                                       wildcard_rdataset_, true);
+    const TreeNodeRRset match_rrset(match_name_, rrclass_, wildcard_node_,
+                                    wildcard_rdataset_, true);
+    EXPECT_FALSE(wildcard_rrset.isSameKind(match_rrset));
+    EXPECT_FALSE(match_rrset.isSameKind(wildcard_rrset));
+
+    // Both are wildcard expanded, and have different names
+    const TreeNodeRRset match2_rrset(Name("match2.example.com"), rrclass_,
+                                     wildcard_node_, wildcard_rdataset_, true);
+    EXPECT_FALSE(match_rrset.isSameKind(match2_rrset));
+    EXPECT_FALSE(match2_rrset.isSameKind(match_rrset));
+
+    // Pathological case.  "badwild" is constructed as if expanded due to
+    // a wildcard, but has the same owner name of the wildcard itself.
+    // Technically, they should be considered of the same kind, but this
+    // implementation considers they are not.  But this case shouldn't happen
+    // as long as the RRsets are only constructed inside the in-memory
+    // zone finder implementation.
+    const TreeNodeRRset badwild_rrset(wildcard_name_, rrclass_, wildcard_node_,
+                                      wildcard_rdataset_, true);
+    EXPECT_FALSE(wildcard_rrset.isSameKind(badwild_rrset));
+    EXPECT_EQ(wildcard_rrset.toText(), badwild_rrset.toText());
+
+    // Pathological case:  Same name, same type, but different class.
+    // This case should be impossible because if the RRsets share the same
+    // tree node, they must belong to the same RR class.  This case is
+    // a caller's bug, and the isSameKind() implementation returns the
+    // "wrong" (= true) answer.
+    EXPECT_TRUE(rrset.isSameKind(TreeNodeRRset(RRClass::CH(), www_node_,
+                                               a_rdataset_, true)));
+
+    // Same kind of different RRset class
+    EXPECT_TRUE(rrset.isSameKind(*a_rrset_));
+
+    // Different kind of different RRset class
+    EXPECT_FALSE(rrset.isSameKind(*aaaa_rrset_));
+}
+
+TEST_F(TreeNodeRRsetTest, unexpectedMethods) {
+    // Note: buffer version of toWire() is checked in the toWire test.
+
+    TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
+
+    EXPECT_THROW(rrset.getTTL(), isc::Unexpected);
+    EXPECT_THROW(rrset.setTTL(RRTTL(0)), isc::Unexpected);
+    EXPECT_THROW(rrset.setName(Name("example")), isc::Unexpected);
+    EXPECT_THROW(rrset.addRdata(createRdata(RRType::A(), rrclass_, "0.0.0.0")),
+                 isc::Unexpected);
+    EXPECT_THROW(rrset.getRRsig(), isc::Unexpected);
+    RdataPtr sig_rdata = createRdata(
+        RRType::RRSIG(), rrclass_,
+        "A 5 2 3600 20120814220826 20120715220826 5300 example.com. FAKE");
+    EXPECT_THROW(rrset.addRRsig(sig_rdata), isc::Unexpected);
+    EXPECT_THROW(rrset.addRRsig(*a_rrsig_rrset_), isc::Unexpected);
+    EXPECT_THROW(rrset.addRRsig(a_rrsig_rrset_), isc::Unexpected);
+    EXPECT_THROW(rrset.addRRsig(RRsetPtr()), isc::Unexpected);
+    EXPECT_THROW(rrset.removeRRsig(), isc::Unexpected);
+}
+}

+ 356 - 0
src/lib/datasrc/memory/treenode_rrset.cc

@@ -0,0 +1,356 @@
+// 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 <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <dns/rdata.h>
+#include <dns/rrset.h>
+
+#include "treenode_rrset.h"
+#include "rdata_serialization.h"
+
+#include <boost/bind.hpp>
+
+#include <cassert>
+#include <string>
+#include <vector>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+const Name&
+TreeNodeRRset::getName() const {
+    if (realname_ != NULL) {
+        return (*realname_);
+    }
+    if (name_ == NULL) {
+        uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+        const LabelSequence name_labels = getOwnerLabels(labels_buf);
+        size_t data_len;
+        const uint8_t* data = name_labels.getData(&data_len);
+        util::InputBuffer buffer(data, data_len);
+        name_ = new Name(buffer);
+    }
+    return (*name_);
+}
+
+const RRTTL&
+TreeNodeRRset::getTTL() const {
+    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::setName(const Name&) {
+    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::setTTL(const RRTTL&) {
+    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+std::string
+TreeNodeRRset::toText() const {
+    // Create TTL from internal data
+    util::InputBuffer ttl_buffer(rdataset_->getTTLData(), sizeof(uint32_t));
+    const RRTTL ttl(ttl_buffer);
+
+    // Dump the main RRset, if not empty
+    std::string ret;
+    RRsetPtr tmp_rrset;
+    for (RdataIteratorPtr rit = getRdataIterator();
+         !rit->isLast();
+         rit->next())
+    {
+        if (!tmp_rrset) {
+            tmp_rrset = RRsetPtr(new RRset(getName(), rrclass_, getType(),
+                                           ttl));
+        }
+        tmp_rrset->addRdata(rit->getCurrent());
+    }
+    if (tmp_rrset) {
+        ret = tmp_rrset->toText();
+    }
+
+    // Dump any RRSIGs
+    tmp_rrset.reset();
+    for (RdataIteratorPtr rit = getSigRdataIterator();
+         !rit->isLast();
+         rit->next())
+    {
+        if (!tmp_rrset) {
+            tmp_rrset = RRsetPtr(new RRset(getName(), rrclass_,
+                                           RRType::RRSIG(), ttl));
+        }
+        tmp_rrset->addRdata(rit->getCurrent());
+    }
+    if (tmp_rrset) {
+        ret += tmp_rrset->toText();
+    }
+
+    return (ret);
+}
+
+namespace {
+void
+renderName(const LabelSequence& name_labels, RdataNameAttributes attr,
+           AbstractMessageRenderer* renderer)
+{
+    renderer->writeName(name_labels, (attr & NAMEATTR_COMPRESSIBLE) != 0);
+}
+
+void
+renderData(const void* data, size_t data_len,
+           AbstractMessageRenderer* renderer)
+{
+    renderer->writeData(data, data_len);
+}
+
+// Common code logic for rendering a single (either main or RRSIG) RRset.
+size_t
+writeRRs(AbstractMessageRenderer& renderer, size_t rr_count,
+         const LabelSequence& name_labels, const RRType& rrtype,
+         const RRClass& rrclass, const void* ttl_data,
+         RdataReader& reader, bool (RdataReader::* rdata_iterate_fn)())
+{
+    for (size_t i = 0; i < rr_count; ++i) {
+        const size_t pos0 = renderer.getLength();
+
+        // Name, type, class, TTL
+        renderer.writeName(name_labels, true);
+        rrtype.toWire(renderer);
+        rrclass.toWire(renderer);
+        renderer.writeData(ttl_data, sizeof(uint32_t));
+
+        // RDLEN and RDATA
+        const size_t pos = renderer.getLength();
+        renderer.skip(sizeof(uint16_t)); // leave the space for RDLENGTH
+        const bool rendered = (reader.*rdata_iterate_fn)();
+        assert(rendered == true);
+        renderer.writeUint16At(renderer.getLength() - pos - sizeof(uint16_t),
+                               pos);
+
+        // Check if truncation would happen
+        if (renderer.getLength() > renderer.getLengthLimit()) {
+            renderer.trim(renderer.getLength() - pos0);
+            renderer.setTruncated();
+            return (i);
+        }
+    }
+    return (rr_count);
+}
+}
+
+unsigned int
+TreeNodeRRset::toWire(AbstractMessageRenderer& renderer) const {
+    RdataReader reader(rrclass_, rdataset_->type, rdataset_->getDataBuf(),
+                       rdataset_->getRdataCount(), rrsig_count_,
+                       boost::bind(renderName, _1, _2, &renderer),
+                       boost::bind(renderData, _1, _2, &renderer));
+
+    // Get the owner name of the RRset in the form of LabelSequence.
+    uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+    const LabelSequence name_labels = getOwnerLabels(labels_buf);
+
+    // Render the main (non RRSIG) RRs
+    const size_t rendered_rdata_count =
+        writeRRs(renderer, rdataset_->getRdataCount(), name_labels,
+                 rdataset_->type, rrclass_, rdataset_->getTTLData(), reader,
+                 &RdataReader::iterateRdata);
+    if (renderer.isTruncated()) {
+        return (rendered_rdata_count);
+    }
+    const bool rendered = reader.iterateRdata();
+    assert(rendered == false); // we should've reached the end
+
+    // Render any RRSIGs, if we supposed to do so
+    const size_t rendered_rrsig_count = dnssec_ok_ ?
+        writeRRs(renderer, rrsig_count_, name_labels, RRType::RRSIG(),
+                 rrclass_, rdataset_->getTTLData(), reader,
+                 &RdataReader::iterateSingleSig) : 0;
+
+    return (rendered_rdata_count + rendered_rrsig_count);
+}
+
+unsigned int
+TreeNodeRRset::toWire(isc::util::OutputBuffer&) const {
+    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::addRdata(rdata::ConstRdataPtr) {
+    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::addRdata(const rdata::Rdata&) {
+    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+namespace {
+// In this namespace we define a set of helper stuff to implement the
+// RdataIterator for the TreeNodeRRset.  We should eventually optimize
+// the code so that performance sensitive path won't require the iterator,
+// so, at the moment, the implementation is straightforward, but less
+// efficient one: It builds a vector of Rdata objects on construction,
+// and its getCurrent() returns the stored data.
+
+class TreeNodeRdataIterator : public RdataIterator {
+public:
+    TreeNodeRdataIterator(const std::vector<ConstRdataPtr>& rdata_list) :
+        rdata_list_(rdata_list), rdata_it_(rdata_list_.begin())
+    {}
+    virtual void first() { rdata_it_ = rdata_list_.begin(); }
+    virtual void next() {
+        ++rdata_it_;
+    }
+    virtual const rdata::Rdata& getCurrent() const {
+        return (**rdata_it_);
+    }
+    virtual bool isLast() const { return (rdata_it_ == rdata_list_.end()); }
+private:
+    const std::vector<ConstRdataPtr> rdata_list_;
+    std::vector<ConstRdataPtr>::const_iterator rdata_it_;
+};
+
+void
+renderNameToBuffer(const LabelSequence& name_labels, RdataNameAttributes,
+                   util::OutputBuffer* buffer)
+{
+    size_t data_len;
+    const uint8_t *data = name_labels.getData(&data_len);
+    buffer->writeData(data, data_len);
+}
+
+void
+renderDataToBuffer(const void* data, size_t data_len,
+                   util::OutputBuffer* buffer)
+{
+    buffer->writeData(data, data_len);
+}
+}
+
+RdataIteratorPtr
+TreeNodeRRset::getRdataIteratorInternal(bool is_rrsig, size_t count) const {
+    util::OutputBuffer buffer(0);
+    RdataReader reader(rrclass_, rdataset_->type, rdataset_->getDataBuf(),
+                       rdataset_->getRdataCount(), rrsig_count_,
+                       boost::bind(renderNameToBuffer, _1, _2, &buffer),
+                       boost::bind(renderDataToBuffer, _1, _2, &buffer));
+
+    std::vector<ConstRdataPtr> rdata_list;
+    for (size_t i = 0; i < count; ++i) {
+        buffer.clear();
+        const bool rendered = is_rrsig ? reader.iterateSingleSig() :
+            reader.iterateRdata();
+        assert(rendered == true);
+        util::InputBuffer ib(buffer.getData(), buffer.getLength());
+        rdata_list.push_back(
+            createRdata(is_rrsig ? RRType::RRSIG() : rdataset_->type, rrclass_,
+                        ib, ib.getLength()));
+    }
+    return (RdataIteratorPtr(new TreeNodeRdataIterator(rdata_list)));
+}
+
+RdataIteratorPtr
+TreeNodeRRset::getRdataIterator() const {
+    return (getRdataIteratorInternal(false, rdataset_->getRdataCount()));
+}
+
+RdataIteratorPtr
+TreeNodeRRset::getSigRdataIterator() const {
+    return (getRdataIteratorInternal(true, dnssec_ok_ ? rrsig_count_ : 0));
+}
+
+RRsetPtr
+TreeNodeRRset::getRRsig() const {
+    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::addRRsig(const rdata::ConstRdataPtr&) {
+    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::addRRsig(const rdata::RdataPtr&) {
+    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::addRRsig(const AbstractRRset&) {
+    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::addRRsig(const ConstRRsetPtr&) {
+    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::addRRsig(const RRsetPtr&) {
+    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::removeRRsig() {
+    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+bool
+TreeNodeRRset::isSameKind(const AbstractRRset& abs_other) const {
+    const TreeNodeRRset* other =
+        dynamic_cast<const TreeNodeRRset*>(&abs_other);
+    if (other != NULL) {
+        // If type is different, they are not the same kind
+        if (rdataset_ != other->rdataset_) {
+            return (false);
+        }
+        // Same for the owner name.  Comparing the nodes also detect
+        // the case where RR classes are different (see the method description
+        // of the header for details).
+        if (node_ != other->node_ ) {
+            return (false);
+        }
+        // If one is constructed with a "real name" and the other isn't
+        // *we consider* them different.
+        if ((realname_ == NULL && other->realname_ != NULL) ||
+            (realname_ != NULL && other->realname_ == NULL)) {
+            return (false);
+        }
+        // If both are constructed with a "real name", we compare their names
+        // explicitly.
+        if (realname_ != NULL && other->realname_ != NULL &&
+            realname_->nequals(*other->realname_)) {
+            return (false);
+        }
+        return (true);
+    }
+    return (AbstractRRset::isSameKind(abs_other));
+}
+
+} // namespace memory
+} // namespace datasrc
+} // datasrc isc

+ 270 - 0
src/lib/datasrc/memory/treenode_rrset.h

@@ -0,0 +1,270 @@
+// 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_TREENODE_RRSET_H
+#define DATASRC_MEMORY_TREENODE_RRSET_H 1
+
+#include <util/buffer.h>
+
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <dns/rdata.h>
+#include <dns/rrset.h>
+
+#include <datasrc/memory/zone_data.h>
+#include <datasrc/memory/rdataset.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <string>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+/// \brief Special RRset for optimizing memory datasource requirement
+///
+/// This is a derived class of \c dns::AbstractRRset intended to be used
+/// by the in-memory data source finder implementation.  It is designed
+/// so performance sensitive operations will be lightweight; for example,
+/// (in the general case) the construction is just set up references to
+/// pre-loaded in-memory objects, not involving any dynamic memory allocation.
+/// Its \c toWire() method is also customized so it won't have to use
+/// the generic but expensive \c dns::RdataIterator.
+///
+/// On the other hand, some other performance-insensitive methods could be
+/// even less efficient than the generic version.  Those include \c getName(),
+/// \c toText(), and \c getRdataIterator() methods.
+///
+/// \note Right now, the authoritative server's query processing still needs
+/// to use \c getRdataIterator() and \c getName() for relatively rare case
+/// operations.  We should revise that part of the authoritative server
+/// implementation in the next phase in order to eliminate the bottleneck.
+///
+/// Since this class is assumed to be instantiated only from the in-memory
+/// zone finder, which only returns immutable (const) \c RRset objects,
+/// we skip implementing non const virtual methods of this class.
+/// Unless the application intentionally breaks the constness or the class
+/// is abused outside of the in-memory data source implementation, this
+/// should be safe because such methods should never be called.
+///
+/// Some other const member methods are still incomplete; if they are called
+/// it will result in an exception.  In the expected usage of this class
+/// it should be safe, but we should eventually provide complete
+/// implementations of these methods.
+///
+/// This class can internally maintain dynamically allocated resource.
+/// It would cause copying a class object complicated while objects of
+/// this class are not expected to be copyable in the usage, so it's
+/// explicitly defined non copyable.
+///
+/// \note This class is exposed in this separate header file so that other
+/// part of the in-memory data source implementation and test code
+/// can refer to its definition, and only for that purpose.  Otherwise this is
+/// essentially a private class of the in-memory data source implementation,
+/// and an application shouldn't directly refer to this class.
+class TreeNodeRRset : boost::noncopyable, public dns::AbstractRRset {
+public:
+    /// \brief Normal case constructor.
+    ///
+    /// This class object is basically defined with a \c ZoneNode and
+    /// \c RdataSet.  The former determines the owner name of the RRset,
+    /// and the latter provides the rest of the RRset parameters.
+    /// Since the RR class is maintained outside of the \c ZoneData,
+    /// it must be explicitly given as a constructor parameter.
+    ///
+    /// The \c RdataSet may or may not be associated with RRSIGs.  It's
+    /// fixed at the load time, but depending on the query context they
+    /// may or may not be requested (and supposed to be visible to the
+    /// caller).  Since \c rdataset cannot be modified at the time of
+    /// construction, a separate parameter (\c dnssec_ok) controls this
+    /// policy.  Any associated RRSIGs are visible if and only if \c dnssec_ok
+    /// is true.  If the RRset is not associated with RRSIGs, the value
+    /// does not have any effect.
+    ///
+    /// In some rare cases \c rdataset may only consist of RRSIGs (when
+    /// the zone contains an RRSIG that doesn't have covered RRsets).
+    /// This class works for such cases, too.
+    ///
+    /// \throw none
+    ///
+    /// \param rrclass The RR class of the RRset.  This must be consistent
+    /// with the corresponding zone class.
+    /// \param node The \c ZoneNode for the \c RRset.  Must not be NULL.
+    /// \param rdataset The \c RdataSet for the \c RRset.  Must not be NULL.
+    /// \param dnssec_ok Whether the RRSIGs for the RRset (if associated)
+    /// should be visible to the caller.
+    TreeNodeRRset(const dns::RRClass& rrclass, const ZoneNode* node,
+                  const RdataSet* rdataset, bool dnssec_ok) :
+        node_(node), rdataset_(rdataset),
+        rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass),
+        dnssec_ok_(dnssec_ok), name_(NULL), realname_(NULL)
+    {}
+
+    /// \brief Constructor for wildcard-expanded owner name.
+    ///
+    /// This constructor is mostly the same as the other version, but takes
+    /// an extra parameter, \c realname.  It effectively overrides the owner
+    /// name of the RRset; wherever the owner name is used (e.g., in the
+    /// \c toWire() method), the specified name will be used instead of
+    /// the name associated with \c node.
+    ///
+    /// The expected usage is \c node has a wildcard name (such as
+    /// *.example.com), but this constructor does not enforce the assumption.
+    ///
+    /// \throw std::bad_alloc Memory allocation fails
+    TreeNodeRRset(const dns::Name& realname, const dns::RRClass& rrclass,
+                  const ZoneNode* node, const RdataSet* rdataset,
+                  bool dnssec_ok) :
+        node_(node), rdataset_(rdataset),
+        rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass),
+        dnssec_ok_(dnssec_ok), name_(NULL), realname_(new dns::Name(realname))
+    {}
+
+    virtual ~TreeNodeRRset() {
+        delete realname_;
+        delete name_;
+    }
+
+    virtual unsigned int getRdataCount() const {
+        return (rdataset_->getRdataCount());
+    }
+
+    virtual const dns::Name& getName() const;
+    virtual const dns::RRClass& getClass() const {
+        return (rrclass_);
+    }
+
+    virtual const dns::RRType& getType() const {
+        return (rdataset_->type);
+    }
+
+    /// \brief Specialized version of \c getTTL() for \c TreeNodeRRset.
+    ///
+    /// It throws \c isc::Unexpected unconditionally.
+    virtual const dns::RRTTL& getTTL() const;
+
+    /// \brief Specialized version of \c setName() for \c TreeNodeRRset.
+    ///
+    /// It throws \c isc::Unexpected unconditionally.
+    virtual void setName(const dns::Name& name);
+
+    /// \brief Specialized version of \c setName() for \c TreeNodeRRset.
+    ///
+    /// It throws \c isc::Unexpected unconditionally.
+    virtual void setTTL(const dns::RRTTL& ttl);
+
+    virtual std::string toText() const;
+
+    virtual unsigned int toWire(dns::AbstractMessageRenderer& renderer) const;
+
+    /// \brief Specialized version of \c toWire(buffer) for \c TreeNodeRRset.
+    ///
+    /// It throws \c isc::Unexpected unconditionally.
+    virtual unsigned int toWire(util::OutputBuffer& buffer) const;
+
+    /// \brief Specialized version of \c addRdata() for \c TreeNodeRRset.
+    ///
+    /// It throws \c isc::Unexpected unconditionally.
+    virtual void addRdata(dns::rdata::ConstRdataPtr rdata);
+
+    /// \brief Specialized version of \c addRdata() for \c TreeNodeRRset.
+    ///
+    /// It throws \c isc::Unexpected unconditionally.
+    virtual void addRdata(const dns::rdata::Rdata& rdata);
+
+    virtual dns::RdataIteratorPtr getRdataIterator() const;
+
+    /// \brief Specialized version of \c getRRsig() for \c TreeNodeRRset.
+    ///
+    /// It throws \c isc::Unexpected unconditionally.
+    virtual dns::RRsetPtr getRRsig() const;
+
+    virtual unsigned int getRRsigDataCount() const {
+        return (dnssec_ok_ ? rrsig_count_ : 0);
+    }
+
+    ///
+    /// \name Specialized version of RRsig related methods for
+    /// \c TreeNodeRRset.
+    ///
+    /// These throw \c isc::Unexpected unconditionally.
+    ////
+    //@{
+    virtual void addRRsig(const dns::rdata::ConstRdataPtr& rdata);
+    virtual void addRRsig(const dns::rdata::RdataPtr& rdata);
+    virtual void addRRsig(const dns::AbstractRRset& sigs);
+    virtual void addRRsig(const dns::ConstRRsetPtr& sigs);
+    virtual void addRRsig(const dns::RRsetPtr& sigs);
+    virtual void removeRRsig();
+    //@}
+
+    /// \brief Specialized version of \c isSameKind() for \c TreeNodeRRset.
+    ///
+    /// As a kind of optimization, this implementation exploits the assumption
+    /// of how \c TreeNodeRRset objects are created: They must be always
+    /// created inside the in-memory data source finder implementation,
+    /// and they are constructed with the \c realname parameter if and only
+    /// if the corresponding query name is subject to wildcard substitution.
+    ///
+    /// So, if the given RRset is of \c TreeNodeRRset, and one and only one of
+    /// of them has \c realname, they are considered to have different names.
+    ///
+    /// Also, this implementation does not compare RR classes explicitly;
+    /// if two \c TreeNodeRRset objects belong to different RR classes,
+    /// they should belong to different zone trees (according to the assumption
+    /// of how the zone data are built), and therefore they cannot be at
+    /// same zone node.  So it's sufficient to compare the (address of the)
+    /// node; if they are different they cannot be of the same kind.
+    virtual bool isSameKind(const dns::AbstractRRset& abs_other) const;
+
+private:
+    dns::RdataIteratorPtr getSigRdataIterator() const;
+
+    // Common backend for getRdataIterator() and getSigRdataIterator()
+    dns::RdataIteratorPtr getRdataIteratorInternal(bool is_rrsig,
+                                                   size_t count) const;
+
+    // Return \c LabelSequence for the owner name regardless of how this
+    /// class is constructed (with or without 'realname')
+    dns::LabelSequence getOwnerLabels(
+        uint8_t labels_buf[dns::LabelSequence::MAX_SERIALIZED_LENGTH]) const
+    {
+        if (realname_ != NULL) {
+            return (dns::LabelSequence(*realname_));
+        }
+        return (node_->getAbsoluteLabels(labels_buf));
+    }
+
+    const ZoneNode* node_;
+    const RdataSet* rdataset_;
+    const size_t rrsig_count_;
+    const dns::RRClass rrclass_;
+    const bool dnssec_ok_;
+    mutable dns::Name* name_;
+    const dns::Name* const realname_;
+};
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_TREENODE_RRSET_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 6 - 0
src/lib/dns/benchmarks/message_renderer_bench.cc

@@ -15,6 +15,7 @@
 #include <bench/benchmark.h>
 
 #include <dns/name.h>
+#include <dns/labelsequence.h>
 #include <dns/messagerenderer.h>
 #include <oldmessagerenderer.h>
 
@@ -115,6 +116,11 @@ public:
     virtual void writeName(const Name& name, const bool = false) {
         name.toWire(getBuffer());
     }
+    virtual void writeName(const LabelSequence&, const bool) {
+        // We shouldn't use this version of writeName (and we internally
+        // control it, so we simply assert it here)
+        assert(false);
+    }
 };
 
 void

+ 8 - 0
src/lib/dns/benchmarks/oldmessagerenderer.cc

@@ -15,6 +15,7 @@
 #include <exceptions/exceptions.h>
 #include <util/buffer.h>
 #include <dns/name.h>
+#include <dns/labelsequence.h>
 #include <oldmessagerenderer.h>
 
 #include <cctype>
@@ -274,5 +275,12 @@ OldMessageRenderer::writeName(const Name& name, const bool compress) {
     }
 }
 
+void
+OldMessageRenderer::writeName(const LabelSequence&, const bool) {
+    // We shouldn't use this version of writeName (and we internally
+    // control it, so we simply assert it here)
+    assert(false);
+}
+
 }
 }

+ 1 - 0
src/lib/dns/benchmarks/oldmessagerenderer.h

@@ -42,6 +42,7 @@ public:
     virtual void setCompressMode(CompressMode mode);
     virtual void clear();
     virtual void writeName(const Name& name, bool compress = true);
+    virtual void writeName(const LabelSequence& labels, bool compress);
 private:
     struct MessageRendererImpl;
     MessageRendererImpl* impl_;

+ 15 - 17
src/lib/dns/messagerenderer.h

@@ -332,8 +332,21 @@ public:
     /// for compressing subsequent names.
     ///
     /// \param name A \c Name object to be written.
-    /// \param compress A boolean indicating whether to enable name compression.
+    /// \param compress A boolean indicating whether to enable name
+    /// compression.
     virtual void writeName(const Name& name, bool compress = true) = 0;
+
+    /// \brief Write a \c LabelSequence object into the internal buffer
+    /// in wire format, with or without name compression.
+    ///
+    /// This is the same as the other version, which takes \c Name instead
+    /// of \c LabelSequence, except for the parameter type.  The passed
+    /// \c LabelSequence must be absolute.
+    ///
+    /// \param ls A \c LabelSequence object to be written.
+    /// \param compress A boolean indicating whether to enable name
+    /// compression.
+    virtual void writeName(const LabelSequence& ls, bool compress = true) = 0;
     //@}
 };
 
@@ -376,22 +389,7 @@ public:
 
     virtual void clear();
     virtual void writeName(const Name& name, bool compress = true);
-
-    /// \brief Write a \c LabelSequence object into the internal buffer
-    /// in wire format, with or without name compression.
-    ///
-    /// If the optional parameter \c compress is \c true, this method tries to
-    /// compress the \c ls if possible, searching the entire message that has
-    /// been rendered.  Otherwise name compression is omitted.  Its default
-    /// value is \c true.
-    ///
-    /// Note: even if \c compress is \c true, the position of the \c ls (and
-    /// possibly its ancestor names) in the message is recorded and may be used
-    /// for compressing subsequent names.
-    ///
-    /// \param ls A \c LabelSequence object to be written.
-    /// \param compress A boolean indicating whether to enable name compression.
-    void writeName(const LabelSequence& ls, bool compress = true);
+    virtual void writeName(const LabelSequence& ls, bool compress = true);
 
 private:
     struct MessageRendererImpl;

+ 1 - 0
src/lib/dns/rdatafields.cc

@@ -81,6 +81,7 @@ public:
     virtual void setTruncated() { truncated_ = true; }
     virtual void setLengthLimit(size_t len) { length_limit_ = len; }
     virtual void setCompressMode(CompressMode mode) { mode_ = mode; }
+    virtual void writeName(const LabelSequence&, bool) {}
     virtual void writeName(const Name& name, bool compress) {
         extendData();
         const RdataFields::Type field_type =