BIND 10 master, updated. e87e34cd12ac1e4b70bd55ea2fcae1bfd9ccfeeb Merge branch 'master' of git+ssh://bind10.isc.org/var/bind10/git/bind10

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Apr 19 21:43:01 UTC 2011


The branch, master has been updated
       via  e87e34cd12ac1e4b70bd55ea2fcae1bfd9ccfeeb (commit)
       via  9ce716d340a89f0212302af25ae75ba68bf69b75 (commit)
       via  29025aa582a5d8771619e4de08c3c3fe4f0d0fd5 (commit)
       via  f65cb09eef192590929e246311b33aa964c5a1a6 (commit)
       via  cf05a54b3c950aef906e577be39d792a17e53796 (commit)
       via  6e2078ecc6e3f91676631b3b3f9c4be7b5584853 (commit)
       via  3384859c8e15572519887abeb44d3ff295dbd178 (commit)
       via  a140e9b97e208ce30d0c3fb51f14dd248f307b1d (commit)
       via  87c6ef9942c9edcb37aa811e1c20357e6bd6bc4b (commit)
       via  95e81e5a713c145307034724994bd64bf35455ff (commit)
       via  34571fc51116f3e476e84c20e7fb7ba59393dab8 (commit)
       via  a125177e363f66375aca5af53649b8e369af58e6 (commit)
       via  69e7fd6b5782434c809f90d8060e148318ba3c7e (commit)
       via  56b5bef538df18c95ad5df3aaf7ebff4464de7c6 (commit)
       via  22e21e72190a88d50dcca4c7ebf0394074d0adc3 (commit)
       via  cc259f459b92c5e5f5f374c99c7f1cde9900544b (commit)
       via  ba422c4d7e15abf64bc7e466d470243d3259e675 (commit)
       via  ff5a80a31953f5aa6f746e6c4e919519926da717 (commit)
       via  e04950d346768779e32c83268b6b68e0838680eb (commit)
      from  4b5b7b0c63830075ce2b063c23a5efa5fd2b9cc7 (commit)

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

- Log -----------------------------------------------------------------
commit e87e34cd12ac1e4b70bd55ea2fcae1bfd9ccfeeb
Merge: 9ce716d340a89f0212302af25ae75ba68bf69b75 4b5b7b0c63830075ce2b063c23a5efa5fd2b9cc7
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Tue Apr 19 23:29:34 2011 +0200

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

commit 9ce716d340a89f0212302af25ae75ba68bf69b75
Merge: 2a27eff879ad941873586006fc2b944fd7c9471d 29025aa582a5d8771619e4de08c3c3fe4f0d0fd5
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Tue Apr 19 23:29:25 2011 +0200

    Merge branch 'work/rdatafields'
    
    Conflicts:
    	src/lib/dns/gen-rdatacode.py.in
    	src/lib/dns/messagerenderer.cc
    	src/lib/dns/messagerenderer.h
    	src/lib/dns/name.h
    	src/lib/dns/rdata.h
    	src/lib/dns/rrtype-placeholder.h

commit 29025aa582a5d8771619e4de08c3c3fe4f0d0fd5
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Tue Apr 19 22:50:48 2011 +0200

    [trac404] Code style

commit f65cb09eef192590929e246311b33aa964c5a1a6
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Mon Apr 18 12:30:24 2011 +0200

    [trac404] Cleanup some getBuffer()s
    
    They are not needed on many places, so remove them.

commit cf05a54b3c950aef906e577be39d792a17e53796
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Mon Apr 18 12:22:50 2011 +0200

    [trac404] Spec size methods
    
    One renamed to be more consistent to getFieldSpecDataSize(), other added
    (getFieldSpecCount()).

commit 6e2078ecc6e3f91676631b3b3f9c4be7b5584853
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Wed Apr 13 13:56:10 2011 +0200

    [trac404] Dox update

