Browse Source

Merge branch 'master' of ssh://bind10.isc.org/var/bind10/git/bind10

chenzhengzhang 14 years ago
parent
commit
457ef26144
56 changed files with 1588 additions and 181 deletions
  1. 1 0
      configure.ac
  2. 1 1
      src/bin/xfrin/tests/Makefile.am
  3. 1 1
      src/bin/xfrout/tests/Makefile.am
  4. 3 3
      src/cppcheck-suppress.lst
  5. 2 1
      src/lib/dns/Makefile.am
  6. 17 0
      src/lib/dns/benchmarks/Makefile.am
  7. 10 0
      src/lib/dns/benchmarks/README
  8. 32 0
      src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_com
  9. 10 0
      src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_nxdomain
  10. 22 0
      src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_org
  11. 188 0
      src/lib/dns/benchmarks/rdatarender_bench.cc
  12. 2 2
      src/lib/dns/gen-rdatacode.py.in
  13. 16 56
      src/lib/dns/messagerenderer.cc
  14. 148 78
      src/lib/dns/messagerenderer.h
  15. 1 2
      src/lib/dns/name.cc
  16. 2 2
      src/lib/dns/name.h
  17. 1 1
      src/lib/dns/python/tests/Makefile.am
  18. 1 1
      src/lib/dns/rdata.cc
  19. 3 3
      src/lib/dns/rdata.h
  20. 2 2
      src/lib/dns/rdata/any_255/tsig_250.cc
  21. 1 1
      src/lib/dns/rdata/ch_3/a_1.cc
  22. 1 1
      src/lib/dns/rdata/generic/cname_5.cc
  23. 1 1
      src/lib/dns/rdata/generic/dname_39.cc
  24. 1 1
      src/lib/dns/rdata/generic/dnskey_48.cc
  25. 1 1
      src/lib/dns/rdata/generic/ds_43.cc
  26. 1 1
      src/lib/dns/rdata/generic/mx_15.cc
  27. 1 1
      src/lib/dns/rdata/generic/ns_2.cc
  28. 1 1
      src/lib/dns/rdata/generic/nsec3_50.cc
  29. 1 1
      src/lib/dns/rdata/generic/nsec3param_51.cc
  30. 1 1
      src/lib/dns/rdata/generic/nsec_47.cc
  31. 1 1
      src/lib/dns/rdata/generic/opt_41.cc
  32. 1 1
      src/lib/dns/rdata/generic/ptr_12.cc
  33. 1 1
      src/lib/dns/rdata/generic/rp_17.cc
  34. 1 1
      src/lib/dns/rdata/generic/rrsig_46.cc
  35. 1 1
      src/lib/dns/rdata/generic/soa_6.cc
  36. 1 1
      src/lib/dns/rdata/generic/txt_16.cc
  37. 1 1
      src/lib/dns/rdata/hs_4/a_1.cc
  38. 1 1
      src/lib/dns/rdata/in_1/a_1.cc
  39. 1 1
      src/lib/dns/rdata/in_1/aaaa_28.cc
  40. 1 1
      src/lib/dns/rdata/template.cc
  41. 223 0
      src/lib/dns/rdatafields.cc
  42. 427 0
      src/lib/dns/rdatafields.h
  43. 2 2
      src/lib/dns/rrtype-placeholder.h
  44. 1 1
      src/lib/dns/rrtype.cc
  45. 1 0
      src/lib/dns/tests/Makefile.am
  46. 380 0
      src/lib/dns/tests/rdatafields_unittest.cc
  47. 4 0
      src/lib/dns/tests/testdata/Makefile.am
  48. 10 0
      src/lib/dns/tests/testdata/rdatafields1.spec
  49. 11 0
      src/lib/dns/tests/testdata/rdatafields2.spec
  50. 11 0
      src/lib/dns/tests/testdata/rdatafields3.spec
  51. 7 0
      src/lib/dns/tests/testdata/rdatafields4.spec
  52. 12 0
      src/lib/dns/tests/testdata/rdatafields5.spec
  53. 13 0
      src/lib/dns/tests/testdata/rdatafields6.spec
  54. 2 2
      src/lib/nsas/tests/nsas_test.h
  55. 1 1
      src/lib/python/isc/notify/tests/Makefile.am
  56. 0 2
      src/lib/util/unittests/Makefile.am

+ 1 - 0
configure.ac