commit 3384859c8e15572519887abeb44d3ff295dbd178
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Wed Apr 13 13:48:27 2011 +0200

    [trac404] Make the buffer private

commit a140e9b97e208ce30d0c3fb51f14dd248f307b1d
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Wed Apr 13 13:02:44 2011 +0200

    [trac404] Unify the interface a little bit
    
    And hint more by a little bit that the user isn't really expected to dig
    inside the data.

commit 87c6ef9942c9edcb37aa811e1c20357e6bd6bc4b
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Tue Apr 12 19:57:43 2011 +0200

    [trac404] Spelling fixes

commit 95e81e5a713c145307034724994bd64bf35455ff
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Tue Apr 12 19:53:21 2011 +0200

    [trac404] Note about design

commit 34571fc51116f3e476e84c20e7fb7ba59393dab8
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Wed Apr 6 10:48:51 2011 +0200

    [trac404] TODO note for future optimisation

commit a125177e363f66375aca5af53649b8e369af58e6
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Wed Apr 6 10:38:50 2011 +0200

    [trac404] Remove duplicate code

commit 69e7fd6b5782434c809f90d8060e148318ba3c7e
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Mon Apr 4 19:14:14 2011 +0200

    [trac404] Small cleanup & comment update

commit 56b5bef538df18c95ad5df3aaf7ebff4464de7c6
Merge: 22e21e72190a88d50dcca4c7ebf0394074d0adc3 62a61edffac3ebdd91fec693fe2c1a94785ddb25
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Mon Apr 4 17:36:26 2011 +0200

    Merge branch 'master' into work/serialize
    
    Conflicts:
    	src/lib/dns/messagerenderer.h
    	src/lib/dns/rdata/ch_3/a_1.cc
    	src/lib/dns/rdata/generic/opt_41.cc
    	src/lib/dns/rdata/hs_4/a_1.cc

commit 22e21e72190a88d50dcca4c7ebf0394074d0adc3
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Thu Nov 4 12:55:46 2010 +0000

    added a simple benchmark test for the RdataFields class compared to Rdata.
    
    git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac404@3447 e5f2f494-b856-4b98-b285-d166d9295462

commit cc259f459b92c5e5f5f374c99c7f1cde9900544b
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Thu Nov 4 12:55:39 2010 +0000

    an editorial fix: removed redundant white spaces
    
    git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac404@3446 e5f2f494-b856-4b98-b285-d166d9295462

commit ba422c4d7e15abf64bc7e466d470243d3259e675
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Thu Nov 4 12:55:32 2010 +0000

    editorial fix to the class document: remove an unnecessary doxygen tag in \code block.
    
    git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac404@3445 e5f2f494-b856-4b98-b285-d166d9295462

commit ff5a80a31953f5aa6f746e6c4e919519926da717
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Nov 3 14:38:06 2010 +0000

    initial implementation of trac#404: in-memory serialized representation of RDATA
    
    
    git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac404@3430 e5f2f494-b856-4b98-b285-d166d9295462

commit e04950d346768779e32c83268b6b68e0838680eb
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Nov 3 14:35:11 2010 +0000

    work/review branch for trac#404
    
    
    git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac404@3429 e5f2f494-b856-4b98-b285-d166d9295462

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

Summary of changes:
 configure.ac                                       |    1 +
 src/lib/dns/Makefile.am                            |    3 +-
 src/lib/dns/benchmarks/Makefile.am                 |   17 +
 src/lib/dns/benchmarks/README                      |   10 +
 .../benchmarks/benchmarkdata/rdatarender_data_com  |   32 ++
 .../benchmarkdata/rdatarender_data_nxdomain        |   10 +
 .../benchmarks/benchmarkdata/rdatarender_data_org  |   22 +
 src/lib/dns/benchmarks/rdatarender_bench.cc        |  188 +++++++++
 src/lib/dns/gen-rdatacode.py.in                    |    4 +-
 src/lib/dns/messagerenderer.cc                     |   72 +---
 src/lib/dns/messagerenderer.h                      |  226 +++++++----
 src/lib/dns/name.cc                                |    3 +-
 src/lib/dns/name.h                                 |    4 +-
 src/lib/dns/rdata.cc                               |    2 +-
 src/lib/dns/rdata.h                                |    6 +-
 src/lib/dns/rdata/any_255/tsig_250.cc              |    4 +-
 src/lib/dns/rdata/ch_3/a_1.cc                      |    2 +-
 src/lib/dns/rdata/generic/cname_5.cc               |    2 +-
 src/lib/dns/rdata/generic/dname_39.cc              |    2 +-
 src/lib/dns/rdata/generic/dnskey_48.cc             |    2 +-
 src/lib/dns/rdata/generic/ds_43.cc                 |    2 +-
 src/lib/dns/rdata/generic/mx_15.cc                 |    2 +-
 src/lib/dns/rdata/generic/ns_2.cc                  |    2 +-
 src/lib/dns/rdata/generic/nsec3_50.cc              |    2 +-
 src/lib/dns/rdata/generic/nsec3param_51.cc         |    2 +-
 src/lib/dns/rdata/generic/nsec_47.cc               |    2 +-
 src/lib/dns/rdata/generic/opt_41.cc                |    2 +-
 src/lib/dns/rdata/generic/ptr_12.cc                |    2 +-
 src/lib/dns/rdata/generic/rp_17.cc                 |    2 +-
 src/lib/dns/rdata/generic/rrsig_46.cc              |    2 +-
 src/lib/dns/rdata/generic/soa_6.cc                 |    2 +-
 src/lib/dns/rdata/generic/txt_16.cc                |    2 +-
 src/lib/dns/rdata/hs_4/a_1.cc                      |    2 +-
 src/lib/dns/rdata/in_1/a_1.cc                      |    2 +-
 src/lib/dns/rdata/in_1/aaaa_28.cc                  |    2 +-
 src/lib/dns/rdata/template.cc                      |    2 +-
 src/lib/dns/rdatafields.cc                         |  223 ++++++++++
 src/lib/dns/rdatafields.h                          |  427 ++++++++++++++++++++
 src/lib/dns/rrtype-placeholder.h                   |    4 +-
 src/lib/dns/rrtype.cc                              |    2 +-
 src/lib/dns/tests/Makefile.am                      |    1 +
 src/lib/dns/tests/rdatafields_unittest.cc          |  380 +++++++++++++++++
 src/lib/dns/tests/testdata/Makefile.am             |    4 +
 src/lib/dns/tests/testdata/rdatafields1.spec       |   10 +
 src/lib/dns/tests/testdata/rdatafields2.spec       |   11 +
 src/lib/dns/tests/testdata/rdatafields3.spec       |   11 +
 src/lib/dns/tests/testdata/rdatafields4.spec       |    7 +
 src/lib/dns/tests/testdata/rdatafields5.spec       |   12 +
 src/lib/dns/tests/testdata/rdatafields6.spec       |   13 +
 src/lib/nsas/tests/nsas_test.h                     |    4 +-
 50 files changed, 1581 insertions(+), 172 deletions(-)
 create mode 100644 src/lib/dns/benchmarks/Makefile.am
 create mode 100644 src/lib/dns/benchmarks/README
 create mode 100644 src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_com
 create mode 100644 src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_nxdomain
 create mode 100644 src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_org
 create mode 100644 src/lib/dns/benchmarks/rdatarender_bench.cc
 create mode 100644 src/lib/dns/rdatafields.cc
 create mode 100644 src/lib/dns/rdatafields.h
 create mode 100644 src/lib/dns/tests/rdatafields_unittest.cc
 create mode 100644 src/lib/dns/tests/testdata/rdatafields1.spec
 create mode 100644 src/lib/dns/tests/testdata/rdatafields2.spec
 create mode 100644 src/lib/dns/tests/testdata/rdatafields3.spec
 create mode 100644 src/lib/dns/tests/testdata/rdatafields4.spec
 create mode 100644 src/lib/dns/tests/testdata/rdatafields5.spec
 create mode 100644 src/lib/dns/tests/testdata/rdatafields6.spec