@@ -682,6 +682,7 @@ AC_CONFIG_FILES([Makefile
                  src/lib/dns/tests/testdata/Makefile
                  src/lib/dns/python/Makefile
                  src/lib/dns/python/tests/Makefile
+                 src/lib/dns/benchmarks/Makefile
                  src/lib/exceptions/Makefile
                  src/lib/exceptions/tests/Makefile
                  src/lib/datasrc/Makefile

+ 1 - 1
src/bin/xfrin/tests/Makefile.am

@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS

+ 1 - 1
src/bin/xfrout/tests/Makefile.am

@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS

+ 3 - 3
src/cppcheck-suppress.lst

@@ -3,13 +3,13 @@
 debug
 missingInclude
 // This is a template, and should be excluded from the check
-unreadVariable:src/lib/dns/rdata/template.cc:59
+unreadVariable:src/lib/dns/rdata/template.cc:60
 // These three trigger warnings due to the incomplete implementation.  This is
 // our problem, but we need to suppress the warnings for now.
 functionConst:src/lib/cache/resolver_cache.h
 functionConst:src/lib/cache/message_cache.h
 functionConst:src/lib/cache/rrset_cache.h
 // Intentional self assignment tests.  Suppress warning about them.
-selfAssignment:src/lib/dns/tests/name_unittest.cc:292
-selfAssignment:src/lib/dns/tests/rdata_unittest.cc:227
+selfAssignment:src/lib/dns/tests/name_unittest.cc:293
+selfAssignment:src/lib/dns/tests/rdata_unittest.cc:228
 selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:104

+ 2 - 1
src/lib/dns/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = . tests python
+SUBDIRS = . tests python benchmarks
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
@@ -72,6 +72,7 @@ libdns___la_SOURCES += name.h name.cc
 libdns___la_SOURCES += opcode.h opcode.cc
 libdns___la_SOURCES += rcode.h rcode.cc
 libdns___la_SOURCES += rdata.h rdata.cc
+libdns___la_SOURCES += rdatafields.h rdatafields.cc
 libdns___la_SOURCES += rrclass.cc
 libdns___la_SOURCES += rrparamregistry.h
 libdns___la_SOURCES += rrset.h rrset.cc

+ 17 - 0
src/lib/dns/benchmarks/Makefile.am

@@ -0,0 +1,17 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+noinst_PROGRAMS = rdatarender_bench
+rdatarender_bench_SOURCES = rdatarender_bench.cc
+
+rdatarender_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
+rdatarender_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+rdatarender_bench_LDADD += $(SQLITE_LIBS)

+ 10 - 0
src/lib/dns/benchmarks/README

@@ -0,0 +1,10 @@
+- rdatarender_bench
+
+  This is a benchmark for RDATA rendering performance comparing the basic
+  Rdata objects and RdataField objects.  It takes a command line argument
+  that specifies an input data file.  Each line of the data file should
+  specify a single RDATA with its RR class and type, e.g.
+  IN A 192.0.2.1
+  IN NS ns.example.com.
+  Lines beginning with '#' and empty lines will be ignored.  Sample input
+  files can be found in benchmarkdata/rdatarender_*.

+ 32 - 0
src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_com

@@ -0,0 +1,32 @@
+# This is sample input data for rdatarender_bench.
+# These are RDATA in the authority and additional sections in a response from
+# a root server for a query domain under COM.
+
+IN	NS	g.gtld-servers.net.
+IN	NS	a.gtld-servers.net.
+IN	NS	k.gtld-servers.net.
+IN	NS	c.gtld-servers.net.
+IN	NS	d.gtld-servers.net.
+IN	NS	m.gtld-servers.net.
+IN	NS	h.gtld-servers.net.
+IN	NS	b.gtld-servers.net.
+IN	NS	j.gtld-servers.net.
+IN	NS	l.gtld-servers.net.
+IN	NS	e.gtld-servers.net.
+IN	NS	f.gtld-servers.net.
+IN	NS	i.gtld-servers.net.
+IN	A	192.5.6.30
+IN	A	192.33.14.30
+IN	A	192.26.92.30
+IN	A	192.31.80.30
+IN	A	192.12.94.30
+IN	A	192.35.51.30
+IN	A	192.42.93.30
+IN	A	192.54.112.30
+IN	A	192.43.172.30
+IN	A	192.48.79.30
+IN	A	192.52.178.30
+IN	A	192.41.162.30
+IN	A	192.55.83.30
+IN	AAAA	2001:503:a83e::2:30
+IN	AAAA	2001:503:231d::2:30

+ 10 - 0
src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_nxdomain

@@ -0,0 +1,10 @@
+# This is sample input data for rdatarender_bench.
+# These are RDATA in the authority section in a response from
+# a root server for a non existent query domain (with DNSSEC).
+
+IN	SOA	a.root-servers.net. nstld.verisign-grs.com. 2010110301 1800 900 604800 86400
+IN	RRSIG	SOA 8 0 86400 20101110000000 20101102230000 40288 . WtvYyX2nIsaqjWqkIG1WHFE5PnJ6eno0KqF6azU/MFJ/t1JpKWQ1P4rA 61rnoq0p252fg7wT4XzEz9UDxmpB5pvF2VApe2w9LvSWxsWIIOg8ue5u e9NAAYdzjd0rsYObQQ6msf7WchyAUbnmrqKvf8/CK6+s1xFihXp5DpYL 6K0=
+IN	NSEC	ac. NS SOA RRSIG NSEC DNSKEY
+IN	RRSIG	NSEC 8 0 86400 20101110000000 20101102230000 40288 . rWfgg4YUDFAjhiUOT+niJy/qbaIbydqoXg5oB/5j//ZjNFy4hqU8DvdM xJr9UybQpEvu7pvmKQ0jRYO98Fw/UTlY5KiKbhVBJ1t8AE93cbU+s5gX d3Q6+wRcFX5MjZyIe+f30llKrYOZHjRyEFALCkLt4XEmr0xsua+ztAFY 65k=
+IN	NSEC	np. NS RRSIG NSEC
+IN	RRSIG	NSEC 8 1 86400 20101110000000 20101102230000 40288 . G32LGynsGA2fyDnesyeCtBCoM3ERMgGS4touDUuoBYW1NrZub76kz5fc z93p8VZfoYWAW7LuC8vJ1jl2sUgBNns4zN4RsfFeopcYjjFnGbGuoZnO NmTU+NKO53Ub7uIcCSeqV+COAaL8XqDfyk1FmVdQvtrBaOW/PWpRahVq 7E8=

+ 22 - 0
src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_org

@@ -0,0 +1,22 @@
+# This is sample input data for rdatarender_bench.
+# These are RDATA in the authority and additional sections in a response from
+# a root server for a query domain under ORG.
+
+IN	NS	b0.org.afilias-nst.org.
+IN	NS	a2.org.afilias-nst.info.
+IN	NS	a0.org.afilias-nst.info.
+IN	NS	c0.org.afilias-nst.info.
+IN	NS	d0.org.afilias-nst.org.
+IN	NS	b2.org.afilias-nst.org.
+IN	A	199.19.56.1
+IN	A	199.249.112.1
+IN	A	199.19.54.1
+IN	A	199.249.120.1
+IN	A	199.19.53.1
+IN	A	199.19.57.1
+IN	AAAA	2001:500:e::1
+IN	AAAA	2001:500:40::1
+IN	AAAA	2001:500:c::1
+IN	AAAA	2001:500:48::1
+IN	AAAA	2001:500:b::1
+IN	AAAA	2001:500:f::1

+ 188 - 0
src/lib/dns/benchmarks/rdatarender_bench.cc

@@ -0,0 +1,188 @@
+// Copyright (C) 2010  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 <iostream>
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include <bench/benchmark.h>
+
+#include <util/buffer.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdatafields.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+using namespace std;
+using namespace isc::bench;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using isc::util::OutputBuffer;
+
+namespace {
+// This templated benchmark class is constructed with a vector of Rdata-like
+// (pointer) objects which should have a "toWire()" method.  In its run(),
+// it calls toWire() for each element of the vector.
+template <typename T>
+class RdataRenderBenchMark {
+public:
+    RdataRenderBenchMark(const vector<T>& dataset) :
+        dataset_(dataset), buffer_(4096), renderer_(buffer_)
+    {}
+    unsigned int run() {
+        typename vector<T>::const_iterator data;
+        typename vector<T>::const_iterator data_end = dataset_.end();
+        for (data = dataset_.begin(); data != data_end; ++data) {
+            renderer_.clear();
+            (*data)->toWire(renderer_);
+        }
+        return (dataset_.size());
+    }
+private:
+    const vector<T>& dataset_;
+    OutputBuffer buffer_;
+    MessageRenderer renderer_;
+};
+
+// This supplemental class emulates an RRset like class that internally
+// uses RdataFields.  On construction it stores RDATA information in the
+// form of RdataFields fields.  Its toWire() method restores the data as
+// an RdataFields object for the rendering.
+class RdataFieldsStore {
+public:
+    RdataFieldsStore(ConstRdataPtr rdata) {
+        const RdataFields fields(*rdata);
+
+        spec_size_ = fields.getFieldSpecDataSize();
+        spec_store_.resize(spec_size_);
+        void* cp_spec = &spec_store_[0];
+        memcpy(cp_spec, fields.getFieldSpecData(), spec_store_.size());
+        spec_ptr_ = cp_spec;
+
+        data_length_ = fields.getDataLength();
+        data_store_.resize(data_length_);
+        void* cp_data = &data_store_[0];
+        memcpy(cp_data, fields.getData(), data_store_.size());
+        // Vector guarantees that the elements are stored in continuous array
+        // in memory, so this is actually correct by the standard
+        data_ptr_ = cp_data;
+    }
+    void toWire(MessageRenderer& renderer) const {
+        RdataFields(spec_ptr_, spec_size_,
+                    data_ptr_, data_length_).toWire(renderer);
+    }
+private:
+    vector<unsigned char> spec_store_;
+    vector<unsigned char> data_store_;
+    const void* spec_ptr_;
+    const void* data_ptr_;
+    unsigned int spec_size_;
+    size_t data_length_;
+};
+
+// We wouldn't necessarily have to use a shared pointer, but it's easier
+// to use pointer-like values to adjust them with the RdataRenderBenchMark
+// template.
+typedef boost::shared_ptr<const RdataFieldsStore> ConstRdataFieldsStorePtr;
+
+void
+readInputFile(const char* const input_file, vector<ConstRdataPtr>& rdata_sets,
+              vector<ConstRdataFieldsStorePtr>& fields_sets)
+{
+    ifstream ifs;
+    ifs.open(input_file, ios_base::in);
+    if ((ifs.rdstate() & istream::failbit) != 0) {
+        cerr << "Failed to read input file: " << input_file << endl;
+        exit (1);
+    }
+    string line;
+    unsigned int linenum = 0;
+    while (getline(ifs, line), !ifs.eof()) {
+        ++linenum;
+        if (ifs.bad() || ifs.fail()) {
+            cerr << "Unexpected input at line " << linenum << endl;
+            exit (1);
+        }
+        if (line.empty() || line[0] == '#') {
+            continue;           // skip comment and blank lines
+        }
+        istringstream iss(line);
+        string rrclass_string, rrtype_string;
+        stringbuf rdatabuf;
+        iss >> rrclass_string >> rrtype_string >> &rdatabuf;
+        if (iss.bad() || iss.fail()) {
+            cerr << "Unexpected input at line " << linenum << endl;
+            exit (1);
+        }
+        ConstRdataPtr rdata = createRdata(RRType(rrtype_string),
+                                          RRClass(rrclass_string),
+                                          rdatabuf.str());
+        rdata_sets.push_back(rdata);
+        fields_sets.push_back(ConstRdataFieldsStorePtr(
+                                  new RdataFieldsStore(rdata)));
+    }
+    ifs.close();
+}
+
+void
+usage() {
+    cerr << "Usage: rdatafields_bench [-n iterations] input_file" << endl;
+    exit (1);
+}
+}
+
+int
+main(int argc, char* argv[]) {
+    int ch;
+    int iteration = 10000;
+    while ((ch = getopt(argc, argv, "n:")) != -1) {
+        switch (ch) {
+        case 'n':
+            iteration = atoi(optarg);
+            break;
+        case '?':
+        default:
+            usage();
+        }
+    }
+    argc -= optind;
+    argv += optind;
+    if (argc < 1) {
+        usage();
+    }
+    const char* const input_file = argv[0];
+
+    vector<ConstRdataPtr> rdata_sets;
+    vector<ConstRdataFieldsStorePtr> fields_sets;
+
+    readInputFile(input_file, rdata_sets, fields_sets);
+
+    cout << "Parameters:" << endl;
+    cout << "  Iterations: " << iteration << endl;
+    cout << "  Input File: " << input_file << endl;
+
+    typedef RdataRenderBenchMark<ConstRdataPtr> RdataBenchMark;
+    cout << "Benchmark for rendering with standard Rdata" << endl;
+    BenchMark<RdataBenchMark>(iteration, RdataBenchMark(rdata_sets));
+
+    typedef RdataRenderBenchMark<ConstRdataFieldsStorePtr> FieldsBenchMark;
+    cout << "Benchmark for rendering with RdataFields" << endl;
+    BenchMark<FieldsBenchMark>(iteration, FieldsBenchMark(fields_sets));
+
+    return (0);
+}

+ 2 - 2
src/lib/dns/gen-rdatacode.py.in

@@ -110,7 +110,7 @@ class OutputBuffer;\n'''
         content += line
         if re.match('// BEGIN_COMMON_DECLARATIONS', line):
             content += '''
-class MessageRenderer;\n\n'''
+class AbstractMessageRenderer;\n\n'''
         if re.match('\s+// BEGIN_COMMON_MEMBERS$', line):
             content += '''
     explicit ''' + type_utxt + '''(const std::string& type_str);
@@ -118,7 +118,7 @@ class MessageRenderer;\n\n'''
     ''' + type_utxt + '''(const ''' + type_utxt + '''& other);
     virtual std::string toText() const;
     virtual void toWire(isc::util::OutputBuffer& buffer) const;
-    virtual void toWire(MessageRenderer& renderer) const;
+    virtual void toWire(AbstractMessageRenderer& renderer) const;
     virtual int compare(const Rdata& other) const;\n\n'''
     rdata_header.close()
     return content

+ 16 - 56
src/lib/dns/messagerenderer.cc

@@ -152,12 +152,10 @@ struct MessageRenderer::MessageRendererImpl {
     ///
     /// \param buffer An \c OutputBuffer object to which wire format data is
     /// written.
-    MessageRendererImpl(OutputBuffer& buffer) :
-        buffer_(buffer), nbuffer_(Name::MAX_WIRE), msglength_limit_(512),
+    MessageRendererImpl() :
+        nbuffer_(Name::MAX_WIRE), msglength_limit_(512),
         truncated_(false), compress_mode_(MessageRenderer::CASE_INSENSITIVE)
     {}
-    /// The buffer that holds the entire DNS message.
-    OutputBuffer& buffer_;
     /// A local working buffer to convert each given name into wire format.
     /// This could be a local variable of the \c writeName() method, but
     /// we keep it in the class so that we can reuse it and avoid construction
@@ -176,7 +174,8 @@ struct MessageRenderer::MessageRendererImpl {
 };
 
 MessageRenderer::MessageRenderer(OutputBuffer& buffer) :
-    impl_(new MessageRendererImpl(buffer))
+    AbstractMessageRenderer(buffer),
+    impl_(new MessageRendererImpl)
 {}
 
 MessageRenderer::~MessageRenderer() {
@@ -184,18 +183,8 @@ MessageRenderer::~MessageRenderer() {
 }
 
 void
-MessageRenderer::skip(const size_t len) {
-    impl_->buffer_.skip(len);
-}
-
-void
-MessageRenderer::trim(const size_t len) {
-    impl_->buffer_.trim(len);
-}
-
-void
 MessageRenderer::clear() {
-    impl_->buffer_.clear();
+    AbstractMessageRenderer::clear();
     impl_->nbuffer_.clear();
     impl_->nodeset_.clear();
     impl_->msglength_limit_ = 512;
@@ -203,41 +192,6 @@ MessageRenderer::clear() {
     impl_->compress_mode_ = CASE_INSENSITIVE;
 }
 
-void
-MessageRenderer::writeUint8(const uint8_t data) {
-    impl_->buffer_.writeUint8(data);
-}
-
-void
-MessageRenderer::writeUint16(const uint16_t data) {
-    impl_->buffer_.writeUint16(data);
-}
-
-void
-MessageRenderer::writeUint16At(const uint16_t data, const size_t pos) {
-    impl_->buffer_.writeUint16At(data, pos);
-}
-
-void
-MessageRenderer::writeUint32(const uint32_t data) {
-    impl_->buffer_.writeUint32(data);
-}
-
-void
-MessageRenderer::writeData(const void* const data, const size_t len) {
-    impl_->buffer_.writeData(data, len);
-}
-
-const void*
-MessageRenderer::getData() const {
-    return (impl_->buffer_.getData());
-}
-
-size_t
-MessageRenderer::getLength() const {
-    return (impl_->buffer_.getLength());
-}
-
 size_t
 MessageRenderer::getLengthLimit() const {
     return (impl_->msglength_limit_);
@@ -293,15 +247,15 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
     }
 
     // Record the current offset before extending the buffer.
-    const size_t offset = impl_->buffer_.getLength();
+    const size_t offset = getLength();
     // Write uncompress part...
-    impl_->buffer_.writeData(impl_->nbuffer_.getData(),
-                             compress ? i : impl_->nbuffer_.getLength());
+    writeData(impl_->nbuffer_.getData(),
+              compress ? i : impl_->nbuffer_.getLength());
     if (compress && n != notfound) {
         // ...and compression pointer if available.
         uint16_t pointer = (*n).pos_;
         pointer |= Name::COMPRESS_POINTER_MARK16;
-        impl_->buffer_.writeUint16(pointer);
+        writeUint16(pointer);
     }
 
     // Finally, add to the set the newly rendered name and its ancestors that
@@ -313,11 +267,17 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
         if (offset + j > Name::MAX_COMPRESS_POINTER) {
             break;
         }
-        impl_->nodeset_.insert(NameCompressNode(*this, impl_->buffer_,
+        impl_->nodeset_.insert(NameCompressNode(*this, getBuffer(),
                                                 offset + j,
                                                 impl_->nbuffer_.getLength() -
                                                 j));
     }
 }
+
+void
+AbstractMessageRenderer::clear() {
+    buffer_.clear();
+}
+
 }
 }

+ 148 - 78
src/lib/dns/messagerenderer.h

@@ -15,67 +15,64 @@
 #ifndef __MESSAGERENDERER_H
 #define __MESSAGERENDERER_H 1
 
+#include <util/buffer.h>
+
 namespace isc {
-namespace util {
-class OutputBuffer;
-}
 
 namespace dns {
 // forward declarations
 class Name;
 
+/// \brief The \c AbstractMessageRenderer class is an abstract base class
+/// that provides common interfaces for rendering a DNS message into a buffer
+/// in wire format.
 ///
-/// \brief The \c MessageRenderer class encapsulates implementation details
-/// of rendering a DNS message into a buffer in wire format.
+/// A specific derived class of \c AbstractMessageRenderer (we call it
+/// a renderer class hereafter) is simply responsible for name compression at
+/// least in the current design.  A renderer class object (conceptually)
+/// manages the positions of names rendered in some sort of buffer and uses
+/// that information to render subsequent names with compression.
 ///
-/// In effect, it's simply responsible for name compression at least in the
-/// current implementation.  A \c MessageRenderer class object manages the
-/// positions of names rendered in a buffer and uses that information to render
-/// subsequent names with compression.
-///
-/// This class is mainly intended to be used as a helper for a more
+/// A renderer class is mainly intended to be used as a helper for a more
 /// comprehensive \c Message class internally; normal applications won't have
-/// to care about this class.
-///
-/// A \c MessageRenderer class object is constructed with a \c OutputBuffer
-/// object, which is the buffer into which the rendered %data will be written.
-/// Normally the buffer is expected to be empty on construction, but it doesn't
-/// have to be so; the \c MessageRenderer object will start rendering from the
-/// end of the buffer at the time of construction.  However, if the
-/// pre-existing portion of the buffer contains DNS names, these names won't
-/// be considered for name compression.
+/// to care about details of this class.
 ///
-/// Once a \c MessageRenderer object is constructed with a buffer, it is
-/// generally expected that all rendering operations are performed via the
-/// \c MessageRenderer object.  If the application modifies the buffer in
-/// parallel with the \c MessageRenderer, the result will be undefined.
+/// Once a renderer class object is constructed with a buffer, it is
+/// generally expected that all rendering operations are performed via that
+/// object.  If the application modifies the buffer in
+/// parallel with the renderer, the result will be undefined.
 ///
 /// Note to developers: we introduced a separate class for name compression
 /// because previous benchmark with BIND9 showed compression affects overall
 /// response performance very much.  By having a separate class dedicated for
 /// this purpose, we'll be able to change the internal implementation of name
 /// compression in the future without affecting other part of the API and
-/// implementation.  For the same reason, we adopt the "pimpl" idiom in the
-/// class definition (i.e., using a pointer to a \c MessageRendererImpl class,
-/// which is defined with the class implementation, not in the header file):
-/// we may want to modify the compression implementation without modifying the
-/// header file thereby requesting rebuild the package.
+/// implementation.
 ///
-/// Furthermore, we may eventually want to allow other developers to develop
-/// and use their own compression implementation.  Should such a case become
-/// realistic, we may want to make the \c MessageRendererImpl class an abstract
-/// base class and let concrete derived classes have their own implementations.
-/// At the moment we don't the strong need for it, so we rather avoid over
-/// abstraction and keep the definition simpler.
-class MessageRenderer {
+/// In addition, by introducing a class hierarchy from
+/// \c AbstractMessageRenderer, we allow an application to use a customized
+/// renderer class for specific purposes.  For example, a high performance
+/// DNS server may want to use an optimized renderer class assuming some
+/// specific underlying data representation.
+///
+/// \note Some functions (like writeUint8) are not virtual. It is because
+///     it is hard to imagine any version of message renderer that would
+///     do anything else than just putting the data into a buffer, so we
+///     provide a default implementation and having them virtual would only
+///     hurt the performance with no real gain. If it would happen a different
+///     implementation is really needed, we can make them virtual in future.
+///     The only one that is virtual is writeName and it's because this
+///     function is much more complicated, therefore there's a lot of space
+///     for different implementations or behaviours.
+class AbstractMessageRenderer {
 public:
     /// \brief Compression mode constants.
     ///
     /// The \c CompressMode enum type represents the name compression mode
-    /// for the \c MessageRenderer.
+    /// for renderer classes.
     /// \c CASE_INSENSITIVE means compress names in case-insensitive manner;
     /// \c CASE_SENSITIVE means compress names in case-sensitive manner.
-    /// By default, \c MessageRenderer compresses names in case-insensitive
+    /// By default, a renderer compresses names in case-insensitive
     /// manner.
     /// Compression mode can be dynamically modified by the
     /// \c setCompressMode() method.
@@ -83,7 +80,7 @@ public:
     /// is not an intended usage.  In this case the names already compressed
     /// are intact; only names being compressed after the mode change are
     /// affected by the change.
-    /// If the internal \c MessageRenderer is reinitialized by the \c clear()
+    /// If a renderer class object is reinitialized by the \c clear()
     /// method, the compression mode will be reset to the default, which is
     /// \c CASE_INSENSITIVE
     ///
@@ -96,25 +93,39 @@ public:
         CASE_INSENSITIVE,  //!< Compress names case-insensitive manner (default)
         CASE_SENSITIVE     //!< Compress names case-sensitive manner
     };
-public:
+protected:
     ///
     /// \name Constructors and Destructor
     //@{
-    /// \brief Constructor from an output buffer.
-    ///
-    /// \param buffer An \c OutputBuffer object to which wire format data is
-    /// written.
-    MessageRenderer(isc::util::OutputBuffer& buffer);
+    /// \brief The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class should
+    /// never be instantiated (except as part of a derived class).
+    /// \param buffer The buffer where the data should be rendered into.
+    /// \todo We might want to revisit this API at some point and remove the
+    ///     buffer parameter. In that case it would create it's own buffer and
+    ///     a function to extract the data would be available instead. It seems
+    ///     like a cleaner design, but it's left undone until we would actually
+    ///     benefit from the change.
+    AbstractMessageRenderer(isc::util::OutputBuffer& buffer) :
+        buffer_(buffer)
+    {}
+public:
     /// \brief The destructor.
-    ///
-    /// The destructor does nothing on the given \c buffer on construction;
-    /// in fact, it is expected that the user will use the resulting buffer
-    /// for some post rendering purposes (e.g., send the data to the network).
-    /// It's the user's responsibility to do any necessary cleanup for the
-    /// \c buffer.
-    ~MessageRenderer();
+    virtual ~AbstractMessageRenderer() {}
     //@}
-
+protected:
+    /// \brief Return the output buffer we render into.
+    const isc::util::OutputBuffer& getBuffer() const { return (buffer_); }
+    isc::util::OutputBuffer& getBuffer() { return (buffer_); }
+private:
+    /// \short Buffer to store data
+    ///
+    /// It was decided that there's no need to have this in every subclass,
+    /// at least not now, and this reduces code size and gives compiler a better
+    /// chance to optimise.
+    isc::util::OutputBuffer& buffer_;
+public:
     ///
     /// \name Getter Methods
     ///
@@ -124,9 +135,15 @@ public:
     ///
     /// This method works exactly same as the same method of the \c OutputBuffer
     /// class; all notes for \c OutputBuffer apply.
-    const void* getData() const;
+    const void* getData() const {
+        return (buffer_.getData());
+    }
+
     /// \brief Return the length of data written in the internal buffer.
-    size_t getLength() const;
+    size_t getLength() const {
+        return (buffer_.getLength());
+    }
+
     /// \brief Return whether truncation has occurred while rendering.
     ///
     /// Once the return value of this method is \c true, it doesn't make sense
@@ -136,20 +153,22 @@ public:
     /// This method never throws an exception.
     ///
     /// \return true if truncation has occurred; otherwise \c false.
-    bool isTruncated() const;
+    virtual bool isTruncated() const = 0;
+
     /// \brief Return the maximum length of rendered data that can fit in the
     /// corresponding DNS message without truncation.
     ///
     /// This method never throws an exception.
     ///
     /// \return The maximum length in bytes.
-    size_t getLengthLimit() const;
-    /// \brief Return the compression mode of the \c MessageRenderer.
+    virtual size_t getLengthLimit() const = 0;
+
+    /// \brief Return the compression mode of the renderer class object.
     ///
     /// This method never throws an exception.
     ///
     /// \return The current compression mode.
-    CompressMode getCompressMode() const;
+    virtual CompressMode getCompressMode() const = 0;
     //@}
 
     ///
@@ -160,20 +179,22 @@ public:
     /// rendering.
     ///
     /// This method never throws an exception.
-    void setTruncated();
+    virtual void setTruncated() = 0;
+
     /// \brief Set the maximum length of rendered data that can fit in the
     /// corresponding DNS message without truncation.
     ///
     /// This method never throws an exception.
     ///
     /// \param len The maximum length in bytes.
-    void setLengthLimit(size_t len);
-    /// \brief Set the compression mode of the \c MessageRenderer.
+    virtual void setLengthLimit(size_t len) = 0;
+
+    /// \brief Set the compression mode of the renderer class object.
     ///
     /// This method never throws an exception.
     ///
     /// \param mode A \c CompressMode value representing the compression mode.
-    void setCompressMode(CompressMode mode);
+    virtual void setCompressMode(CompressMode mode) = 0;
     //@}
 
     ///
@@ -187,7 +208,10 @@ public:
     /// that is to be filled in later, e.g, by \ref writeUint16At().
     ///
     /// \param len The length of the gap to be inserted in bytes.
-    void skip(size_t len);
+    void skip(size_t len) {
+        buffer_.skip(len);
+    }
+
     /// \brief Trim the specified length of data from the end of the internal
     /// buffer.
     ///
@@ -198,21 +222,31 @@ public:
     /// be thrown.
     ///
     /// \param len The length of data that should be trimmed.
-    void trim(size_t len);
+    void trim(size_t len) {
+        buffer_.trim(len);
+    }
+
     /// \brief Clear the internal buffer and other internal resources.
     ///
     /// This method can be used to re-initialize and reuse the renderer
     /// without constructing a new one.
-    void clear();
+    virtual void clear();
+
     /// \brief Write an unsigned 8-bit integer into the internal buffer.
     ///
     /// \param data The 8-bit integer to be written into the internal buffer.
-    void writeUint8(uint8_t data);
+    void writeUint8(const uint8_t data) {
+        buffer_.writeUint8(data);
+    }
+
     /// \brief Write an unsigned 16-bit integer in host byte order into the
     /// internal buffer in network byte order.
     ///
     /// \param data The 16-bit integer to be written into the buffer.
-    void writeUint16(uint16_t data);
+    void writeUint16(uint16_t data) {
+        buffer_.writeUint16(data);
+    }
+
     /// \brief Write an unsigned 16-bit integer in host byte order at the
     /// specified position of the internal buffer in network byte order.
     ///
@@ -224,26 +258,29 @@ public:
     ///
     /// \param data The 16-bit integer to be written into the internal buffer.
     /// \param pos The beginning position in the buffer to write the data.
-    void writeUint16At(uint16_t data, size_t pos);
+    void writeUint16At(uint16_t data, size_t pos) {
+        buffer_.writeUint16At(data, pos);
+    }
+
     /// \brief Write an unsigned 32-bit integer in host byte order into the
     /// internal buffer in network byte order.
     ///
     /// \param data The 32-bit integer to be written into the buffer.
-    void writeUint32(uint32_t data);
+    void writeUint32(uint32_t data) {
+        buffer_.writeUint32(data);
+    }
+
     /// \brief Copy an arbitrary length of data into the internal buffer
-    /// of the \c MessageRenderer.
+    /// of the renderer object.
     ///
     /// No conversion on the copied data is performed.
     ///
     /// \param data A pointer to the data to be copied into the internal buffer.
     /// \param len The length of the data in bytes.
-    void writeData(const void *data, size_t len);
-    //@}
+    void writeData(const void *data, size_t len) {
+        buffer_.writeData(data, len);
+    }
 
-    ///
-    /// \name Rendering Methods
-    ///
-    //@{
     /// \brief Write a \c Name object into the internal buffer in wire format,
     /// with or without name compression.
     ///
@@ -258,8 +295,41 @@ public:
     ///
     /// \param name A \c Name object to be written.
     /// \param compress A boolean indicating whether to enable name compression.
-    void writeName(const Name& name, bool compress = true);
+    virtual void writeName(const Name& name, bool compress = true) = 0;
     //@}
+};
+
+/// The \c MessageRenderer is a concrete derived class of
+/// \c AbstractMessageRenderer as a general purpose implementation of the
+/// renderer interfaces.
+///
+/// A \c MessageRenderer object is constructed with a \c OutputBuffer
+/// object, which is the buffer into which the rendered %data will be written.
+/// Normally the buffer is expected to be empty on construction, but it doesn't
+/// have to be so; the renderer object will start rendering from the
+/// end of the buffer at the time of construction.  However, if the
+/// pre-existing portion of the buffer contains DNS names, these names won't
+/// be considered for name compression.
+class MessageRenderer : public AbstractMessageRenderer {
+public:
+    using AbstractMessageRenderer::CASE_INSENSITIVE;
+    using AbstractMessageRenderer::CASE_SENSITIVE;
+
+    /// \brief Constructor from an output buffer.
+    ///
+    /// \param buffer An \c OutputBuffer object to which wire format data is
+    /// written.
+    MessageRenderer(isc::util::OutputBuffer& buffer);
+
+    virtual ~MessageRenderer();
+    virtual bool isTruncated() const;
+    virtual size_t getLengthLimit() const;
+    virtual CompressMode getCompressMode() const;
+    virtual void setTruncated();
+    virtual void setLengthLimit(size_t len);
+    virtual void setCompressMode(CompressMode mode);
+    virtual void clear();
+    virtual void writeName(const Name& name, bool compress = true);
 private:
     struct MessageRendererImpl;
     MessageRendererImpl* impl_;

+ 1 - 2
src/lib/dns/name.cc

@@ -28,7 +28,6 @@
 using namespace std;
 using namespace isc::util;
 using isc::dns::NameComparisonResult;
-using isc::dns::MessageRenderer;
 
 namespace isc {
 namespace dns {
@@ -404,7 +403,7 @@ Name::toWire(OutputBuffer& buffer) const {
 }
 
 void
-Name::toWire(MessageRenderer& renderer) const {
+Name::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeName(*this);
 }
 

+ 2 - 2
src/lib/dns/name.h

@@ -29,7 +29,7 @@ class OutputBuffer;
 }
 
 namespace dns {
-class MessageRenderer;
+class AbstractMessageRenderer;
 
 ///
 /// \brief A standard DNS module exception that is thrown if the name parser
@@ -350,7 +350,7 @@ public:
     ///
     /// \param renderer DNS message rendering context that encapsulates the
     /// output buffer and name compression information.
-    void toWire(MessageRenderer& renderer) const;
+    void toWire(AbstractMessageRenderer& renderer) const;
 
     /// \brief Render the <code>Name</code> in the wire format without
     /// compression.

+ 1 - 1
src/lib/dns/python/tests/Makefile.am

@@ -20,7 +20,7 @@ EXTRA_DIST += testutil.py
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS

+ 1 - 1
src/lib/dns/rdata.cc

@@ -230,7 +230,7 @@ Generic::toWire(isc::util::OutputBuffer& buffer) const {
 }
 
 void
-Generic::toWire(MessageRenderer& renderer) const {
+Generic::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeData(&impl_->data_[0], impl_->data_.size());
 }
 

+ 3 - 3
src/lib/dns/rdata.h

@@ -27,7 +27,7 @@ class InputBuffer;
 class OutputBuffer;
 }
 namespace dns {
-class MessageRenderer;
+class AbstractMessageRenderer;
 class RRType;
 class RRClass;
 class Name;
@@ -182,7 +182,7 @@ public:
     ///
     /// \param renderer DNS message rendering context that encapsulates the
     /// output buffer in which the \c Rdata is to be stored.
-    virtual void toWire(MessageRenderer& renderer) const = 0;
+    virtual void toWire(AbstractMessageRenderer& renderer) const = 0;
     //@}
 
     ///
@@ -337,7 +337,7 @@ public:
     ///
     /// \param renderer DNS message rendering context that encapsulates the
     /// output buffer in which the \c Generic object is to be stored.
-    virtual void toWire(MessageRenderer& renderer) const;
+    virtual void toWire(AbstractMessageRenderer& renderer) const;
     //@}
 
     ///

+ 2 - 2
src/lib/dns/rdata/any_255/tsig_250.cc

@@ -380,9 +380,9 @@ TSIG::toWire(OutputBuffer& buffer) const {
 /// \param renderer DNS message rendering context that encapsulates the
 /// output buffer and name compression information.
 void
-TSIG::toWire(MessageRenderer& renderer) const {
+TSIG::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeName(impl_->algorithm_, false);
-    impl_->toWireCommon<MessageRenderer>(renderer);
+    impl_->toWireCommon<AbstractMessageRenderer>(renderer);
 }
 
 // A helper function commonly used for TSIG::compare().

+ 1 - 1
src/lib/dns/rdata/ch_3/a_1.cc

@@ -45,7 +45,7 @@ A::toWire(OutputBuffer&) const {
 }
 
 void
-A::toWire(MessageRenderer&) const {
+A::toWire(AbstractMessageRenderer&) const {
     // TBD
 }
 

+ 1 - 1
src/lib/dns/rdata/generic/cname_5.cc

@@ -53,7 +53,7 @@ CNAME::toWire(OutputBuffer& buffer) const {
 }
 
 void
-CNAME::toWire(MessageRenderer& renderer) const {
+CNAME::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeName(cname_);
 }
 

+ 1 - 1
src/lib/dns/rdata/generic/dname_39.cc

@@ -53,7 +53,7 @@ DNAME::toWire(OutputBuffer& buffer) const {
 }
 
 void
-DNAME::toWire(MessageRenderer& renderer) const {
+DNAME::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeName(dname_);
 }
 

+ 1 - 1
src/lib/dns/rdata/generic/dnskey_48.cc

@@ -136,7 +136,7 @@ DNSKEY::toWire(OutputBuffer& buffer) const {
 }
 
 void
-DNSKEY::toWire(MessageRenderer& renderer) const {
+DNSKEY::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeUint16(impl_->flags_);
     renderer.writeUint8(impl_->protocol_);
     renderer.writeUint8(impl_->algorithm_);

+ 1 - 1
src/lib/dns/rdata/generic/ds_43.cc

@@ -133,7 +133,7 @@ DS::toWire(OutputBuffer& buffer) const {
 }
 
 void
-DS::toWire(MessageRenderer& renderer) const {
+DS::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeUint16(impl_->tag_);
     renderer.writeUint8(impl_->algorithm_);
     renderer.writeUint8(impl_->digest_type_);

+ 1 - 1
src/lib/dns/rdata/generic/mx_15.cc

@@ -72,7 +72,7 @@ MX::toWire(OutputBuffer& buffer) const {
 }
 
 void
-MX::toWire(MessageRenderer& renderer) const {
+MX::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeUint16(preference_);
     renderer.writeName(mxname_);
 }

+ 1 - 1
src/lib/dns/rdata/generic/ns_2.cc

@@ -49,7 +49,7 @@ NS::toWire(OutputBuffer& buffer) const {
 }
 
 void
-NS::toWire(MessageRenderer& renderer) const {
+NS::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeName(nsname_);
 }
 

+ 1 - 1
src/lib/dns/rdata/generic/nsec3_50.cc

@@ -262,7 +262,7 @@ NSEC3::toWire(OutputBuffer& buffer) const {
 }
 
 void
-NSEC3::toWire(MessageRenderer& renderer) const {
+NSEC3::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeUint8(impl_->hashalg_);
     renderer.writeUint8(impl_->flags_);
     renderer.writeUint16(impl_->iterations_);

+ 1 - 1
src/lib/dns/rdata/generic/nsec3param_51.cc

@@ -136,7 +136,7 @@ NSEC3PARAM::toWire(OutputBuffer& buffer) const {
 }
 
 void
-NSEC3PARAM::toWire(MessageRenderer& renderer) const {
+NSEC3PARAM::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeUint8(impl_->hashalg_);
     renderer.writeUint8(impl_->flags_);
     renderer.writeUint16(impl_->iterations_);

+ 1 - 1
src/lib/dns/rdata/generic/nsec_47.cc

@@ -173,7 +173,7 @@ NSEC::toWire(OutputBuffer& buffer) const {
 }
 
 void
-NSEC::toWire(MessageRenderer& renderer) const {
+NSEC::toWire(AbstractMessageRenderer& renderer) const {
     impl_->nextname_.toWire(renderer);
     renderer.writeData(&impl_->typebits_[0], impl_->typebits_.size());
 }

+ 1 - 1
src/lib/dns/rdata/generic/opt_41.cc

@@ -57,7 +57,7 @@ OPT::toWire(OutputBuffer&) const {
 }
 
 void
-OPT::toWire(MessageRenderer&) const {
+OPT::toWire(AbstractMessageRenderer&) const {
     // nothing to do, as this simple version doesn't support any options.
 }
 

+ 1 - 1
src/lib/dns/rdata/generic/ptr_12.cc

@@ -54,7 +54,7 @@ PTR::toWire(OutputBuffer& buffer) const {
 }
 
 void
-PTR::toWire(MessageRenderer& renderer) const {
+PTR::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeName(ptr_name_);
 }
 

+ 1 - 1
src/lib/dns/rdata/generic/rp_17.cc

@@ -103,7 +103,7 @@ RP::toWire(OutputBuffer& buffer) const {
 }
 
 void
-RP::toWire(MessageRenderer& renderer) const {
+RP::toWire(AbstractMessageRenderer& renderer) const {
     // Type RP is not "well-known", and name compression must be disabled
     // per RFC3597.
     renderer.writeName(mailbox_, false);

+ 1 - 1
src/lib/dns/rdata/generic/rrsig_46.cc

@@ -184,7 +184,7 @@ RRSIG::toWire(OutputBuffer& buffer) const {
 }
 
 void
-RRSIG::toWire(MessageRenderer& renderer) const {
+RRSIG::toWire(AbstractMessageRenderer& renderer) const {
     impl_->covered_.toWire(renderer);
     renderer.writeUint8(impl_->algorithm_);
     renderer.writeUint8(impl_->labels_);

+ 1 - 1
src/lib/dns/rdata/generic/soa_6.cc

@@ -100,7 +100,7 @@ SOA::toWire(OutputBuffer& buffer) const {
 }
 
 void
-SOA::toWire(MessageRenderer& renderer) const {
+SOA::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeName(mname_);
     renderer.writeName(rname_);
     renderer.writeData(numdata_, sizeof(numdata_));

+ 1 - 1
src/lib/dns/rdata/generic/txt_16.cc

@@ -102,7 +102,7 @@ TXT::toWire(OutputBuffer& buffer) const {
 }
 
 void
-TXT::toWire(MessageRenderer& renderer) const {
+TXT::toWire(AbstractMessageRenderer& renderer) const {
     for (vector<vector<uint8_t> >::const_iterator it = string_list_.begin();
          it != string_list_.end();
          ++it)

+ 1 - 1
src/lib/dns/rdata/hs_4/a_1.cc

@@ -45,7 +45,7 @@ A::toWire(OutputBuffer&) const {
 }
 
 void
-A::toWire(MessageRenderer&) const {
+A::toWire(AbstractMessageRenderer&) const {
     // TBD
 }
 

+ 1 - 1
src/lib/dns/rdata/in_1/a_1.cc

@@ -70,7 +70,7 @@ A::toWire(OutputBuffer& buffer) const {
 }
 
 void
-A::toWire(MessageRenderer& renderer) const {
+A::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeData(&addr_, sizeof(addr_));
 }
 

+ 1 - 1
src/lib/dns/rdata/in_1/aaaa_28.cc

@@ -67,7 +67,7 @@ AAAA::toWire(OutputBuffer& buffer) const {
 }
 
 void
-AAAA::toWire(MessageRenderer& renderer) const {
+AAAA::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeData(&addr_, sizeof(addr_));
 }
 

+ 1 - 1
src/lib/dns/rdata/template.cc

@@ -51,7 +51,7 @@ MyType::toWire(OutputBuffer& buffer) const {
 }
 
 void
-MyType::toWire(MessageRenderer& renderer) const {
+MyType::toWire(AbstractMessageRenderer& renderer) const {
 }
 
 int

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

@@ -0,0 +1,223 @@
+// Copyright (C) 2010  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 <stdint.h>
+
+#include <cassert>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdatafields.h>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using isc::util::OutputBuffer;
+using isc::util::InputBuffer;
+
+namespace isc {
+namespace dns {
+namespace rdata {
+
+/// This is a helper class for \c RdataFields.
+///
+/// It manages a local storage for the data when \c RdataFields is constructed
+/// from an \c Rdata.
+/// To minimize construction overhead in the other case, an instance of
+/// this class is instantiated only when necessary - we don't need the vectors
+/// when only rendering.
+struct RdataFields::RdataFieldsDetail {
+    RdataFieldsDetail(const vector<FieldSpec>& fields,
+                      const uint8_t* data, size_t data_length) :
+        allocated_fields_(fields),
+        allocated_data_(data, data + data_length)
+    {}
+    const vector<FieldSpec> allocated_fields_;
+    const vector<uint8_t> allocated_data_;
+};
+
+namespace {
+// This class is used to divide the content of RDATA into \c RdataField
+// fields via message rendering logic.
+// The idea is to identify domain name fields in the writeName() method,
+// and determine whether they are compressible using the "compress"
+// parameter.
+// Other types of data are simply copied into the internal buffer, and
+// consecutive such fields are combined into a single \c RdataField field.
+//
+// Technically, this use of inheritance may be considered a violation of
+// Liskov Substitution Principle in that it doesn't actually compress domain
+// names, and some of the methods are not expected to be used.
+// In fact, skip() or trim() may not be make much sense in this context.
+// Nevertheless we keep this idea at the moment.  Since the usage is limited
+// (it's only used within this file, and only used with \c Rdata variants),
+// it's hopefully an acceptable practice.
+class RdataFieldComposer : public AbstractMessageRenderer {
+public:
+    RdataFieldComposer(OutputBuffer& buffer) :
+        AbstractMessageRenderer(buffer),
+        truncated_(false), length_limit_(65535),
+        mode_(CASE_INSENSITIVE), last_data_pos_(0)
+    {}
+    virtual ~RdataFieldComposer() {}
+    virtual bool isTruncated() const { return (truncated_); }
+    virtual size_t getLengthLimit() const { return (length_limit_); }
+    virtual CompressMode getCompressMode() const { return (mode_); }
+    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 Name& name, bool compress) {
+        extendData();
+        const RdataFields::Type field_type =
+            compress ? RdataFields::COMPRESSIBLE_NAME :
+            RdataFields::INCOMPRESSIBLE_NAME;
+        // TODO: When we get rid of need for getBuffer, we can output the name
+        // to a buffer and then write the buffer inside
+        name.toWire(getBuffer());
+        fields_.push_back(RdataFields::FieldSpec(field_type,
+                                                 name.getLength()));
+        last_data_pos_ = getLength();
+    }
+
+    virtual void clear() {
+        isc_throw(Unexpected, "unexpected clear() for RdataFieldComposer");
+    }
+    bool truncated_;
+    size_t length_limit_;
+    CompressMode mode_;
+    vector<RdataFields::FieldSpec> fields_;
+    vector<RdataFields::FieldSpec>& getFields() {
+        extendData();
+        return (fields_);
+    }
+    // We use generict write* methods, with the exception of writeName.
+    // So new data can arrive without us knowing it, this considers all new
+    // data to be just data and extends the fields to take it into account.
+    size_t last_data_pos_;
+    void extendData() {
+        // No news, return to work
+        if (getLength() == last_data_pos_) {
+            return;
+        }
+        // The new bytes are just ordinary uninteresting data
+        if (fields_.empty() || fields_.back().type != RdataFields::DATA) {
+            fields_.push_back(RdataFields::FieldSpec(RdataFields::DATA, 0));
+        }
+        // We added this much data from last time
+        fields_.back().len += getLength() - last_data_pos_;
+        last_data_pos_ = getLength();
+    }
+};
+
+}
+
+RdataFields::RdataFields(const Rdata& rdata) {
+    OutputBuffer buffer(0);
+    RdataFieldComposer field_composer(buffer);
+    rdata.toWire(field_composer);
+    nfields_ = field_composer.getFields().size();
+    data_length_ = field_composer.getLength();
+    if (nfields_ > 0) {
+        assert(data_length_ > 0);
+        detail_ = new RdataFieldsDetail(field_composer.getFields(),
+                                        static_cast<const uint8_t*>
+                                        (field_composer.getData()),
+                                        field_composer.getLength());
+        data_ = &detail_->allocated_data_[0];
+        fields_ = &detail_->allocated_fields_[0];
+    } else {
+        assert(data_length_ == 0);
+        detail_ = NULL;
+        data_ = NULL;
+        fields_ = NULL;
+    }
+}
+
+RdataFields::RdataFields(const void* fields, const unsigned int fields_length,
+                         const void* data, const size_t data_length) :
+    fields_(static_cast<const FieldSpec*>(fields)),
+    nfields_(fields_length / sizeof(*fields_)),
+    data_(static_cast<const uint8_t*>(data)),
+    data_length_(data_length),
+    detail_(NULL)
+{
+    if ((fields_ == NULL && nfields_ > 0) ||
+        (fields_ != NULL && nfields_ == 0)) {
+        isc_throw(InvalidParameter,
+                  "Inconsistent parameters for RdataFields: fields_length ("
+                  << fields_length << ") and fields conflict each other");
+    }
+    if ((data_ == NULL && data_length_ > 0) ||
+        (data_ != NULL && data_length_ == 0)) {
+        isc_throw(InvalidParameter,
+                  "Inconsistent parameters for RdataFields: data length ("
+                  << data_length_ << ") and data conflict each other");
+    }
+
+    size_t total_length = 0;
+    for (int i = 0; i < nfields_; ++i) {
+        total_length += fields_[i].len;
+    }
+    if (total_length != data_length_) {
+        isc_throw(InvalidParameter,
+                  "Inconsistent parameters for RdataFields; "
+                  "fields len: " << total_length <<
+                  " data len: " << data_length_);
+    }
+}
+
+RdataFields::~RdataFields() {
+    delete detail_;
+}
+
+RdataFields::FieldSpec
+RdataFields::getFieldSpec(const unsigned int field_id) const {
+    if (field_id >= nfields_) {
+        isc_throw(OutOfRange, "Rdata field ID is out of range: " << field_id);
+    }
+    return (fields_[field_id]);
+}
+
+void
+RdataFields::toWire(AbstractMessageRenderer& renderer) const {
+    size_t offset = 0;
+
+    for (int i = 0; i < nfields_; ++i) {
+        if (fields_[i].type == DATA) {
+            renderer.writeData(data_ + offset, fields_[i].len);
+        } else {
+            // XXX: this is inefficient.  Even if it's quite likely the
+            // data is a valid wire representation of a name we parse
+            // it to construct the Name object in the generic mode.
+            // This should be improved in a future version.
+            InputBuffer buffer(data_ + offset, fields_[i].len);
+            renderer.writeName(Name(buffer),
+                               fields_[i].type == COMPRESSIBLE_NAME);
+        }
+        offset += fields_[i].len;
+    }
+}
+
+void
+RdataFields::toWire(OutputBuffer& buffer) const {
+    buffer.writeData(data_, data_length_);
+}
+} // end of namespace rdata
+} // end of namespace dns
+} // end of namespace isc

+ 427 - 0
src/lib/dns/rdatafields.h

@@ -0,0 +1,427 @@
+// Copyright (C) 2010  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 __RDATAFIELDS_H
+#define __RDATAFIELDS_H 1
+
+#include <stdint.h>
+
+#include <cstddef>
+
+namespace isc {
+namespace util {
+class OutputBuffer;
+}
+namespace dns {
+class AbstractMessageRenderer;
+
+namespace rdata {
+class Rdata;
+
+/// A low-level, RR type-independent representation of DNS RDATA.
+///
+/// <b>Purpose of the Class</b>
+///
+/// This class intends to help "serialization" of the content of RDATA
+/// in a space-efficient manner.  Specific derived classes of \c Rdata
+/// focus on the convenience of accessing RDATA fields for RR type-specific
+/// protocol operations, and can be inefficient in terms of space.
+/// For example, a DNS character string may be internally represented as a
+/// \c std::string object with all of the overhead of the richer class.
+/// If an application needs to maintain a very large number of RRs and it
+/// does not have to perform RR specific operation so often, it may make more
+/// sense to store the data in memory in a lower-level but space efficient
+/// form.
+///
+/// Another purpose of this class is to improve rendering performance for
+/// RDATA.  If the only requirement were space efficiency, it would be just
+/// sufficient to convert the \c RDATA into a binary sequence in the wire
+/// format.  However, to render the data in a DNS message, we'd have to
+/// re-construct a corresponding \c Rdata object in the case where name
+/// compression is necessary.  This is not desirable, and this class is
+/// provided to avoid such unnecessary overhead.
+///
+/// <b>Data Format</b>
+///
+/// To meet these goals, this class helps convert an \c Rdata object into
+/// two pieces of information: Wire-format representation of the \c Rdata
+/// and associated meta information for efficient rendering.
+///
+/// Specifically, it maintains the wire-format data as a sequence of typed
+/// fields.  The types are:
+/// - Compressible name: a domain name as an RDATA field that can be compressed
+/// - Incompressible name: a domain name as an RDATA field that cannot be
+///   compressed
+/// - Other data: any other fields of RDATA, which should be treated as opaque
+///
+/// (See also the description of \c RdataFields::Type)
+/// Whether a name can or cannot be compressed is determined according to
+/// RFC3597.
+///
+/// A "other data" field may not always correspond to a single RDATA field.
+/// A \c RdataFields field (of other data) is just a contiguous region of the
+/// wire-format data that does not involve name compression.
+/// For example, the SOA RDATA begins with two "compressible" names followed
+/// by 5 32-bit fields.
+/// In \c RdataFields the last 5 fields would be considered a single 20-byte
+/// field.
+///
+/// Each \c RdataFields field is identified by the \c FieldSpec structure,
+/// which provides the type and length of the field.
+/// An \c RdataFields object internally maintains a sequence of \c FieldSpec
+/// objects in a form of plain C-style array, which can be referenced via
+/// a pointer returned by the \c getFieldSpecData() method.
+/// The \c \c FieldSpec for a specific field can also be retrieved by the
+/// \c getFieldSpec() method.
+///
+/// The following diagram shows the internal memory representation of
+/// an SOA RDATA in the form of \c RdataFields object and how an application
+/// can get access to the memory region.
+/** \verbatim
+accessible via      |0                               getDataLength() bytes
+getData()----------> <MNAME><RNAME><Rest of the data>
+                     <---------- 3 * sizeof(FieldSpec) bytes ------------->
+getFieldSpecData()-> { compressible name { compressible name { other data
+                       len: MNAME-len }    len: RNAME-len }    len: 20    }
+\endverbatim
+ */
+/// where MNAME and RNAME are wire format representations of the MNAME and
+/// RNAME fields of the SOA RDATA, respectively, and "Rest of the data"
+/// encodes the remaining 20 bytes of the RDATA in network byte order.
+///
+/// <b>Usage of the Class</b>
+///
+/// One major and common use case of the \c RdataFields class is to convert
+/// a \c Rdata object (possibly given from a DNS message or some configuration
+/// source such as a zone file) in the serialized format and store a copy of
+/// the data somewhere in memory.  The following code sample implements this
+/// scenario:
+/// \code // assume "rdata" is a reference type to Rdata
+/// const RdataFields fields(rdata);
+/// const unsigned int fields_size = fields.getFieldDataSize();
+/// memcpy(some_place, fields.getFieldSpecData(), fields_size);
+/// const size_t data_length = fields.getDataLength();
+/// memcpy(other_place, fields.getData(), data_length);
+/// // (fields_size and data_length should be stored somewhere, too)
+/// \endcode
+///
+/// Another typical usage is to render the stored data in the wire format
+/// as efficiently as possible.  The following code is an example of such
+/// usage:
+/// \code // assume "renderer" is of type MessageRenderer
+/// // retrieve data_length and fields_size from the storage
+/// RdataFields(some_place, fields_size, other_place,
+///             data_length).toWire(renderer);
+/// \endcode
+///
+/// <b>Notes to Users</b>
+///
+/// The main purposes of this class is to help efficient operation
+/// for some (limited classes of) performance sensitive application.
+/// For this reason the interface and implementation rely on relatively
+/// lower-level, riskier primitives such as passing around bare pointers.
+///
+/// It is therefore discouraged to use this class for general purpose
+/// applications that do not need to maximize performance in terms of either
+/// memory footprint or rendering speed.
+/// All functionality provided by this class can be achieved via higher level
+/// interfaces such as the \c Rdata class variants.
+/// Normal applications should use those interfaces.
+///
+/// The data format is public information so that an application can examine
+/// and use selected parts of data.  For example, an application may want to
+/// encode domain names in RDATA in a different way while storing the other
+/// data in a separate place.
+/// However, at this moment the format is still in flux, and it may not
+/// be compatible with future versions (see below).
+///
+/// <b>Development Notes</b>
+///
+/// We should conduct benchmark tests to measure rendering performance.
+///
+/// The current implementation needs to re-construct name objects from
+/// compressible and incompressible name fields as wire-format data.
+/// This is not efficient, and we'll probably want to improve this in a
+/// future version.  One possibility is to store offset information as well
+/// as the name data (at the cost of increasing memory footprint), and
+/// to use the pair of data for faster rendering.
+class RdataFields {
+public:
+    /// Types of \c RdataFields fields.
+    ///
+    /// \c COMPRESSIBLE_NAME and \c INCOMPRESSIBLE_NAME represent a domain
+    /// name used as a field of an RDATA that can and cannot be compressed
+    /// per RFC3597.
+    /// \c DATA means all other types of fields.
+    enum Type {
+        DATA,              ///< Plain data.
+        COMPRESSIBLE_NAME, ///< A domain name subject to name compression.
+        INCOMPRESSIBLE_NAME ///< A domain name that shouldn't be compressed.
+    };
+
+    /// Structure that specifies a single \c RdataFields field.
+    ///
+    /// This is a straightforward pair of the type and length of a single
+    /// \c RdataFields field.
+    ///
+    /// In some cases an application may want to do deeper inspection of
+    /// some \c RdataFields field(s).  For example, an application may want
+    /// to construct a \c Name object for each domain name field of an RDATA
+    /// and use it for some special purpose.
+    /// The \c FieldSpec structure provides necessary parameters to get access
+    /// to a specific \c RdataFields field.
+    ///
+    /// The following code snippet implements the above example scenario:
+    /// \code // assume "fields" is of type RdataFields
+    /// size_t offset = 0;
+    /// for (int i = 0; i < fields.getFieldCount(); ++i) {
+    ///     const FieldSpec spec = fields.getFieldSpec(i);
+    ///     if (spec.type == RdataFields::COMPRESSIBLE_NAME ||
+    ///         spec.type == RdataFields::INCOMPRESSIBLE_NAME) {
+    ///         InputBuffer ibuffer(fields.getData() + offset, spec.len);
+    ///         Name name(ibuffer);
+    ///         // do something with name
+    ///     }
+    ///     offset += spec.len;
+    /// } \endcode
+    ///
+    /// Note that the offset is not included in \c FieldSpec.
+    /// This is because such deeper inspection would be a relatively rare
+    /// operation while it is desirable to keep this structure as small as
+    /// possible for the purpose of space efficiency.
+    /// Also, if and when an application wants to look into a specific field,
+    /// it would be quite likely that the application iterates over all fields
+    /// and does something special for selected fields like the above example.
+    /// In that case the application can easily and efficiently identify the
+    /// necessary offset, again, as shown in the above code example.
+    ///
+    /// \todo We might find that 16bits per field is generally too much and
+    ///     squeeze the two bit type into it as well, having 14bit length
+    ///     (in the rare case of having too long field, it could be split into
+    ///     multiple ones). That would save 2 bytes per item (one for the type,
+    ///     one for padding).
+    struct FieldSpec {
+        FieldSpec(Type type_param, uint16_t len_param) :
+            type(type_param), len(len_param)
+        {}
+        Type type;              ///< The type of the field.
+        uint16_t len;           ///< The length of the field in bytes.
+    };
+
+    ///
+    /// \name Constructors and Destructor.
+    ///
+    /// \b Note:
+    /// The copy constructor and the assignment operator are intentionally
+    /// defined as private, making this class non copyable.
+    //@{
+private:
+    RdataFields(const RdataFields& source);
+    RdataFields& operator=(const RdataFields& source);
+
+public:
+    /// Constructor from Rdata.
+    ///
+    /// This constructor converts the data of a given \c Rdata object into
+    /// an \c RdataFields object so that the resulting data can be stored
+    /// in memory in a space-efficient way.
+    ///
+    /// It makes a local copy of the original data and dynamically allocates
+    /// necessary memory, so is not very efficient.
+    /// The basic idea is to perform the expensive conversion once and keep
+    /// using the result as long as possible to improve overall performance
+    /// in a longer term.
+    ///
+    /// If the internal resource allocation fails, a corresponding standard
+    /// exception will be thrown.
+    /// The current implementation of this constructor internally calls
+    /// the <code>Rdata::toWire(AbstractMessageRenderer&) const</code> method
+    /// for the conversion.
+    /// If that method throws an exception it will be propagated to the caller
+    /// of this constructor.
+    ///
+    /// \param rdata The RDATA for which the \c RdataFields to be constructed.
+    RdataFields(const Rdata& rdata);
+
+    /// Constructor from field parameters.
+    ///
+    /// The intended usage of this version of constructor is to form a
+    /// structured representation of \c RDATA encoded by the other
+    /// constructor so that the resulting object can be used for subsequent
+    /// operations such as rendering in the wire format.
+    /// This version is intended to be efficient by not making any copy
+    /// of variable length data or expensive data inspection.
+    ///
+    /// This constructor is basically exception free, except against bogus
+    /// input parameters.
+    /// Specifically, the parameters must meet the following conditions;
+    /// otherwise an exception of class \c InvalidParameter will be thrown.
+    /// - \c fields can be \c NULL if and only if \c nfields is 0
+    /// - \c data can be \c NULL if and only if \c data_length is 0
+    /// - the sum of the lengths of \c fields entries must be equal to
+    ///   \c data_length
+    ///
+    /// This constructor assumes that the memory region pointed by \c data (if
+    /// non \c NULL) is encoded as a sequence of valid \c RdataFields fields,
+    /// and does not perform deep inspection on each field.
+    /// In particular, for fields of type \c COMPRESSIBLE_NAME or
+    /// \c INCOMPRESSIBLE_NAME, this constructor assumes the corresponding
+    /// memory region is a valid representation of domain name.
+    /// Otherwise, a subsequent method call such as
+    /// <code>toWire(AbstractMessageRenderer&) const</code>
+    /// may trigger an unexpected exception. It also expects the fields reside
+    /// on address that is valid for them (eg. it has valid alignment), see
+    /// getFieldSpecData() for details.
+    ///
+    /// It is the caller's responsibility to ensure this assumption.
+    /// In general, this constructor is expected to be used for serialized data
+    /// generated by the other constructor from a valid \c Rdata.
+    /// The result is not guaranteed if the data is generated in any other
+    /// ways.
+    ///
+    /// The resulting \c RdataFields object does not maintain a copy of
+    /// \c fields or \c data.  It is the caller's responsibility to ensure
+    /// the memory regions pointed to by these parameters are valid and intact
+    /// as long as the \c RdataFields object is used.
+    ///
+    /// \param fields An array of \c FieldSpec entries.  This can be \c NULL.
+    /// \param nfields The number of entries of \c fields.
+    /// \param data A pointer to memory region for the entire RDATA.  This can
+    /// be NULL.
+    /// \param data_length The length of \c data in bytes.
+    RdataFields(const void* fields, const unsigned int fields_length,
+                const void* data, const size_t data_length);
+
+    /// The destructor.
+    ~RdataFields();
+    //@}
+
+    ///
+    /// \name Getter Methods
+    ///
+    //@{
+    /// \brief Return the length of the entire RDATA encoded in the
+    /// \c RdataFields in bytes.
+    ///
+    /// This method never throws an exception.
+    unsigned int getDataLength() const { return (data_length_); }
+
+    /// \brief Return a pointer to the RDATA encoded in the \c RdataFields.
+    ///
+    /// The RdataFields holds ownership of the data.
+    ///
+    /// This method never throws an exception.
+    const void* getData() const { return (data_); }
+
+    /// \brief Return the number of bytes the buffer returned by
+    ///      getFieldSpecData() will occupy.
+    ///
+    /// This method never throws an exception.
+    unsigned int getFieldSpecDataSize() const { return (nfields_ *
+                                                    sizeof (*fields_)); }
+
+    /// \brief Return the number of specs fields.
+    ///
+    /// It specifies the range of parameter for getFieldSpec().
+    ///
+    /// This method never throws.
+    unsigned int getFieldCount() const { return (nfields_); }
+
+    /// \brief Return a pointer to a sequence of \c FieldSpec for the
+    /// \c RdataFields.
+    ///
+    /// This should be treated as an opaque internal representation you can
+    /// just store off somewhere and use it to construct a new RdataFields.
+    /// from it. If you are really interested, you can typecast it to
+    /// FieldSpec * (which is what it really is internally).
+    ///
+    /// The RdataFields holds ownership of the data.
+    ///
+    /// \note You should, however, be aware of alignment issues. The pointer
+    ///     you pass to the constructor must be an address where the FieldSpec
+    ///     can live. If you store it at a wrong address (eg. even one with
+    ///     current implementation on most architectures), it might lead bad
+    ///     things from slow access to SIGBUS. The easiest way is not to
+    ///     interleave the fields with data from getData(). It is OK to place
+    ///     all the fields first (even from multiple RdataFields) and then
+    ///     place all the data after them.
+    ///
+    /// This method never throws an exception.
+    const void* getFieldSpecData() const {
+        return (fields_);
+    }
+
+    /// \brief Return the specification of the field identified by the given
+    /// index.
+    ///
+    /// \c field_id is the field index, which must be in the range of
+    /// <code>[0, getFieldCount())</code>.  0 means the first field, and
+    /// <code>getFieldCount()-1</code> means the last.
+    ///
+    /// If the given index is not in the valid range, an exception of class
+    /// \c OutOfRange will be thrown.
+    /// This method never throws an exception otherwise.
+    ///
+    /// \param field_id The index of an \c RdataFields field to be returned.
+    /// \return A \c FieldSpec structure that contains the information of
+    /// the \c field_id-th field.
+    FieldSpec getFieldSpec(const unsigned int field_id) const;
+    //@}
+
+    ///
+    /// \name Converter Methods
+    ///
+    //@{
+    /// \brief Render the RdataFields in the wire format with name compression.
+    ///
+    /// This method may require resource allocation in \c renderer.
+    /// If it fails, a corresponding standard exception will be thrown.
+    /// It should not throw any other exception as long as the \c RdataFields
+    /// object was constructed from valid parameters (see the description of
+    /// constructors).  The result is not guaranteed if it's constructed in
+    /// any other ways.
+    ///
+    /// \param renderer DNS message rendering context that encapsulates the
+    /// output buffer and name compression information.
+    void toWire(AbstractMessageRenderer& renderer) const;
+
+    /// \brief Render the RdataFields in the wire format without name
+    /// compression.
+    ///
+    /// This method may require resource allocation in \c buffer.
+    /// If it fails, a corresponding standard exception will be thrown.
+    ///
+    /// \param buffer An output buffer to store the wire data.
+    void toWire(isc::util::OutputBuffer& buffer) const;
+    //@}
+
+private:
+    const FieldSpec* fields_;
+    unsigned int nfields_;
+    const uint8_t* data_;
+    size_t data_length_;
+
+    // hide further details within the implementation and don't create vectors
+    // every time we don't need them.
+    struct RdataFieldsDetail;
+    RdataFieldsDetail* detail_;
+};
+}
+}
+}
+#endif  // __RDATAFIELDS_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 2 - 2
src/lib/dns/rrtype-placeholder.h

@@ -31,7 +31,7 @@ class OutputBuffer;
 namespace dns {
 
 // forward declarations
-class MessageRenderer;
+class AbstractMessageRenderer;
 
 ///
 /// \brief A standard DNS module exception that is thrown if an RRType object
@@ -181,7 +181,7 @@ public:
     /// standard exception will be thrown.
     ///
     /// \param buffer An output buffer to store the wire data.
-    void toWire(MessageRenderer& renderer) const;
+    void toWire(AbstractMessageRenderer& renderer) const;
     /// \brief Render the \c RRType in the wire format.
     ///
     /// This method renders the type code in network byte order into the

+ 1 - 1
src/lib/dns/rrtype.cc

@@ -53,7 +53,7 @@ RRType::toWire(OutputBuffer& buffer) const {
 }
 
 void
-RRType::toWire(MessageRenderer& renderer) const {
+RRType::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeUint16(typecode_);
 }
 

+ 1 - 0
src/lib/dns/tests/Makefile.am

@@ -25,6 +25,7 @@ run_unittests_SOURCES += rrttl_unittest.cc
 run_unittests_SOURCES += opcode_unittest.cc
 run_unittests_SOURCES += rcode_unittest.cc
 run_unittests_SOURCES += rdata_unittest.h rdata_unittest.cc
+run_unittests_SOURCES += rdatafields_unittest.cc
 run_unittests_SOURCES += rdata_in_a_unittest.cc rdata_in_aaaa_unittest.cc
 run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc
 run_unittests_SOURCES += rdata_txt_unittest.cc rdata_mx_unittest.cc

+ 380 - 0
src/lib/dns/tests/rdatafields_unittest.cc

@@ -0,0 +1,380 @@
+// Copyright (C) 2010  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 <vector>
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatafields.h>
+#include <dns/tests/unittest_util.h>
+
+#include <gtest/gtest.h>
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using isc::util::OutputBuffer;
+using isc::util::InputBuffer;
+
+namespace {
+class RdataFieldsTest : public ::testing::Test {
+protected:
+    RdataFieldsTest() : obuffer(0), renderer_buffer(0),
+                        renderer(renderer_buffer),
+                        ns_name("example.com"),
+                        other_name("www.example.com")
+    {}
+    void constructCommonTests(const RdataFields& fields,
+                              const uint8_t* const expected_data,
+                              const size_t expected_data_len);
+    void constructCommonTestsNS(const RdataFields& fields);
+    void constructCommonTestsTXT(const RdataFields& fields);
+    void constructCommonTestsRRSIG(const RdataFields& fields);
+    void constructCommonTestsOPT(const RdataFields& fields);
+    OutputBuffer obuffer;
+    OutputBuffer renderer_buffer;
+    MessageRenderer renderer;
+    const Name ns_name;
+    const Name other_name;
+    vector<unsigned char> expected_wire;
+    vector<unsigned char> fields_wire;
+};
+
+const uint8_t in_a_data[] = { 192, 0, 2, 1 };
+// binary representation of example.com.
+const uint8_t ns_data[] = { 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+                            0x03, 0x63, 0x6f, 0x6d, 0x00 };
+
+//
+// IN/A RDATA: fixed length, single data field
+//
+void
+RdataFieldsTest::constructCommonTests(const RdataFields& fields,
+                                      const uint8_t* const expected_data,
+                                      const size_t expected_data_len)
+{
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, expected_data,
+                        expected_data_len, fields.getData(),
+                        fields.getDataLength());
+    EXPECT_EQ(sizeof(RdataFields::FieldSpec), fields.getFieldSpecDataSize());
+    EXPECT_EQ(1, fields.getFieldCount());
+    EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(0).type);
+    EXPECT_EQ(4, fields.getFieldSpec(0).len);
+
+    fields.toWire(obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, expected_data,
+                        expected_data_len, obuffer.getData(),
+                        obuffer.getLength());
+
+    fields.toWire(renderer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, expected_data,
+                        expected_data_len, renderer.getData(),
+                        renderer.getLength());
+}
+
+TEST_F(RdataFieldsTest, constructFromRdata) {
+    const RdataFields fields(in::A("192.0.2.1"));
+    constructCommonTests(fields, in_a_data, sizeof(in_a_data));
+}
+
+TEST_F(RdataFieldsTest, constructFromParams) {
+    const RdataFields::FieldSpec spec(RdataFields::DATA, 4);
+    const RdataFields fields(&spec, sizeof(spec), in_a_data,
+                             sizeof(in_a_data));
+    constructCommonTests(fields, in_a_data, sizeof(in_a_data));
+}
+
+//
+// NS RDATA: containing a compressible name.
+//
+void
+RdataFieldsTest::constructCommonTestsNS(const RdataFields& fields) {
+    EXPECT_EQ(sizeof(RdataFields::FieldSpec), fields.getFieldSpecDataSize());
+    EXPECT_EQ(1, fields.getFieldCount());
+    EXPECT_EQ(RdataFields::COMPRESSIBLE_NAME, fields.getFieldSpec(0).type);
+    EXPECT_EQ(ns_name.getLength(), fields.getFieldSpec(0).len);
+
+    expected_wire.clear();
+    UnitTestUtil::readWireData("rdatafields1.wire", expected_wire);
+    other_name.toWire(obuffer);
+    fields.toWire(obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
+                        expected_wire.size(), obuffer.getData(),
+                        obuffer.getLength());
+
+    expected_wire.clear();
+    UnitTestUtil::readWireData("rdatafields2.wire", expected_wire);
+    other_name.toWire(renderer);
+    fields.toWire(renderer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
+                        expected_wire.size(), renderer.getData(),
+                        renderer.getLength());
+}
+
+TEST_F(RdataFieldsTest, constructFromRdataNS) {
+    const RdataFields fields_ns((generic::NS(ns_name)));
+    constructCommonTestsNS(fields_ns);
+}
+
+TEST_F(RdataFieldsTest, constructFromParamsNS) {
+    const RdataFields::FieldSpec spec(RdataFields::COMPRESSIBLE_NAME,
+                                      sizeof(ns_data));
+    const RdataFields fields_ns(&spec, sizeof(spec), ns_data, sizeof(ns_data));
+    constructCommonTestsNS(fields_ns);
+}
+
+//
+// TXT RDATA: multiple fields, lengths vary
+//
+void
+RdataFieldsTest::constructCommonTestsTXT(const RdataFields& fields) {
+    // Since all fields are plain data, they are handled as a single data
+    // field.
+    EXPECT_EQ(sizeof(RdataFields::FieldSpec), fields.getFieldSpecDataSize());
+    EXPECT_EQ(1, fields.getFieldCount());
+    EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(0).type);
+    EXPECT_EQ(expected_wire.size(), fields.getFieldSpec(0).len);
+
+    fields.toWire(obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
+                        expected_wire.size(), obuffer.getData(),
+                        obuffer.getLength());
+
+    fields.toWire(renderer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
+                        expected_wire.size(), renderer.getData(),
+                        renderer.getLength());
+}
+
+TEST_F(RdataFieldsTest, constructFromRdataTXT) {
+    UnitTestUtil::readWireData("rdatafields3.wire", expected_wire);
+    InputBuffer ibuffer(&expected_wire[0], expected_wire.size());
+    const uint16_t rdlen = ibuffer.readUint16();
+    const RdataFields fields(generic::TXT(ibuffer, rdlen));
+
+    // drop the RDLEN part
+    expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2);
+
+    constructCommonTestsTXT(fields);
+}
+
+TEST_F(RdataFieldsTest, constructFromParamsTXT) {
+    UnitTestUtil::readWireData("rdatafields3.wire", expected_wire);
+    expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2);
+    const RdataFields::FieldSpec spec(RdataFields::DATA, expected_wire.size());
+    const RdataFields fields(&spec, sizeof(spec), &expected_wire[0],
+                             expected_wire.size());
+    constructCommonTestsTXT(fields);
+}
+
+//
+// RRSIG: multiple fields, with an incompressible name
+//
+void
+RdataFieldsTest::constructCommonTestsRRSIG(const RdataFields& fields) {
+    // In terms of RdataFields RRSIG RDATA consists of 3 fields:
+    // - 18-byte data field (from the "type covered" field to "key tag" field)
+    // - an incompressible name field (for the signer's name field).
+    //   this is a variable length field.  In this test it's a 13-byte field.
+    // - a variable-length data field for the signature.  In this tests
+    //   it's a 15-byte field.
+    EXPECT_EQ(3 * sizeof(RdataFields::FieldSpec),
+              fields.getFieldSpecDataSize());
+    EXPECT_EQ(3, fields.getFieldCount());
+    EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(0).type);
+    EXPECT_EQ(18, fields.getFieldSpec(0).len);
+    EXPECT_EQ(RdataFields::INCOMPRESSIBLE_NAME, fields.getFieldSpec(1).type);
+    EXPECT_EQ(13, fields.getFieldSpec(1).len);
+    EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(2).type);
+    EXPECT_EQ(15, fields.getFieldSpec(2).len);
+
+    expected_wire.clear();
+    UnitTestUtil::readWireData("rdatafields5.wire", expected_wire);
+    Name("com").toWire(obuffer);
+    obuffer.writeUint16(fields.getDataLength());
+    fields.toWire(obuffer);
+    other_name.toWire(obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
+                        expected_wire.size(), obuffer.getData(),
+                        obuffer.getLength());
+
+    expected_wire.clear();
+    UnitTestUtil::readWireData("rdatafields6.wire", expected_wire);
+    Name("com").toWire(renderer);
+    renderer.writeUint16(fields.getDataLength());
+    fields.toWire(renderer);    // the signer field won't be compressed
+    other_name.toWire(renderer); // but will be used as a compression target
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
+                        expected_wire.size(), renderer.getData(),
+                        renderer.getLength());
+}
+
+TEST_F(RdataFieldsTest, constructFromRdataRRSIG) {
+    UnitTestUtil::readWireData("rdatafields4.wire", expected_wire);
+    InputBuffer ibuffer(&expected_wire[0], expected_wire.size());
+    const uint16_t rdlen = ibuffer.readUint16();
+    const RdataFields fields(generic::RRSIG(ibuffer, rdlen));
+
+    // drop the RDLEN part
+    expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2);
+
+    constructCommonTestsRRSIG(fields);
+}
+
+TEST_F(RdataFieldsTest, constructFromParamsRRSIG) {
+    UnitTestUtil::readWireData("rdatafields4.wire", fields_wire);
+    fields_wire.erase(fields_wire.begin(), fields_wire.begin() + 2);
+
+    const RdataFields::FieldSpec specs[] = {
+        RdataFields::FieldSpec(RdataFields::DATA, 18),
+        RdataFields::FieldSpec(RdataFields::INCOMPRESSIBLE_NAME, 13),
+        RdataFields::FieldSpec(RdataFields::DATA, 15)
+    };
+    const RdataFields fields(specs, sizeof(specs), &fields_wire[0],
+                             fields_wire.size());
+    constructCommonTestsRRSIG(fields);
+}
+
+TEST_F(RdataFieldsTest, convertRdatatoParams) {
+    // Confirm we can restore the original data from the serialized data.
+    // We use RRSIG as a relatively complicated field structure.
+    UnitTestUtil::readWireData("rdatafields4.wire", expected_wire);
+    InputBuffer ibuffer(&expected_wire[0], expected_wire.size());
+    const uint16_t rdlen = ibuffer.readUint16();
+    const RdataFields fields(generic::RRSIG(ibuffer, rdlen));
+
+    expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2);
+
+    // Copy the data in separate storage
+    vector<uint8_t> spec_store(fields.getFieldSpecDataSize());
+    void* cp_spec = &spec_store[0];
+    memcpy(cp_spec, fields.getFieldSpecData(), spec_store.size());
+    vector<uint8_t> data_store(fields.getDataLength());
+    memcpy(&data_store[0], fields.getData(), fields.getDataLength());
+
+    // Restore the data in the form of RdataFields
+    const RdataFields fields_byparams(cp_spec, fields.getFieldSpecDataSize(),
+                                      &data_store[0], fields.getDataLength());
+
+    // Check it's valid
+    constructCommonTestsRRSIG(fields_byparams);
+}
+
+//
+// OPT: an empty RDATA
+//
+void
+RdataFieldsTest::constructCommonTestsOPT(const RdataFields& fields) {
+    EXPECT_EQ(0, fields.getFieldSpecDataSize());
+    EXPECT_EQ(0, fields.getFieldCount());
+    EXPECT_EQ(0, fields.getDataLength());
+    EXPECT_EQ((const uint8_t*) NULL, fields.getData());
+    fields.toWire(obuffer);
+    EXPECT_EQ(0, obuffer.getLength());
+    fields.toWire(renderer);
+    EXPECT_EQ(0, renderer.getLength());
+}
+
+TEST_F(RdataFieldsTest, constructFromRdataOPT) {
+    InputBuffer ibuffer(NULL, 0);
+    const RdataFields fields(generic::OPT(ibuffer, 0));
+    constructCommonTestsOPT(fields);
+}
+
+TEST_F(RdataFieldsTest, constructFromParamsOPT) {
+    const RdataFields fields(NULL, 0, NULL, 0);
+    constructCommonTestsOPT(fields);
+}
+
+// Invalid input to the "from parameter" constructor: sum of the field lengths
+// is not equal to the data length.
+TEST_F(RdataFieldsTest, invalidFieldLength) {
+    UnitTestUtil::readWireData("rdatafields4.wire", fields_wire);
+    fields_wire.erase(fields_wire.begin(), fields_wire.begin() + 2);
+
+    const RdataFields::FieldSpec specs[] = {
+        RdataFields::FieldSpec(RdataFields::DATA, 18),
+        RdataFields::FieldSpec(RdataFields::INCOMPRESSIBLE_NAME, 13),
+        RdataFields::FieldSpec(RdataFields::DATA, 14)
+    };
+    // sum of field len < data len
+    EXPECT_THROW(RdataFields(specs, 3, &fields_wire[0], fields_wire.size()),
+                 isc::InvalidParameter);
+    // sum of field len > data len
+    EXPECT_THROW(RdataFields(specs, 3, &fields_wire[0],
+                             fields_wire.size() - 2),
+                 isc::InvalidParameter);
+}
+
+// Invalid input to the "from parameter" constructor: NULL vs length mismatch
+TEST_F(RdataFieldsTest, mismatchFieldLengthAndData) {
+    const unsigned char dummy_data = 0;
+    const RdataFields::FieldSpec dummy_spec(RdataFields::DATA, 1);
+
+    EXPECT_THROW(RdataFields(NULL, 1, &dummy_data, 1), isc::InvalidParameter);
+    EXPECT_THROW(RdataFields(&dummy_spec, 0, NULL, 0), isc::InvalidParameter);
+    EXPECT_THROW(RdataFields(&dummy_spec, 1, NULL, 1), isc::InvalidParameter);
+    EXPECT_THROW(RdataFields(NULL, 0, &dummy_data, 0), isc::InvalidParameter);
+}
+
+// Bogus input to getFieldSpec()
+TEST_F(RdataFieldsTest, getFieldSpecWithBadFieldId) {
+    const RdataFields fields_in_a(in::A("192.0.2.1"));
+    EXPECT_THROW(fields_in_a.getFieldSpec(1), isc::OutOfRange);
+}
+
+// Tests for unexpected methods in RdataFieldComposerTest.  Confirm
+// a call to these methods triggers an exception.  Expected methods are
+// tested via other tests above.
+class DummyRdata : public Rdata {
+public:
+    enum Mode { CLEAR, SKIP, TRIM };
+    explicit DummyRdata(Mode mode) : mode_(mode) {}
+    DummyRdata(const DummyRdata& source) : Rdata(), mode_(source.mode_) {}
+    virtual ~DummyRdata() {}
+    virtual void toWire(AbstractMessageRenderer& renderer) const {
+        // call the unexpected method corresponding to the test mode.
+        // method parameters don't matter.
+        switch (mode_) {
+        case CLEAR:
+            renderer.clear();
+            break;
+        case SKIP:
+            renderer.skip(2);
+            break;
+        case TRIM:
+            renderer.trim(2);
+            break;
+        }
+    }
+    
+    // These are defined only to make the compiler happy.  We don't use them
+    // for the test.
+    virtual string toText() const { return (""); }
+    virtual void toWire(OutputBuffer&) const {}
+    virtual int compare(const Rdata&) const { return (0); }
+private:
+    const int mode_;
+};
+
+TEST(RdataFieldComposerTest, unusedMethods) {
+    EXPECT_THROW(RdataFields(DummyRdata(DummyRdata::CLEAR)), isc::Unexpected);
+}
+}

+ 4 - 0
src/lib/dns/tests/testdata/Makefile.am

@@ -4,6 +4,8 @@ BUILT_SOURCES = edns_toWire1.wire edns_toWire2.wire edns_toWire3.wire
 BUILT_SOURCES += edns_toWire4.wire
 BUILT_SOURCES += message_fromWire10.wire message_fromWire11.wire
 BUILT_SOURCES += name_toWire5.wire name_toWire6.wire
+BUILT_SOURCES += rdatafields1.wire rdatafields2.wire rdatafields3.wire
+BUILT_SOURCES += rdatafields4.wire rdatafields5.wire rdatafields6.wire
 BUILT_SOURCES += rdata_nsec_fromWire4.wire rdata_nsec_fromWire5.wire
 BUILT_SOURCES += rdata_nsec_fromWire6.wire rdata_nsec_fromWire7.wire
 BUILT_SOURCES += rdata_nsec_fromWire8.wire rdata_nsec_fromWire9.wire
@@ -52,6 +54,8 @@ EXTRA_DIST += name_fromWire13 name_fromWire14
 EXTRA_DIST += name_toWire1 name_toWire2 name_toWire3 name_toWire4
 EXTRA_DIST += name_toWire5.spec name_toWire6.spec
 EXTRA_DIST += question_fromWire question_toWire1 question_toWire2
+EXTRA_DIST += rdatafields1.spec rdatafields2.spec rdatafields3.spec
+EXTRA_DIST += rdatafields4.spec rdatafields5.spec rdatafields6.spec
 EXTRA_DIST += rdata_cname_fromWire rdata_dname_fromWire rdata_dnskey_fromWire
 EXTRA_DIST += rdata_ds_fromWire rdata_in_a_fromWire rdata_in_aaaa_fromWire
 EXTRA_DIST += rdata_mx_fromWire rdata_mx_toWire1 rdata_mx_toWire2

+ 10 - 0
src/lib/dns/tests/testdata/rdatafields1.spec

@@ -0,0 +1,10 @@
+#
+# A sequence of names that could be compressed (but not compressed)
+#
+
+[custom]
+sections: name/1:name/2
+[name/1]
+name: www.example.com
+[name/2]
+name: example.com

+ 11 - 0
src/lib/dns/tests/testdata/rdatafields2.spec

@@ -0,0 +1,11 @@
+#
+# A sequence of names that can be compressed.
+#
+
+[custom]
+sections: name/1:name/2
+[name/1]
+name: www.example.com
+[name/2]
+name: ''
+pointer: 4

+ 11 - 0
src/lib/dns/tests/testdata/rdatafields3.spec

@@ -0,0 +1,11 @@
+#
+# TXT RDATA with multiple character-strings.
+#
+
+[custom]
+sections: txt
+[txt]
+nstring: 3
+string0: 'first string'
+string1: 'second string'
+string2: 'last string'

+ 7 - 0
src/lib/dns/tests/testdata/rdatafields4.spec

@@ -0,0 +1,7 @@
+#
+# Simple form of RRSIG (all fields use the default of generator script)
+#
+
+[custom]
+sections: rrsig
+[rrsig]

+ 12 - 0
src/lib/dns/tests/testdata/rdatafields5.spec

@@ -0,0 +1,12 @@
+#
+# Names and RDATA (RRSIG) with an incompressible name.  All names are
+# rendered without compression.
+#
+
+[custom]
+sections: name/1:rrsig:name/2
+[name/1]
+name: com
+[rrsig]
+[name/2]
+name: www.example.com

+ 13 - 0
src/lib/dns/tests/testdata/rdatafields6.spec

@@ -0,0 +1,13 @@
+#
+# Names and RDATA (RRSIG) with an incompressible name.  The name in RRSIG
+# isn't compressed, but it's used as the compression target.
+#
+
+[custom]
+sections: name/1:rrsig:name/2
+[name/1]
+name: com
+[rrsig]
+[name/2]
+name: www
+pointer: 25

+ 2 - 2
src/lib/nsas/tests/nsas_test.h

@@ -125,7 +125,7 @@ public:
     virtual void toWire(OutputBuffer& buffer) const;
 
     /// \brief render the \Rdata in the wire format to a \c MessageRenderer
-    virtual void toWire(MessageRenderer& renderer) const;
+    virtual void toWire(AbstractMessageRenderer& renderer) const;
     
     /// \brief Comparison Method
     virtual int compare(const Rdata& other) const;
@@ -141,7 +141,7 @@ void RdataTest<T>::toWire(OutputBuffer&) const {
 }
 
 template <typename T>
-void RdataTest<T>::toWire(MessageRenderer&) const {
+void RdataTest<T>::toWire(AbstractMessageRenderer&) const {
 }
 
 template <typename T>

+ 1 - 1
src/lib/python/isc/notify/tests/Makefile.am

@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS

+ 0 - 2
src/lib/util/unittests/Makefile.am

@@ -3,7 +3,5 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 lib_LTLIBRARIES = libutil_unittests.la
 libutil_unittests_la_SOURCES = fork.h fork.cc
-libutil_unittests_la_LIBADD = \
-	$(top_builddir)/src/lib/util/io/libutil_io.la
 
 CLEANFILES = *.gcno *.gcda