-----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index e072f89..81df476 100644
--- a/configure.ac
+++ b/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
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index 9b42e8c..5a7151e 100644
--- a/src/lib/dns/Makefile.am
+++ b/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
diff --git a/src/lib/dns/benchmarks/Makefile.am b/src/lib/dns/benchmarks/Makefile.am
new file mode 100644
index 0000000..8645385
--- /dev/null
+++ b/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)
diff --git a/src/lib/dns/benchmarks/README b/src/lib/dns/benchmarks/README
new file mode 100644
index 0000000..7119c13
--- /dev/null
+++ b/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_*.
diff --git a/src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_com b/src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_com
new file mode 100644
index 0000000..12155a6
--- /dev/null
+++ b/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
diff --git a/src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_nxdomain b/src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_nxdomain
new file mode 100644
index 0000000..eb14b5f
--- /dev/null
+++ b/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=
diff --git a/src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_org b/src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_org
new file mode 100644
index 0000000..17b80d2
--- /dev/null
+++ b/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
diff --git a/src/lib/dns/benchmarks/rdatarender_bench.cc b/src/lib/dns/benchmarks/rdatarender_bench.cc
new file mode 100644
index 0000000..d1fb0f2
--- /dev/null
+++ b/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);
+}
diff --git a/src/lib/dns/gen-rdatacode.py.in b/src/lib/dns/gen-rdatacode.py.in
index ff56735..b3c8da2 100755
--- a/src/lib/dns/gen-rdatacode.py.in
+++ b/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
diff --git a/src/lib/dns/messagerenderer.cc b/src/lib/dns/messagerenderer.cc
index 9ae2d34..6438fb0 100644
--- a/src/lib/dns/messagerenderer.cc
+++ b/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();
+}
+
 }
 }
diff --git a/src/lib/dns/messagerenderer.h b/src/lib/dns/messagerenderer.h
index 4d51eea..52d9245 100644
--- a/src/lib/dns/messagerenderer.h
+++ b/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_;
diff --git a/src/lib/dns/name.cc b/src/lib/dns/name.cc
index 8269b40..4cd0b2b 100644
--- a/src/lib/dns/name.cc
+++ b/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);
 }
 
diff --git a/src/lib/dns/name.h b/src/lib/dns/name.h
index f58bb93..4ff7fe5 100644
--- a/src/lib/dns/name.h
+++ b/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.
diff --git a/src/lib/dns/rdata.cc b/src/lib/dns/rdata.cc
index a1ba063..27496a2 100644
--- a/src/lib/dns/rdata.cc
+++ b/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());
 }
 
diff --git a/src/lib/dns/rdata.h b/src/lib/dns/rdata.h
index c8064f9..afcf4b3 100644
--- a/src/lib/dns/rdata.h
+++ b/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;
     //@}
 
     ///
diff --git a/src/lib/dns/rdata/any_255/tsig_250.cc b/src/lib/dns/rdata/any_255/tsig_250.cc
index 228b6c2..f2bd7ad 100644
--- a/src/lib/dns/rdata/any_255/tsig_250.cc
+++ b/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().
diff --git a/src/lib/dns/rdata/ch_3/a_1.cc b/src/lib/dns/rdata/ch_3/a_1.cc
index 359624d..65378a1 100644
--- a/src/lib/dns/rdata/ch_3/a_1.cc
+++ b/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
 }
 
diff --git a/src/lib/dns/rdata/generic/cname_5.cc b/src/lib/dns/rdata/generic/cname_5.cc
index 6a3447d..5bb0aea 100644
--- a/src/lib/dns/rdata/generic/cname_5.cc
+++ b/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_);
 }
 
diff --git a/src/lib/dns/rdata/generic/dname_39.cc b/src/lib/dns/rdata/generic/dname_39.cc
index 7cd22bf..165c4a6 100644
--- a/src/lib/dns/rdata/generic/dname_39.cc
+++ b/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_);
 }
 
diff --git a/src/lib/dns/rdata/generic/dnskey_48.cc b/src/lib/dns/rdata/generic/dnskey_48.cc
index 70521b7..e0f5461 100644
--- a/src/lib/dns/rdata/generic/dnskey_48.cc
+++ b/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_);
diff --git a/src/lib/dns/rdata/generic/ds_43.cc b/src/lib/dns/rdata/generic/ds_43.cc
index df7fe5e..1b48456 100644
--- a/src/lib/dns/rdata/generic/ds_43.cc
+++ b/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_);
diff --git a/src/lib/dns/rdata/generic/mx_15.cc b/src/lib/dns/rdata/generic/mx_15.cc
index 8999384..4765222 100644
--- a/src/lib/dns/rdata/generic/mx_15.cc
+++ b/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_);
 }
diff --git a/src/lib/dns/rdata/generic/ns_2.cc b/src/lib/dns/rdata/generic/ns_2.cc
index dcdbcd2..631da9d 100644
--- a/src/lib/dns/rdata/generic/ns_2.cc
+++ b/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_);
 }
 
diff --git a/src/lib/dns/rdata/generic/nsec3_50.cc b/src/lib/dns/rdata/generic/nsec3_50.cc
index 4be1a73..3bd0bb2 100644
--- a/src/lib/dns/rdata/generic/nsec3_50.cc
+++ b/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_);
diff --git a/src/lib/dns/rdata/generic/nsec3param_51.cc b/src/lib/dns/rdata/generic/nsec3param_51.cc
index 7fcdacf..49f666b 100644
--- a/src/lib/dns/rdata/generic/nsec3param_51.cc
+++ b/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_);
diff --git a/src/lib/dns/rdata/generic/nsec_47.cc b/src/lib/dns/rdata/generic/nsec_47.cc
index 07b6486..93b8b5f 100644
--- a/src/lib/dns/rdata/generic/nsec_47.cc
+++ b/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());
 }
diff --git a/src/lib/dns/rdata/generic/opt_41.cc b/src/lib/dns/rdata/generic/opt_41.cc
index fc80832..62cfc17 100644
--- a/src/lib/dns/rdata/generic/opt_41.cc
+++ b/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.
 }
 
diff --git a/src/lib/dns/rdata/generic/ptr_12.cc b/src/lib/dns/rdata/generic/ptr_12.cc
index 63585b0..86ddeb4 100644
--- a/src/lib/dns/rdata/generic/ptr_12.cc
+++ b/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_);
 }
 
diff --git a/src/lib/dns/rdata/generic/rp_17.cc b/src/lib/dns/rdata/generic/rp_17.cc
index c1c91ef..b8b2ba2 100644
--- a/src/lib/dns/rdata/generic/rp_17.cc
+++ b/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);
diff --git a/src/lib/dns/rdata/generic/rrsig_46.cc b/src/lib/dns/rdata/generic/rrsig_46.cc
index 68e4884..0c82406 100644
--- a/src/lib/dns/rdata/generic/rrsig_46.cc
+++ b/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_);
diff --git a/src/lib/dns/rdata/generic/soa_6.cc b/src/lib/dns/rdata/generic/soa_6.cc
index 58d6da4..7ecd84f 100644
--- a/src/lib/dns/rdata/generic/soa_6.cc
+++ b/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_));
diff --git a/src/lib/dns/rdata/generic/txt_16.cc b/src/lib/dns/rdata/generic/txt_16.cc
index d1d4d89..ac2ba8a 100644
--- a/src/lib/dns/rdata/generic/txt_16.cc
+++ b/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)
diff --git a/src/lib/dns/rdata/hs_4/a_1.cc b/src/lib/dns/rdata/hs_4/a_1.cc
index 359624d..65378a1 100644
--- a/src/lib/dns/rdata/hs_4/a_1.cc
+++ b/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
 }
 
diff --git a/src/lib/dns/rdata/in_1/a_1.cc b/src/lib/dns/rdata/in_1/a_1.cc
index 1d7b8de..fa46f90 100644
--- a/src/lib/dns/rdata/in_1/a_1.cc
+++ b/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_));
 }
 
diff --git a/src/lib/dns/rdata/in_1/aaaa_28.cc b/src/lib/dns/rdata/in_1/aaaa_28.cc
index 1125a59..e9fc122 100644
--- a/src/lib/dns/rdata/in_1/aaaa_28.cc
+++ b/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_));
 }
 
diff --git a/src/lib/dns/rdata/template.cc b/src/lib/dns/rdata/template.cc
index 157ea90..d9f08ee 100644
--- a/src/lib/dns/rdata/template.cc
+++ b/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
diff --git a/src/lib/dns/rdatafields.cc b/src/lib/dns/rdatafields.cc
new file mode 100644
index 0000000..d3f282b
--- /dev/null
+++ b/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
diff --git a/src/lib/dns/rdatafields.h b/src/lib/dns/rdatafields.h
new file mode 100644
index 0000000..e33bcd7
--- /dev/null
+++ b/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:
diff --git a/src/lib/dns/rrtype-placeholder.h b/src/lib/dns/rrtype-placeholder.h
index 69437f5..1cb028c 100644
--- a/src/lib/dns/rrtype-placeholder.h
+++ b/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
diff --git a/src/lib/dns/rrtype.cc b/src/lib/dns/rrtype.cc
index e234fad..af077d4 100644
--- a/src/lib/dns/rrtype.cc
+++ b/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_);
 }
 
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index b345411..fdd6e1e 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/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
diff --git a/src/lib/dns/tests/rdatafields_unittest.cc b/src/lib/dns/tests/rdatafields_unittest.cc
new file mode 100644
index 0000000..d619220
--- /dev/null
+++ b/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);
+}
+}
diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am
index 1dc1c00..31771bb 100644
--- a/src/lib/dns/tests/testdata/Makefile.am
+++ b/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
diff --git a/src/lib/dns/tests/testdata/rdatafields1.spec b/src/lib/dns/tests/testdata/rdatafields1.spec
new file mode 100644
index 0000000..6e105fb
--- /dev/null
+++ b/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
diff --git a/src/lib/dns/tests/testdata/rdatafields2.spec b/src/lib/dns/tests/testdata/rdatafields2.spec
new file mode 100644
index 0000000..920dc95
--- /dev/null
+++ b/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
diff --git a/src/lib/dns/tests/testdata/rdatafields3.spec b/src/lib/dns/tests/testdata/rdatafields3.spec
new file mode 100644
index 0000000..b37fca3
--- /dev/null
+++ b/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'
diff --git a/src/lib/dns/tests/testdata/rdatafields4.spec b/src/lib/dns/tests/testdata/rdatafields4.spec
new file mode 100644
index 0000000..24b59aa
--- /dev/null
+++ b/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]
diff --git a/src/lib/dns/tests/testdata/rdatafields5.spec b/src/lib/dns/tests/testdata/rdatafields5.spec
new file mode 100644
index 0000000..2c78282
--- /dev/null
+++ b/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
diff --git a/src/lib/dns/tests/testdata/rdatafields6.spec b/src/lib/dns/tests/testdata/rdatafields6.spec
new file mode 100644
index 0000000..f9f0da1
--- /dev/null
+++ b/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
diff --git a/src/lib/nsas/tests/nsas_test.h b/src/lib/nsas/tests/nsas_test.h
index 0fcca39..033df81 100644
--- a/src/lib/nsas/tests/nsas_test.h
+++ b/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>




More information about the bind10-changes mailing list