BIND 10 trac2222, updated. 1231dd5b95fc8eaab5d1ada38334c5e20e9e8442 [2222] Merge branch 'trac2158' of ssh://git.bind10.isc.org/var/bind10/git/bind10 into trac2222

BIND 10 source code commits bind10-changes at lists.isc.org
Fri Aug 31 07:10:07 UTC 2012


The branch, trac2222 has been updated
       via  1231dd5b95fc8eaab5d1ada38334c5e20e9e8442 (commit)
       via  23070d2189c17a5cccee6c84267d0453d3c96226 (commit)
       via  9c938d158026dcc6d41ecdbf054dcba202ca9c4f (commit)
       via  f2f058d5a3ee567c0be22f758a02abacc159e927 (commit)
       via  b67bc1ca8694f7f0fab51b645ad1227c05866e11 (commit)
       via  4275cf4bdb01aecee2ef8beb52d40c320e2016b0 (commit)
       via  0f43c975990d11c7f0a071f3bd29836d28e08f4a (commit)
       via  5f5dd8f7dcfbb4423d53fe87ced200e4fdc9bef5 (commit)
       via  8fd8ed2aed8f309b655661389cf6ae043a854fe5 (commit)
       via  d5db8b95c963cb2d493b24dda0380f71a4df67f2 (commit)
       via  307a588d6ec0aa2adf2063fa4943bba0c63fc2c7 (commit)
       via  e69183d61f5827e70f6ea47129d3cd6fee9d7561 (commit)
       via  9a7b74dbe965302b635d21a6e8c5ce76d0b350d4 (commit)
       via  d659abdd9f3f369a29830831297f64484ac7b051 (commit)
       via  5d1e19947ce5875cd950f1b1750dad5076909446 (commit)
       via  0aeb26af21dc9828af28565e4da127b4d898bbdd (commit)
       via  558b2dbe02c6de2cac63ef44e1eeab12600aa99a (commit)
       via  7985ae6830540ce6f6f6c3d6d71b2876c4e7315e (commit)
       via  44f92aeb7817fec9534698ac659d898d1a35acbb (commit)
       via  9ebbd0cc6b9d20b1b67bf8d7161ba4b3239d5482 (commit)
       via  870aa95f8b1c608468021d7666f24078cc78d5be (commit)
       via  78eeb8706770caf3f25c166b0413c9a324be1de3 (commit)
       via  ee81b663d85fbc1a8f74e8206a1da8e0a54d604a (commit)
       via  8052c97a801dd4d46241ce48744b0f2a69edd801 (commit)
       via  732703856c60f3934403e666df86fb105b3f69ce (commit)
       via  fcde1f32cd4925a3c5b4c061a8b79fb83e2406af (commit)
       via  6e500e577f4fd8d6be953c15561f20c5ef1a784e (commit)
       via  2497f5480351f20ebb4f1ae72e9dbfc36b9ea582 (commit)
       via  aa14f630842559ebe3fb682061a1be805cd23811 (commit)
       via  7443b1d25391782d488ae94f3d36dd65f54d948f (commit)
       via  4527f307386bc991e9f3bef201d4a6f1c0ab0dfb (commit)
       via  22d7925d2a9a7de2544c02bec4096783597274be (commit)
       via  25db0405ba8aa808b9481b32c3c229029870ea28 (commit)
       via  4bd4bf928475f7fe9181fc71f0d87546b7b8a760 (commit)
       via  8c9a5e0ce95663f27736cacf432c5ff6ece35a06 (commit)
       via  5e463e914a5442a5ab72c1a7aed0d8eb103a7dda (commit)
       via  dd15647393e777419c3f0783ed4416cdfe907a82 (commit)
       via  6e2de8ba7a4a1582802ba3a03b02e286ecaf50d1 (commit)
       via  56de9af8c7a03c5daf92e7460191f6d95b1bf917 (commit)
       via  79f1c5b644db4040b975750410363e31b56f480a (commit)
       via  1159e5bb601361ce0b4c9a8ed43aa92828b21744 (commit)
       via  4ecc4e3295bddf87f14f2e60fca98ceac05fbf1d (commit)
       via  abcd7d97d6caad3c68c4088cc58e8116ba8ae576 (commit)
       via  2fabaed6bb37dbefb1c394554971a2a27328890c (commit)
       via  11af4c1f3ed1af14fe6cc75a73f9f32b37a79b00 (commit)
       via  33ab70a67c5cc9f340faadabe7a3003b5c959863 (commit)
       via  a173297fd323767a6a37dbf7acba00f54b10ccdc (commit)
       via  3afbf54cd0bb69ea6b56015735825d42dd4dfd15 (commit)
       via  552c5782d796ec0c1091af6dd8212f23b9775af9 (commit)
       via  3c20e037be36b32640289bb61f45d555649bad12 (commit)
       via  32dd44a999662e0021389bac3bfcc581fc00c00d (commit)
       via  a8d00eae92e24cb76e197e1fc1263ea11c9181bd (commit)
       via  aa708cee10b9ca277966df0d490606ae56a00fff (commit)
       via  23492e5eb5cba0ad07253db37f69b3e3e32c4299 (commit)
       via  50ab96b1412fe65d26184603e0a96650d9e9c6cc (commit)
       via  5b27cee6f2b2ea3199700f2358ab38655c60e5eb (commit)
       via  b97f714d59f1990b62a01e5ca0c5f1c95834ed7c (commit)
       via  d9e1e79ffff5bf77517474d811ed7cfece349559 (commit)
       via  220c3ff25ab6bb3e0cd4f4888bdfdaadc87a2738 (commit)
       via  db4962ad778838b6872fa1516a3e2e6c3e2579be (commit)
       via  83979200b23f48735babe19b6359660222b2c525 (commit)
       via  a57188382f9187c8096f198cf2fbf281c5419f2b (commit)
       via  a22851178b9359cdb68c0284ca9945d325955ebe (commit)
       via  70d316e7900864491e1a9ca83aaf2659a8f0aff1 (commit)
       via  fb6be89241c386c904d45fbd3135dec41caf30b8 (commit)
       via  1e8e2101a48216215a50c548ee54c3cad4872aeb (commit)
       via  9886e2d5b3f70e59d83af08db4ad00c4a8354a85 (commit)
       via  88e51fd004645f182dd631fb42376d53b54cef80 (commit)
       via  0a0497e8f5ff374f7488f084e228c321ac097abf (commit)
       via  b93c6e1daacfdd32d6952987186576ec3b01a534 (commit)
       via  0b8a3ffb1876c789a1694b627eaf370bb66c19d5 (commit)
       via  94d0fd242d1b56ac9afe56e8f1240084946556c5 (commit)
       via  1fbbeb35aae24cab6e48169517e6aafadb9dc97d (commit)
       via  43dd02a2bdbeef1d4c933d3b059dadc3d3ca0043 (commit)
       via  1cc1a42e7557a89a4a8e3e85f6ab0e37c0530ded (commit)
       via  a68e90427f5459d4995a3e8f26feea83804a7cc3 (commit)
       via  76c6361071fcb50a3ef06b7072f840ca8bbdbe26 (commit)
       via  1414b51d75e9f194f3df0d7f342d445fdab265ad (commit)
       via  3ba9a889ca3a866bd4602b2c5c30b62ba20793ee (commit)
       via  aacf7cb74062a0e752e407d49d891cbbfc44ed86 (commit)
       via  7f00956d43e793624bddf52454a8eedf91376e69 (commit)
       via  9a13449fd43574629557b2b0aa67592a58729fc5 (commit)
       via  2a22df22a13e01d9a18a02e65bf1334b8ac3b58a (commit)
       via  c6bdb4b4c1585232edf057e27b632cfeda4c38cd (commit)
       via  e21efca5f30a849289a3daa44d2f9b3526fd3e86 (commit)
       via  042e6179638fccfcc7ceaebbaf89ad830bfa3c56 (commit)
       via  af5cb72b4dccefd265437fd7e5b8fc9cb9ad1c1b (commit)
       via  495845ba4ffa7889c4439074958a62b0106b0923 (commit)
       via  c051d7d9e18ee85c249e4619a2f1caa65f9c55d4 (commit)
       via  e50a2637c7c63aa2417c6210012c2a07d5e4cbb9 (commit)
       via  52f1d2c35bb10b9d5d3f7d055ce39cac3b2423b8 (commit)
       via  18324f005be1053dd9be7ccce0c5a6182fcb144c (commit)
       via  6679f424fbcf59fee039531ef213eb1f8488fcff (commit)
       via  98a0dad2ad8c342bc6ac8f0fb36e43c695c89524 (commit)
       via  02c38b82542f7b8aaab6d5d5f957fdfb6de6a8f5 (commit)
       via  ea17ff0dcbe916339f7ad72b8125a8bc1d2543bf (commit)
       via  3128629ce0cf4ae59b249e5fb4cbc0873b7ace7f (commit)
       via  e7a479a7b022033fea15c1fb642ecbbc1b769e9b (commit)
       via  7923aa4c6b5f21d953d837c3373adfcb84ce5ea9 (commit)
       via  f06012941410d267b59e83812168cb0254f808ef (commit)
       via  6ca7b0d6bcc6a0c72033636a460d96592f6b3dc7 (commit)
       via  ef85853043e2d5f1fe3c2d494cd4af3b553d88fa (commit)
       via  4ecd5fbc083dfb1a7fed1f1f4e182a8dc2a59f7a (commit)
       via  cdfbc3523e1b6c290ed56fea530276e1119f0d4d (commit)
       via  bddce14fdd3f92879bad0fa6f88772e5575b7c02 (commit)
       via  04c050cb0c48cfb1b4e0f0303c098e47d5da7c2b (commit)
       via  d057be8fd1bb48ad6ccead205fd81056c8289713 (commit)
       via  525a74df1749fc36b12047d0e8109519e5d0088c (commit)
       via  9c03bc82344de6539ba48a197add60a0c9bdcff4 (commit)
       via  4e3a85f9ba40aa0934e84eb54bf518733e56a504 (commit)
       via  a851fd51275ea398dd819fbaca703524f417c281 (commit)
       via  8cc84c69fa53568b69491d6374eb46abeb76621d (commit)
       via  5438673961a060a71bde212f93e4cc4b14eda269 (commit)
       via  4c8609122e54e7a4d2d20057311a0b68e2bd5a49 (commit)
       via  c4500a6a000f2ae6903f26675f88c8680ee74ad8 (commit)
       via  ace794f6a528370d1533790512836870a89b4344 (commit)
       via  b59e0b908ecbf12daaee9b66d3cd77b7a8554b04 (commit)
       via  d2a9f546a9e5d436fcf0e5e02b12f459854f8fd1 (commit)
       via  7c0e1396fab1be6b6021d299c79c275335488944 (commit)
       via  f4833d5074447b1fc5aaca8d705fd36f7a49244e (commit)
       via  2fd528da04c3a154ca0b787c7f1b537d4bd869db (commit)
       via  0596cd9d23b29e2ba6096838044de6994d6b06ff (commit)
       via  879a73596bebf9a46b6667a0cdb8c7493c50eebb (commit)
       via  e6152707a2bb03f93d4c72e4122eb0020b8949ae (commit)
       via  c097d3c770c379cd3bccf310a251dd517e040a43 (commit)
       via  6ddaa4996de0032c41b2e32fbe351ccb3b0f20b6 (commit)
       via  ba9f6851070614e9b5e23f43267305a3fe455d02 (commit)
       via  e8849df60c9347f76b33903ca115a1f77f5e0282 (commit)
       via  a7df756bee32ebd4be5a010659b68c71df152af7 (commit)
       via  e3fa6f452eed5632904fc77e2e33da560e721c25 (commit)
       via  6753f9e88f5515d8896ad1479a6eb3634b32882f (commit)
       via  c9b0c40018c2007309b68af9cc78b474a28258ef (commit)
       via  0019f64f94e6c449b8d702f65b3fd0453aa56633 (commit)
       via  87830ae57ed3fd5da5567a30bf2a8c33132ba955 (commit)
       via  8131de02fde6e3948a765a37b660e276d0afa146 (commit)
       via  6bcd73aa1c0ef3f2ea13b6aa87f003db437d55f0 (commit)
       via  45d9762c6dfed8746f361fa01700899947154363 (commit)
       via  fdd121bda3753d43f9e322d79b5ad6e8b49064e1 (commit)
       via  c49d19491e9d93a5908afe667beeba4bcfcc8af3 (commit)
       via  909df7ac12941253014a4dafb33c4574989999da (commit)
       via  6694e40b41ac8909ead7ec6e59bc23ca310573d7 (commit)
       via  256771158ce922d32b0341cfb9109c83b4bbc45a (commit)
       via  fdb51ca68f97d1aedf06b91a48b8c96733e20bb0 (commit)
       via  305f3328e78cb462a7586421cc862a9fe9f327c2 (commit)
       via  57ebe0199af9aa5245bc6f7a7cf98c0f780a5b2c (commit)
       via  4b92c8b2f217dcd9706d2e1ff02ba2550d7fd87e (commit)
       via  2a95f12277a41b38f151660a6fc313e7a98f8e73 (commit)
       via  cd1a4d1452deaa95e341d867cde73b82ef8e3804 (commit)
       via  11cfdc151d5d949cf8c6fd5b05f888225b6a9d9d (commit)
      from  8f822cc5bd3c3f3394a08a0f3f3843a5abd4ae8f (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 1231dd5b95fc8eaab5d1ada38334c5e20e9e8442
Merge: 8f822cc 23070d2
Author: Naoki Kambe <kambe at jprs.co.jp>
Date:   Fri Aug 31 16:04:22 2012 +0900

    [2222] Merge branch 'trac2158' of ssh://git.bind10.isc.org/var/bind10/git/bind10 into trac2222
    
    Conflicts:
    	ChangeLog

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

Summary of changes:
 ChangeLog                                          |   17 +-
 configure.ac                                       |    1 +
 src/bin/auth/query.cc                              |   20 +-
 src/bin/auth/query.h                               |   12 +-
 src/bin/auth/tests/query_unittest.cc               |   60 +-
 src/bin/xfrout/tests/xfrout_test.py.in             |    4 +-
 src/bin/xfrout/xfrout.py.in                        |    3 +-
 src/lib/cache/message_entry.cc                     |   13 +-
 src/lib/cache/message_entry.h                      |    4 +-
 src/lib/datasrc/Makefile.am                        |    2 +-
 src/lib/datasrc/data_source.cc                     |    4 +-
 src/lib/datasrc/database.cc                        |   48 +-
 src/lib/datasrc/database.h                         |    3 +
 src/lib/datasrc/datasrc_messages.mes               |   14 +-
 src/lib/datasrc/memory/Makefile.am                 |   11 +-
 src/lib/datasrc/memory/benchmarks/.gitignore       |    1 +
 src/lib/datasrc/memory/benchmarks/Makefile.am      |   18 +
 .../memory/benchmarks/rdata_reader_bench.cc        |  221 ++++++
 src/lib/datasrc/memory/domaintree.h                |  527 ++++++------
 .../{rdata_encoder.cc => rdata_serialization.cc}   |  251 +++---
 .../{rdata_encoder.h => rdata_serialization.h}     |  307 +++++--
 src/lib/datasrc/memory/rdata_serialization_priv.cc |   65 ++
 src/lib/datasrc/memory/rdataset.cc                 |  178 +++++
 src/lib/datasrc/memory/rdataset.h                  |  389 +++++++++
 src/lib/datasrc/memory/segment_object_holder.h     |   63 ++
 src/lib/datasrc/memory/tests/Makefile.am           |    8 +-
 .../datasrc/memory/tests/domaintree_unittest.cc    |   51 +-
 src/lib/datasrc/memory/tests/memory_segment_test.h |   62 ++
 .../datasrc/memory/tests/rdata_encoder_unittest.cc |  481 -----------
 .../memory/tests/rdata_serialization_unittest.cc   |  839 ++++++++++++++++++++
 src/lib/datasrc/memory/tests/rdataset_unittest.cc  |  298 +++++++
 .../memory/tests/segment_object_holder_unittest.cc |   67 ++
 src/lib/datasrc/memory/tests/zone_data_unittest.cc |  255 ++++++
 .../datasrc/memory/tests/zone_table_unittest.cc    |   39 +-
 src/lib/datasrc/memory/zone_data.cc                |  171 ++++
 src/lib/datasrc/memory/zone_data.h                 |  541 ++++++++++++-
 src/lib/datasrc/memory/zone_table.cc               |   65 +-
 src/lib/datasrc/memory/zone_table.h                |   32 +-
 src/lib/datasrc/memory_datasrc.cc                  |   32 +-
 src/lib/datasrc/sqlite3_accessor.cc                |   10 +-
 src/lib/datasrc/sqlite3_datasrc.cc                 |    2 +-
 src/lib/datasrc/tests/Makefile.am                  |   24 +-
 src/lib/datasrc/tests/database_unittest.cc         |   60 +-
 src/lib/datasrc/tests/faked_nsec3.cc               |   35 +-
 src/lib/datasrc/tests/faked_nsec3.h                |    5 +-
 src/lib/datasrc/tests/memory_datasrc_unittest.cc   |    8 +-
 .../datasrc/tests/zone_finder_context_unittest.cc  |    4 +
 src/lib/datasrc/zone.h                             |    7 +
 src/lib/datasrc/zone_finder.cc                     |   55 ++
 src/lib/dns/benchmarks/message_renderer_bench.cc   |   15 +-
 src/lib/dns/benchmarks/rdatarender_bench.cc        |   13 +-
 src/lib/dns/labelsequence.h                        |   14 +-
 src/lib/dns/message.cc                             |   10 +-
 src/lib/dns/message.h                              |    7 +-
 src/lib/dns/messagerenderer.h                      |    5 +-
 src/lib/dns/python/message_python.cc               |   10 +-
 src/lib/dns/rrset.cc                               |   41 +
 src/lib/dns/rrset.h                                |   11 +
 src/lib/dns/tests/labelsequence_unittest.cc        |   10 +
 src/lib/dns/tests/message_unittest.cc              |   82 +-
 src/lib/dns/tests/testdata/Makefile.am             |    2 +-
 src/lib/dns/tests/testdata/message_toWire6         |    2 +-
 src/lib/dns/tests/testdata/message_toWire7         |   35 +
 src/lib/python/isc/datasrc/tests/datasrc_test.py   |   16 +-
 src/lib/resolve/resolve.cc                         |    2 +-
 src/lib/testutils/dnsmessage_test.h                |   16 +
 66 files changed, 4470 insertions(+), 1208 deletions(-)
 create mode 100644 src/lib/datasrc/memory/benchmarks/.gitignore
 create mode 100644 src/lib/datasrc/memory/benchmarks/Makefile.am
 create mode 100644 src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc
 rename src/lib/datasrc/memory/{rdata_encoder.cc => rdata_serialization.cc} (79%)
 rename src/lib/datasrc/memory/{rdata_encoder.h => rdata_serialization.h} (54%)
 create mode 100644 src/lib/datasrc/memory/rdata_serialization_priv.cc
 create mode 100644 src/lib/datasrc/memory/rdataset.cc
 create mode 100644 src/lib/datasrc/memory/rdataset.h
 create mode 100644 src/lib/datasrc/memory/segment_object_holder.h
 create mode 100644 src/lib/datasrc/memory/tests/memory_segment_test.h
 delete mode 100644 src/lib/datasrc/memory/tests/rdata_encoder_unittest.cc
 create mode 100644 src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc
 create mode 100644 src/lib/datasrc/memory/tests/rdataset_unittest.cc
 create mode 100644 src/lib/datasrc/memory/tests/segment_object_holder_unittest.cc
 create mode 100644 src/lib/datasrc/memory/tests/zone_data_unittest.cc
 create mode 100644 src/lib/datasrc/memory/zone_data.cc
 create mode 100644 src/lib/datasrc/zone_finder.cc
 create mode 100644 src/lib/dns/tests/testdata/message_toWire7

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 7fae2da..d7ceafc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,17 +1,17 @@
-nnn.    [func]          naokikambe
-        Added Xfrout statistics counters: notifyoutv4, notifyoutv6, xfrrej, and
-        xfrreqdone. These are per-zone type counters. The value of these
-        counters can be seen with zone name by invoking "Stats show Xfrout" via
-        bindctl.
-        (Trac #2158, git TBD)
-
 nnn.	[func]		naokikambe
+	Added Xfrout statistics counters: notifyoutv4, notifyoutv6, xfrrej, and
+	xfrreqdone. These are per-zone type counters. The value of these
+	counters can be seen with zone name by invoking "Stats show Xfrout" via
+	bindctl.
+	(Trac #2158, git TBD)
+
+470.	[func]		naokikambe
 	The stats module now supports partial statistics updates. Each
 	module can return only statistics data which have been updated since
 	the last time it sent them to the stats module. The purpose of partial
 	updates is to reduce the amount of statistics data sent through the
 	message queue.
-	(Trac #2179, git TBD)
+	(Trac #2179, git d659abdd9f3f369a29830831297f64484ac7b051)
 
 469.	[bug]		jelte
 	libdatasrc: the data source client list class now ignores zone
@@ -96,7 +96,6 @@ bind10-devel-20120816 released on August 16, 2012
 	(Trac #1708, git e0d7c52a71414f4de1361b09d3c70431c96daa3f)
 
 458.	[build]*		jinmei
->>>>>>> master
 	BIND 10 now relies on Boost offset_ptr, which caused some new
 	portability issues.  Such issues are detected at ./configure time.
 	If ./configure stops due to this, try the following workaround:
diff --git a/configure.ac b/configure.ac
index f83689b..c00b9cd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1166,6 +1166,7 @@ AC_CONFIG_FILES([Makefile
                  src/lib/datasrc/Makefile
                  src/lib/datasrc/memory/Makefile
                  src/lib/datasrc/memory/tests/Makefile
+                 src/lib/datasrc/memory/benchmarks/Makefile
                  src/lib/datasrc/tests/Makefile
                  src/lib/datasrc/tests/testdata/Makefile
                  src/lib/xfr/Makefile
diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc
index 1eb6228..6ccd4d2 100644
--- a/src/bin/auth/query.cc
+++ b/src/bin/auth/query.cc
@@ -57,7 +57,7 @@ namespace auth {
 void
 Query::ResponseCreator::addRRset(isc::dns::Message& message,
                                  const isc::dns::Message::Section section,
-                                 const ConstRRsetPtr& rrset, const bool dnssec)
+                                 const ConstRRsetPtr& rrset)
 {
     /// Is this RRset already in the list of RRsets added to the message?
     const std::vector<const AbstractRRset*>::const_iterator i =
@@ -68,8 +68,7 @@ Query::ResponseCreator::addRRset(isc::dns::Message& message,
         // No - add it to both the message and the list of RRsets processed.
         // The const-cast is wrong, but the message interface seems to insist.
         message.addRRset(section,
-                         boost::const_pointer_cast<AbstractRRset>(rrset),
-                         dnssec);
+                         boost::const_pointer_cast<AbstractRRset>(rrset));
         added_.push_back(rrset.get());
     }
 }
@@ -78,8 +77,7 @@ void
 Query::ResponseCreator::create(Message& response,
                                const vector<ConstRRsetPtr>& answers,
                                const vector<ConstRRsetPtr>& authorities,
-                               const vector<ConstRRsetPtr>& additionals,
-                               const bool dnssec)
+                               const vector<ConstRRsetPtr>& additionals)
 {
     // Inserter should be reset each time the query is reset, so should be
     // empty at this point.
@@ -91,13 +89,13 @@ Query::ResponseCreator::create(Message& response,
     // guarantee that if there are duplicates, the single RRset added will
     // appear in the most important section.
     BOOST_FOREACH(const ConstRRsetPtr& rrset, answers) {
-        addRRset(response, Message::SECTION_ANSWER, rrset, dnssec);
+        addRRset(response, Message::SECTION_ANSWER, rrset);
     }
     BOOST_FOREACH(const ConstRRsetPtr& rrset, authorities) {
-        addRRset(response, Message::SECTION_AUTHORITY, rrset, dnssec);
+        addRRset(response, Message::SECTION_AUTHORITY, rrset);
     }
     BOOST_FOREACH(const ConstRRsetPtr& rrset, additionals) {
-        addRRset(response, Message::SECTION_ADDITIONAL, rrset, dnssec);
+        addRRset(response, Message::SECTION_ADDITIONAL, rrset);
     }
 }
 
@@ -533,8 +531,7 @@ Query::process(datasrc::ClientList& client_list,
             break;
     }
 
-    response_creator_.create(*response_, answers_, authorities_, additionals_,
-                             dnssec_);
+    response_creator_.create(*response_, answers_, authorities_, additionals_);
 }
 
 void
@@ -592,8 +589,7 @@ Query::processDSAtChild() {
         }
     }
 
-    response_creator_.create(*response_, answers_, authorities_, additionals_,
-                             dnssec_);
+    response_creator_.create(*response_, answers_, authorities_, additionals_);
     return (true);
 }
 
diff --git a/src/bin/auth/query.h b/src/bin/auth/query.h
index 1dc5ec0..5af2361 100644
--- a/src/bin/auth/query.h
+++ b/src/bin/auth/query.h
@@ -441,16 +441,13 @@ public:
         /// authority, and additional sections, and add them to their
         /// corresponding sections in the given message.  The RRsets are
         /// filtered such that a particular RRset appears only once in the
-        /// message.
-        ///
-        /// If \c dnssec is true, it tells the message to include any RRSIGs
-        /// attached to the RRsets.
+        /// message. Any RRSIGs attached to the RRsets will be included
+        /// when they are rendered.
         void create(
             isc::dns::Message& message,
             const std::vector<isc::dns::ConstRRsetPtr>& answers_,
             const std::vector<isc::dns::ConstRRsetPtr>& authorities_,
-            const std::vector<isc::dns::ConstRRsetPtr>& additionals_,
-            const bool dnssec);
+            const std::vector<isc::dns::ConstRRsetPtr>& additionals_);
 
     private:
         // \brief RRset comparison functor.
@@ -469,10 +466,9 @@ public:
         /// \param message Message to which the RRset is to be added
         /// \param section Section of the message in which the RRset is put
         /// \param rrset Pointer to RRset to be added to the message
-        /// \param dnssec Whether RRSIG records should be added as well
         void addRRset(isc::dns::Message& message,
                       const isc::dns::Message::Section section,
-                      const isc::dns::ConstRRsetPtr& rrset, const bool dnssec);
+                      const isc::dns::ConstRRsetPtr& rrset);
 
 
     private:
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 69d6608..603bf5c 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -211,12 +211,18 @@ const char* const nonsec_a_txt =
     "nonsec.example.com. 3600 IN A 192.0.2.0\n";
 
 // NSEC3 RRs.  You may also need to add mapping to MockZoneFinder::hash_map_.
-const char* const nsec3_apex_txt =
+const string nsec3_apex_txt =
     "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.com. 3600 IN NSEC3 1 1 12 "
     "aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA NSEC3PARAM RRSIG\n";
-const char* const nsec3_www_txt =
+const string nsec3_apex_rrsig_txt =
+    "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.com. 3600 IN RRSIG NSEC3 5 3 "
+    "3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE";
+const string nsec3_www_txt =
     "q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN NSEC3 1 1 12 "
     "aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG\n";
+const string nsec3_www_rrsig_txt =
+    "q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN RRSIG NSEC3 5 3 "
+    "3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE";
 
 // NSEC3 for wild.example.com (used in wildcard tests, will be added on
 // demand not to confuse other tests)
@@ -479,9 +485,10 @@ protected:
                                        isc::dns::ConstRRsetPtr rrset,
                                        FindResultFlags flags = RESULT_DEFAULT)
     {
+        ConstRRsetPtr rp = stripRRsigs(rrset, options);
         return (ZoneFinderContextPtr(
                     new Context(*this, options,
-                                ResultContext(code, rrset, flags))));
+                                ResultContext(code, rp, flags))));
     }
 
 private:
@@ -594,7 +601,7 @@ MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
                  found_domain->second.begin();
                  found_rrset != found_domain->second.end(); ++found_rrset) {
                 // Insert RRs under the domain name into target
-                target.push_back(found_rrset->second);
+                target.push_back(stripRRsigs(found_rrset->second, options));
             }
             return (ZoneFinderContextPtr(
                         new Context(*this, options,
@@ -701,25 +708,8 @@ MockZoneFinder::find(const Name& name, const RRType& type,
         RRsetStore::const_iterator found_rrset =
             found_domain->second.find(type);
         if (found_rrset != found_domain->second.end()) {
-            ConstRRsetPtr rrset;
-            // Strip whatever signature there is in case DNSSEC is not required
-            // Just to make sure the Query asks for it when it is needed
-            if ((options & ZoneFinder::FIND_DNSSEC) != 0 ||
-                include_rrsig_anyway_ ||
-                !found_rrset->second->getRRsig()) {
-                rrset = found_rrset->second;
-            } else {
-                RRsetPtr noconst(new RRset(found_rrset->second->getName(),
-                                           found_rrset->second->getClass(),
-                                           found_rrset->second->getType(),
-                                           found_rrset->second->getTTL()));
-                for (RdataIteratorPtr
-                     i(found_rrset->second->getRdataIterator());
-                     !i->isLast(); i->next()) {
-                    noconst->addRdata(i->getCurrent());
-                }
-                rrset = noconst;
-            }
+            ConstRRsetPtr rrset = ZoneFinder::stripRRsigs(found_rrset->second,
+                                                          options);
             return (createContext(options, SUCCESS, rrset));
         }
 
@@ -1985,14 +1975,16 @@ TEST_F(QueryTest, findNSEC3) {
     // Apex name.  It should have a matching NSEC3
     {
         SCOPED_TRACE("apex, non recursive");
-        nsec3Check(true, expected_closest_labels, nsec3_apex_txt,
+        nsec3Check(true, expected_closest_labels,
+                   nsec3_apex_txt + "\n" + nsec3_apex_rrsig_txt,
                    mock_finder->findNSEC3(Name("example.com"), false));
     }
 
     // Recursive mode doesn't change the result in this case.
     {
         SCOPED_TRACE("apex, recursive");
-        nsec3Check(true, expected_closest_labels, nsec3_apex_txt,
+        nsec3Check(true, expected_closest_labels,
+                   nsec3_apex_txt + "\n" + nsec3_apex_rrsig_txt,
                    mock_finder->findNSEC3(Name("example.com"), true));
     }
 
@@ -2000,7 +1992,8 @@ TEST_F(QueryTest, findNSEC3) {
     // returned.
     {
         SCOPED_TRACE("nxdomain, non recursive");
-        nsec3Check(false, 4, nsec3_www_txt,
+        nsec3Check(false, 4,
+                   nsec3_www_txt + "\n" + nsec3_www_rrsig_txt,
                    mock_finder->findNSEC3(Name("nxdomain.example.com"),
                                           false));
     }
@@ -2010,7 +2003,8 @@ TEST_F(QueryTest, findNSEC3) {
     {
         SCOPED_TRACE("nxdomain, recursive");
         nsec3Check(true, expected_closest_labels,
-                   string(nsec3_apex_txt) + string(nsec3_www_txt),
+                   nsec3_apex_txt + "\n" + nsec3_apex_rrsig_txt + "\n" +
+                   nsec3_www_txt + "\n" + nsec3_www_rrsig_txt,
                    mock_finder->findNSEC3(Name("nxdomain.example.com"), true));
     }
 
@@ -2019,7 +2013,8 @@ TEST_F(QueryTest, findNSEC3) {
     {
         SCOPED_TRACE("nxdomain, next closer != qname");
         nsec3Check(true, expected_closest_labels,
-                   string(nsec3_apex_txt) + string(nsec3_www_txt),
+                   nsec3_apex_txt + "\n" + nsec3_apex_rrsig_txt + "\n" +
+                   nsec3_www_txt + "\n" + nsec3_www_rrsig_txt,
                    mock_finder->findNSEC3(Name("nx.domain.example.com"),
                                           true));
     }
@@ -2027,13 +2022,15 @@ TEST_F(QueryTest, findNSEC3) {
     // In the rest of test we check hash comparison for wrap around cases.
     {
         SCOPED_TRACE("largest");
-        nsec3Check(false, 4, nsec3_apex_txt,
+        nsec3Check(false, 4,
+                   nsec3_apex_txt + "\n" + nsec3_apex_rrsig_txt,
                    mock_finder->findNSEC3(Name("nxdomain2.example.com"),
                                           false));
     }
     {
         SCOPED_TRACE("smallest");
-        nsec3Check(false, 4, nsec3_www_txt,
+        nsec3Check(false, 4,
+                   nsec3_www_txt + "\n" + nsec3_www_rrsig_txt,
                    mock_finder->findNSEC3(Name("nxdomain3.example.com"),
                                           false));
     }
@@ -2516,8 +2513,7 @@ TEST_F(QueryTest, DuplicateNameRemoval) {
     EXPECT_EQ(0, message.getRRCount(Message::SECTION_ADDITIONAL));
 
     // ... and fill it.
-    Query::ResponseCreator().create(message, answer, authority, additional,
-                                    false);
+    Query::ResponseCreator().create(message, answer, authority, additional);
 
     // Check counts in each section.  Note that these are RR counts,
     // not RRset counts.
diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in
index dd1f1cf..b471603 100644
--- a/src/bin/xfrout/tests/xfrout_test.py.in
+++ b/src/bin/xfrout/tests/xfrout_test.py.in
@@ -1690,8 +1690,8 @@ class TestXfroutServer(unittest.TestCase):
 
     def test_getstats(self):
         self.assertEqual(
-            self.xfrout_server.command_handler('getstats', None),
-            isc.config.ccsession.create_answer(0,  {}))
+            self.xfrout_server.command_handler('getstats', None), \
+                create_answer(0,  {}))
 
 if __name__== "__main__":
     isc.log.resetUnitTestRootLogger()
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index b6a1303..48f967d 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -1235,8 +1235,7 @@ class XfroutServer:
             # daemon is periodically asking to the b10-xfrout daemon.
             logger.debug(DBG_XFROUT_TRACE, \
                              XFROUT_RECEIVED_GETSTATS_COMMAND)
-            answer = isc.config.ccsession.create_answer(
-                0, self._counter.get_statistics())
+            answer = create_answer(0, self._counter.get_statistics())
 
         else:
             answer = create_answer(1, "Unknown command:" + str(cmd))
diff --git a/src/lib/cache/message_entry.cc b/src/lib/cache/message_entry.cc
index d9560a6..b6af869 100644
--- a/src/lib/cache/message_entry.cc
+++ b/src/lib/cache/message_entry.cc
@@ -110,8 +110,7 @@ MessageEntry::getRRsetEntries(vector<RRsetEntryPtr>& rrset_entry_vec,
 void
 MessageEntry::addRRset(isc::dns::Message& message,
                        const vector<RRsetEntryPtr>& rrset_entry_vec,
-                       const isc::dns::Message::Section& section,
-                       bool dnssec_need)
+                       const isc::dns::Message::Section& section)
 {
     uint16_t start_index = 0;
     uint16_t end_index = answer_count_;
@@ -126,8 +125,7 @@ MessageEntry::addRRset(isc::dns::Message& message,
     }
 
     for (uint16_t index = start_index; index < end_index; ++index) {
-        message.addRRset(section, rrset_entry_vec[index]->getRRset(),
-                         dnssec_need);
+        message.addRRset(section, rrset_entry_vec[index]->getRRset());
     }
 }
 
@@ -156,10 +154,9 @@ MessageEntry::genMessage(const time_t& time_now,
         msg.setHeaderFlag(Message::HEADERFLAG_AA, false);
         msg.setHeaderFlag(Message::HEADERFLAG_TC, headerflag_tc_);
 
-        bool dnssec_need = msg.getEDNS().get();
-        addRRset(msg, rrset_entry_vec, Message::SECTION_ANSWER, dnssec_need);
-        addRRset(msg, rrset_entry_vec, Message::SECTION_AUTHORITY, dnssec_need);
-        addRRset(msg, rrset_entry_vec, Message::SECTION_ADDITIONAL, dnssec_need);
+        addRRset(msg, rrset_entry_vec, Message::SECTION_ANSWER);
+        addRRset(msg, rrset_entry_vec, Message::SECTION_AUTHORITY);
+        addRRset(msg, rrset_entry_vec, Message::SECTION_ADDITIONAL);
 
         return (true);
     }
diff --git a/src/lib/cache/message_entry.h b/src/lib/cache/message_entry.h
index 6775ff6..6da27cc 100644
--- a/src/lib/cache/message_entry.h
+++ b/src/lib/cache/message_entry.h
@@ -154,11 +154,9 @@ protected:
     /// \param rrset_entry_vec vector for rrset entries in
     ///        different sections.
     /// \param section The section to add to
-    /// \param dnssec_need need dnssec records or not.
     void addRRset(isc::dns::Message& message,
                   const std::vector<RRsetEntryPtr>& rrset_entry_vec,
-                  const isc::dns::Message::Section& section,
-                  bool dnssec_need);
+                  const isc::dns::Message::Section& section);
 
     /// \brief Get the all the rrset entries for the message entry.
     ///
diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am
index bb18b75..d1e113f 100644
--- a/src/lib/datasrc/Makefile.am
+++ b/src/lib/datasrc/Makefile.am
@@ -29,7 +29,7 @@ libb10_datasrc_la_SOURCES += cache.h cache.cc
 libb10_datasrc_la_SOURCES += rbnode_rrset.h
 libb10_datasrc_la_SOURCES += rbtree.h
 libb10_datasrc_la_SOURCES += zonetable.h zonetable.cc
-libb10_datasrc_la_SOURCES += zone.h zone_finder_context.cc
+libb10_datasrc_la_SOURCES += zone.h zone_finder.cc zone_finder_context.cc
 libb10_datasrc_la_SOURCES += result.h
 libb10_datasrc_la_SOURCES += logger.h logger.cc
 libb10_datasrc_la_SOURCES += client.h iterator.h
diff --git a/src/lib/datasrc/data_source.cc b/src/lib/datasrc/data_source.cc
index a713b82..0e5b3a3 100644
--- a/src/lib/datasrc/data_source.cc
+++ b/src/lib/datasrc/data_source.cc
@@ -564,12 +564,12 @@ addToMessage(Query& q, const Message::Section sect, RRsetPtr rrset,
         if (rrset->getType() == RRType::RRSIG() ||
             !m.hasRRset(sect, rrset->getName(), rrset->getClass(),
                         rrset->getType())) {
-            m.addRRset(sect, rrset, false);
+            m.addRRset(sect, rrset);
         }
     } else {
         if (!m.hasRRset(sect, rrset->getName(), rrset->getClass(),
                         rrset->getType())) {
-            m.addRRset(sect, rrset, q.wantDnssec());
+            m.addRRset(sect, rrset);
         }
     }
 }
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 82a5e54..ede7aa3 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -157,7 +157,7 @@ public:
         const isc::dns::RRType& type_covered =
             static_cast<isc::dns::rdata::generic::RRSIG*>(
                 sig_rdata.get())->typeCovered();
-        sigs[type_covered].push_back(sig_rdata);
+        sigs_[type_covered].push_back(sig_rdata);
     }
 
     // If the store contains signatures for the type of the given
@@ -165,21 +165,26 @@ public:
     void appendSignatures(isc::dns::RRsetPtr& rrset) const {
         std::map<isc::dns::RRType,
                  std::vector<isc::dns::rdata::RdataPtr> >::const_iterator
-            found = sigs.find(rrset->getType());
-        if (found != sigs.end()) {
+            found = sigs_.find(rrset->getType());
+        if (found != sigs_.end()) {
             BOOST_FOREACH(isc::dns::rdata::RdataPtr sig, found->second) {
                 rrset->addRRsig(sig);
             }
         }
     }
 
+    bool empty() const {
+        return (sigs_.empty());
+    }
+
 private:
-    std::map<isc::dns::RRType, std::vector<isc::dns::rdata::RdataPtr> > sigs;
+    std::map<isc::dns::RRType, std::vector<isc::dns::rdata::RdataPtr> > sigs_;
 };
 }
 
 DatabaseClient::Finder::FoundRRsets
 DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
+                                  bool sigs,
                                   const string* construct_name, bool any,
                                   DatabaseAccessor::IteratorContextPtr context)
 {
@@ -213,7 +218,7 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
         try {
             const RRType cur_type(columns[DatabaseAccessor::TYPE_COLUMN]);
 
-            if (cur_type == RRType::RRSIG()) {
+            if (sigs && (cur_type == RRType::RRSIG())) {
                 // If we get signatures before we get the actual data, we
                 // can't know which ones to keep and which to drop...
                 // So we keep a separate store of any signature that may be
@@ -228,7 +233,7 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
             if (types.find(cur_type) != types.end() || any) {
                 // This type is requested, so put it into result
                 const RRTTL cur_ttl(columns[DatabaseAccessor::TTL_COLUMN]);
-                // Ths sigtype column was an optimization for finding the
+                // The sigtype column was an optimization for finding the
                 // relevant RRSIG RRs for a lookup. Currently this column is
                 // not used in this revised datasource implementation. We
                 // should either start using it again, or remove it from use
@@ -276,10 +281,12 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
         isc_throw(DataSourceError, "CNAME shares domain " << name <<
                   " with something else");
     }
-    // Add signatures to all found RRsets
-    for (std::map<RRType, RRsetPtr>::iterator i(result.begin());
-         i != result.end(); ++ i) {
-        sig_store.appendSignatures(i->second);
+    if (!sig_store.empty()) {
+        // Add signatures to all found RRsets
+        for (std::map<RRType, RRsetPtr>::iterator i(result.begin());
+             i != result.end(); ++ i) {
+            sig_store.appendSignatures(i->second);
+        }
     }
     if (records_found && any) {
         result[RRType::ANY()] = RRsetPtr();
@@ -448,7 +455,9 @@ DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
         // Look if there's NS or DNAME at this point of the tree, but ignore
         // the NS RRs at the apex of the zone.
         const FoundRRsets found = getRRsets(superdomain.toText(),
-                                            DELEGATION_TYPES());
+                                            DELEGATION_TYPES(),
+                                            ((options & FIND_DNSSEC) ==
+                                             FIND_DNSSEC));
         if (found.first) {
             // This node contains either NS or DNAME RRs so it does exist.
             const FoundIterator nsi(found.second.find(RRType::NS()));
@@ -581,6 +590,8 @@ DatabaseClient::Finder::findWildcardMatch(
         // RFC 4592 section 4.4).
         // Search for a match.  The types are the same as with original query.
         const FoundRRsets found = getRRsets(wildcard, final_types,
+                                            ((options & FIND_DNSSEC) ==
+                                             FIND_DNSSEC),
                                             &construct_name,
                                             type == RRType::ANY());
         if (found.first) {
@@ -685,7 +696,7 @@ DatabaseClient::Finder::FindDNSSECContext::probe() {
             // such cases).
             const string origin = finder_.getOrigin().toText();
             const FoundRRsets nsec3_found =
-                finder_.getRRsets(origin, NSEC3PARAM_TYPES());
+                finder_.getRRsets(origin, NSEC3PARAM_TYPES(), true);
             const FoundIterator nfi=
                 nsec3_found.second.find(RRType::NSEC3PARAM());
             is_nsec3_ = (nfi != nsec3_found.second.end());
@@ -696,7 +707,7 @@ DatabaseClient::Finder::FindDNSSECContext::probe() {
             // described in Section 10.4 of RFC 5155.
             if (!is_nsec3_) {
                 const FoundRRsets nsec_found =
-                    finder_.getRRsets(origin, NSEC_TYPES());
+                    finder_.getRRsets(origin, NSEC_TYPES(), true);
                 const FoundIterator nfi =
                     nsec_found.second.find(RRType::NSEC());
                 is_nsec_ = (nfi != nsec_found.second.end());
@@ -749,7 +760,7 @@ DatabaseClient::Finder::FindDNSSECContext::getDNSSECRRset(const Name &name,
         const Name& nsec_name =
             covering ? finder_.findPreviousName(name) : name;
         const FoundRRsets found = finder_.getRRsets(nsec_name.toText(),
-                                                    NSEC_TYPES());
+                                                    NSEC_TYPES(), true);
         const FoundIterator nci = found.second.find(RRType::NSEC());
         if (nci != found.second.end()) {
             return (nci->second);
@@ -976,6 +987,8 @@ DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
     WantedTypes final_types(FINAL_TYPES());
     final_types.insert(type);
     const FoundRRsets found = getRRsets(name.toText(), final_types,
+                                        ((options & FIND_DNSSEC) ==
+                                         FIND_DNSSEC),
                                         NULL, type == RRType::ANY());
     FindDNSSECContext dnssec_ctx(*this, options);
     if (found.first) {
@@ -1009,7 +1022,8 @@ DatabaseClient::Finder::findNSEC3(const Name& name, bool recursive) {
     // Now, we need to get the NSEC3 params from the apex and create the hash
     // creator for it.
     const FoundRRsets nsec3param(getRRsets(getOrigin().toText(),
-                                           NSEC3PARAM_TYPES()));
+                                           NSEC3PARAM_TYPES(),
+                                           true));
     const FoundIterator param(nsec3param.second.find(RRType::NSEC3PARAM()));
     if (!nsec3param.first || param == nsec3param.second.end()) {
         // No NSEC3 params? :-(
@@ -1049,6 +1063,7 @@ DatabaseClient::Finder::findNSEC3(const Name& name, bool recursive) {
         }
 
         const FoundRRsets nsec3(getRRsets(hash + "." + otext, NSEC3_TYPES(),
+                                          true,
                                           NULL, false, context));
 
         if (nsec3.first) {
@@ -1074,7 +1089,8 @@ DatabaseClient::Finder::findNSEC3(const Name& name, bool recursive) {
                 arg(labels).arg(prevHash);
             context = accessor_->getNSEC3Records(prevHash, zone_id_);
             const FoundRRsets prev_nsec3(getRRsets(prevHash + "." + otext,
-                                                   NSEC3_TYPES(), NULL, false,
+                                                   NSEC3_TYPES(), true,
+                                                   NULL, false,
                                                    context));
 
             if (!prev_nsec3.first) {
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index 65ddfcc..6895509 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -963,6 +963,8 @@ public:
         ///
         /// \param name Which domain name should be scanned.
         /// \param types List of types the caller is interested in.
+        /// \param sigs Return RRSIGs if true is passed. Otherwise, no
+        ///     associated RRSIGs are set on the returned RRsets.
         /// \param construct_name If this is NULL, the resulting RRsets have
         ///     their name set to name. If it is not NULL, it overrides the
         ///     name and uses this one (this can be used for wildcard
@@ -984,6 +986,7 @@ public:
         ///     database or the database contains bad data.
         FoundRRsets getRRsets(const std::string& name,
                               const WantedTypes& types,
+                              bool sigs,
                               const std::string* construct_name = NULL,
                               bool any = false,
                               DatabaseAccessor::IteratorContextPtr srcContext =
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
index 6837476..94b4d42 100644
--- a/src/lib/datasrc/datasrc_messages.mes
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -787,10 +787,20 @@ example.com). However, this name is not contained in any zone in the
 data source. This is an error since it indicates a problem in the earlier
 processing of the query.
 
-% DATASRC_SQLITE_SETUP setting up SQLite database
+% DATASRC_SQLITE_SETUP setting up new SQLite3 database in '%1'
 The database for SQLite data source was found empty. It is assumed this is the
 first run and it is being initialized with current schema.  It'll still contain
-no data, but it will be ready for use.
+no data, but it will be ready for use. If this is indeed the first run of
+BIND 10, it is to be expected and completely harmless. If you just configured
+a data source to point to an existing file and you see this, you may have
+misspelled the file name.
+
+% DATASRC_SQLITE_SETUP_OLD_API setting up new SQLite database
+The database for SQLite data source was found empty. It is assumed this is the
+first run and it is being initialized with current schema.  It'll still contain
+no data, but it will be ready for use. This is similar to DATASRC_SQLITE_SETUP
+message, but it is logged from the old API. You should never see it, since the
+API is deprecated.
 
 % DATASRC_STATIC_CLASS_NOT_CH static data source can handle CH class only
 An error message indicating that a query requesting a RR for a class other
diff --git a/src/lib/datasrc/memory/Makefile.am b/src/lib/datasrc/memory/Makefile.am
index 8e7b03e..ef46abe 100644
--- a/src/lib/datasrc/memory/Makefile.am
+++ b/src/lib/datasrc/memory/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . tests
+SUBDIRS = . tests 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
@@ -10,7 +10,10 @@ CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
 
 noinst_LTLIBRARIES = libdatasrc_memory.la
 
-libdatasrc_memory_la_SOURCES = rdata_encoder.h rdata_encoder.cc
-libdatasrc_memory_la_SOURCES += domaintree.h
-libdatasrc_memory_la_SOURCES += zone_data.h
+libdatasrc_memory_la_SOURCES = domaintree.h
+libdatasrc_memory_la_SOURCES += rdataset.h rdataset.cc
+libdatasrc_memory_la_SOURCES += rdata_serialization.h rdata_serialization.cc
+libdatasrc_memory_la_SOURCES += zone_data.h zone_data.cc
+libdatasrc_memory_la_SOURCES += segment_object_holder.h
 libdatasrc_memory_la_SOURCES += zone_table.h zone_table.cc
+EXTRA_DIST  = rdata_serialization_priv.cc
diff --git a/src/lib/datasrc/memory/benchmarks/.gitignore b/src/lib/datasrc/memory/benchmarks/.gitignore
new file mode 100644
index 0000000..ea243a4
--- /dev/null
+++ b/src/lib/datasrc/memory/benchmarks/.gitignore
@@ -0,0 +1 @@
+/rdata_reader_bench
diff --git a/src/lib/datasrc/memory/benchmarks/Makefile.am b/src/lib/datasrc/memory/benchmarks/Makefile.am
new file mode 100644
index 0000000..81d5642
--- /dev/null
+++ b/src/lib/datasrc/memory/benchmarks/Makefile.am
@@ -0,0 +1,18 @@
+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 = rdata_reader_bench
+
+rdata_reader_bench_SOURCES = rdata_reader_bench.cc
+
+rdata_reader_bench_LDADD = $(top_builddir)/src/lib/datasrc/memory/libdatasrc_memory.la
+rdata_reader_bench_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+rdata_reader_bench_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
diff --git a/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc b/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc
new file mode 100644
index 0000000..4e6af56
--- /dev/null
+++ b/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc
@@ -0,0 +1,221 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <bench/benchmark.h>
+
+#include <util/buffer.h>
+
+#include <dns/name.h>
+#include <dns/messagerenderer.h>
+#include <dns/rrset.h>
+#include <dns/rrclass.h>
+#include <dns/masterload.h>
+
+#include <datasrc/memory/rdata_serialization.h>
+
+#include <boost/bind.hpp>
+
+#include <vector>
+#include <sstream>
+
+#include <unistd.h>
+
+using std::vector;
+using namespace isc::bench;
+using namespace isc::datasrc::memory;
+using namespace isc::dns;
+
+namespace {
+struct EncodeParam {
+    EncodeParam(RdataEncoder& encoder, ConstRRsetPtr rrset,
+                ConstRRsetPtr sig_rrset = ConstRRsetPtr()) :
+        rrclass(rrset->getClass()), rrtype(rrset->getType()),
+        rdata_count(rrset->getRdataCount()),
+        sig_count(sig_rrset ? sig_rrset->getRdataCount() : 0)
+    {
+        encoder.start(rrclass, rrtype);
+        for (RdataIteratorPtr it = rrset->getRdataIterator();
+             !it->isLast();
+             it->next()) {
+            encoder.addRdata(it->getCurrent());
+        }
+        if (sig_rrset) {
+            for (RdataIteratorPtr it = sig_rrset->getRdataIterator();
+                 !it->isLast();
+                 it->next())
+            {
+                encoder.addSIGRdata(it->getCurrent());
+            }
+        }
+        const size_t data_len = encoder.getStorageLength();
+        data.resize(data_len);
+        encoder.encode(&data[0], data.size());
+    }
+    RRClass rrclass;
+    RRType rrtype;
+    size_t rdata_count;
+    size_t sig_count;
+    vector<uint8_t> data;
+};
+
+// Encapsulating parameters for a RdataReader.  It extracts from the given
+// RRset and its RRSIGs parameters that are necessary construct an RdataReader.
+// RDATA data will be stored in the 'data' vector.
+// members are defined as non const so we can use the object of this struct
+// in a vector.
+class ReaderBenchMark {
+public:
+    ReaderBenchMark(const vector<EncodeParam>& encode_params,
+                    MessageRenderer& renderer) :
+        encode_params_(encode_params), renderer_(renderer)
+    {}
+    unsigned int run() {
+        vector<EncodeParam>::const_iterator it;
+        const vector<EncodeParam>::const_iterator it_end =
+            encode_params_.end();
+        renderer_.clear();
+        for (it = encode_params_.begin(); it != it_end; ++it) {
+            RdataReader reader(it->rrclass, it->rrtype, &it->data[0],
+                               it->rdata_count, it->sig_count,
+                               boost::bind(&ReaderBenchMark::renderName,
+                                           this, _1, _2),
+                               boost::bind(&ReaderBenchMark::renderData,
+                                           this, _1, _2));
+            reader.iterate();
+            reader.iterateAllSigs();
+        }
+        return (1);
+    }
+    void renderName(const LabelSequence& labels,
+                    RdataNameAttributes attributes)
+    {
+        const bool compress =
+            (attributes & NAMEATTR_COMPRESSIBLE) != 0;
+        renderer_.writeName(labels, compress);
+    }
+    void renderData(const void* data, size_t data_len) {
+        renderer_.writeData(data, data_len);
+    }
+private:
+    const vector<EncodeParam>& encode_params_;
+    MessageRenderer& renderer_;
+};
+
+// Builtin benchmark data.  This is a list of RDATA (of RRs) in a response
+// from a root server for the query for "www.example.com" (as of this
+// implementation).  We use a real world example to make the case practical.
+const char* const rrsets_txt =
+    // AUTHORITY SECTION (NS)
+    "com. 172800 IN NS a.gtld-servers.net.\n"
+    "com. 172800 IN NS b.gtld-servers.net.\n"
+    "com. 172800 IN NS c.gtld-servers.net.\n"
+    "com. 172800 IN NS d.gtld-servers.net.\n"
+    "com. 172800 IN NS e.gtld-servers.net.\n"
+    "com. 172800 IN NS f.gtld-servers.net.\n"
+    "com. 172800 IN NS g.gtld-servers.net.\n"
+    "com. 172800 IN NS h.gtld-servers.net.\n"
+    "com. 172800 IN NS i.gtld-servers.net.\n"
+    "com. 172800 IN NS j.gtld-servers.net.\n"
+    "com. 172800 IN NS k.gtld-servers.net.\n"
+    "com. 172800 IN NS l.gtld-servers.net.\n"
+    "com. 172800 IN NS m.gtld-servers.net.\n"
+    // AUTHORITY SECTION (DS)
+    "com. 86400 IN DS 30909 8 2 "
+    "E2D3C916F6DEEAC73294E8268FB5885044A833FC5459588F4A9184CFC41A5766\n"
+    // AUTHORITY SECTION (RRSIG for DS)
+    "com. 86400 IN RRSIG DS 8 1 86400 20120822000000 20120814230000 50398 . "
+    "lcIpLRq4s91Fh1FihDXiDvVMMRqgy2jjlpiP4Y6sSjIrLue6Boi7xraj"
+    "Ouka34ubpl4KuIcopWe99LI/7Npvq0MYr9DaqfnX9dTW6Vc2C7/hKSsz"
+    "POYjraZZA3SCApgfNVzq+AscYlShi56f1vm7DQWw1eh1wHLdatidrQwNyDo=\n"
+    // ADDITIONAL SECTION
+    "a.gtld-servers.net. 172800 IN A 192.5.6.30\n"
+    "b.gtld-servers.net. 172800 IN A 192.33.14.30\n"
+    "c.gtld-servers.net. 172800 IN A 192.26.92.30\n"
+    "d.gtld-servers.net. 172800 IN A 192.31.80.30\n"
+    "e.gtld-servers.net. 172800 IN A 192.12.94.30\n"
+    "f.gtld-servers.net. 172800 IN A 192.35.51.30\n"
+    "g.gtld-servers.net. 172800 IN A 192.42.93.30\n"
+    "h.gtld-servers.net. 172800 IN A 192.54.112.30\n"
+    "i.gtld-servers.net. 172800 IN A 192.43.172.30\n"
+    "j.gtld-servers.net. 172800 IN A 192.48.79.30\n"
+    "k.gtld-servers.net. 172800 IN A 192.52.178.30\n"
+    "l.gtld-servers.net. 172800 IN A 192.41.162.30\n"
+    "m.gtld-servers.net. 172800 IN A 192.55.83.30\n"
+    "a.gtld-servers.net. 172800 IN AAAA 2001:503:a83e::2:30\n"
+    "b.gtld-servers.net. 172800 IN AAAA 2001:503:231d::2:30";
+
+void
+usage() {
+    std::cerr << "Usage: rdata_reader_bench [-n iterations]" << std::endl;
+    exit (1);
+}
+
+// Helper callback for masterLoad() used in main() to build test data.
+void
+setRRset(vector<ConstRRsetPtr>* rrsets, ConstRRsetPtr rrset) {
+    rrsets->push_back(rrset);
+}
+}
+
+int
+main(int argc, char* argv[]) {
+    int ch;
+    int iteration = 100000;
+    while ((ch = getopt(argc, argv, "n:")) != -1) {
+        switch (ch) {
+        case 'n':
+            iteration = atoi(optarg);
+            break;
+        case '?':
+        default:
+            usage();
+        }
+    }
+    argc -= optind;
+    argv += optind;
+    if (argc != 0) {
+        usage();
+    }
+
+    // Build test data.  rrsets will consist of a list of RRsets corresponding
+    // to rrsets_txt defined above.
+    vector<ConstRRsetPtr> rrsets;
+    std::stringstream rrsets_stream(rrsets_txt);
+    masterLoad(rrsets_stream, Name::ROOT_NAME(), RRClass::IN(),
+               boost::bind(setRRset, &rrsets, _1));
+
+    // Create EncodeParam for each RRset (possibly with RRSIG) in rrsets,
+    // and store them in encode_param_list.  It's the direct test input.
+    vector<EncodeParam> encode_param_list;
+    RdataEncoder encoder;
+    encode_param_list.push_back(EncodeParam(encoder, rrsets.at(0)));
+    encode_param_list.push_back(EncodeParam(encoder, rrsets.at(1),
+                                            rrsets.at(2)));
+    for (vector<ConstRRsetPtr>::const_iterator it = rrsets.begin() + 3;
+         it != rrsets.end();
+         ++it) {
+        encode_param_list.push_back(EncodeParam(encoder, *it));
+    }
+
+    // The benchmark test uses a message renderer.  Create it now and keep
+    // using it throughout the test.
+    isc::util::OutputBuffer buffer(4096); // 4096 should be sufficiently large
+    MessageRenderer renderer;
+    renderer.setBuffer(&buffer);
+
+    std::cout << "Benchmark for RdataReader" << std::endl;
+    BenchMark<ReaderBenchMark>(iteration,
+                                ReaderBenchMark(encode_param_list, renderer));
+    return (0);
+}
diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h
index 2c4bd59..3ee1552 100644
--- a/src/lib/datasrc/memory/domaintree.h
+++ b/src/lib/datasrc/memory/domaintree.h
@@ -44,7 +44,7 @@ namespace memory {
 
 /// Forward declare DomainTree class here is convinent for following
 /// friend class declare inside DomainTreeNode and DomainTreeNodeChain
-template <typename T, typename DT>
+template <typename T>
 class DomainTree;
 
 /// \brief \c DomainTreeNode is used by DomainTree to store any data
@@ -79,18 +79,18 @@ class DomainTree;
 /// immediately following the main node object.  The size of the
 /// allocated space for the labels data is encoded by borrowing some
 /// bits of the "flags" field.
-template <typename T, typename DT>
+template <typename T>
 class DomainTreeNode : public boost::noncopyable {
 private:
     /// The DomainTreeNode is meant for use from within DomainTree, so
     /// it has access to it.
-    friend class DomainTree<T, DT>;
+    friend class DomainTree<T>;
 
     /// \brief Just a type alias
     ///
     /// We are going to use a lot of these offset pointers here and they
     /// have a long name.
-    typedef boost::interprocess::offset_ptr<DomainTreeNode<T, DT> >
+    typedef boost::interprocess::offset_ptr<DomainTreeNode<T> >
         DomainTreeNodePtr;
 
     /// \name Constructors
@@ -131,12 +131,12 @@ private:
     ///
     /// \param mem_sgmt A \c MemorySegment from which memory for the new
     /// \c DomainTreeNode is allocated.
-    static DomainTreeNode<T, DT>* create(util::MemorySegment& mem_sgmt,
-                             const dns::LabelSequence& labels)
+    static DomainTreeNode<T>* create(util::MemorySegment& mem_sgmt,
+                                     const dns::LabelSequence& labels)
     {
         const size_t labels_len = labels.getSerializedLength();
-        void* p = mem_sgmt.allocate(sizeof(DomainTreeNode<T, DT>) + labels_len);
-        DomainTreeNode<T, DT>* node = new(p) DomainTreeNode<T, DT>(labels_len);
+        void* p = mem_sgmt.allocate(sizeof(DomainTreeNode<T>) + labels_len);
+        DomainTreeNode<T>* node = new(p) DomainTreeNode<T>(labels_len);
         labels.serialize(node->getLabelsData(), labels_len);
         return (node);
     }
@@ -151,11 +151,12 @@ private:
     /// that was originally created by the \c create() method (the behavior
     /// is undefined if this condition isn't met).
     static void destroy(util::MemorySegment& mem_sgmt,
-                        DomainTreeNode<T, DT>* node) {
+                        DomainTreeNode<T>* node)
+    {
         const size_t labels_capacity = node->labels_capacity_;
-        node->~DomainTreeNode<T, DT>();
+        node->~DomainTreeNode<T>();
         mem_sgmt.deallocate(node,
-                            sizeof(DomainTreeNode<T, DT>) + labels_capacity);
+                            sizeof(DomainTreeNode<T>) + labels_capacity);
     }
 
     /// \brief Reset node's label sequence to a new one.
@@ -247,17 +248,22 @@ public:
 
     /// \brief Set the data stored in the node.
     ///
-    /// Any old data is destroyed.
+    /// If there is old data, a pointer to the data will be returned;
+    /// otherwise NULL will be returned.  The caller is responsible for
+    /// releasing any resource for the old data if it's not needed any more.
+    /// See also the note about data ownership in the \c DomainTree
+    /// description.
     ///
-    /// \param mem_sgmt The \c MemorySegment that allocated memory for
-    ///                 the node data.
-    /// \param data The new data to set.
-    void setData(util::MemorySegment& mem_sgmt, T* data) {
-        if (data_) {
-            const DT deleter;
-            deleter(mem_sgmt, data_.get());
-        }
+    /// \c data can be NULL, in which case it effectively clears any existing
+    /// old data.
+    ///
+    /// \param data The new data to set.  It can be NULL.
+    /// \return A pointer to the old data or NULL if the node doesn't have
+    /// data.
+    T* setData(T* data) {
+        T* olddata = data_.get();
         data_ = data;
+        return (olddata);
     }
     //@}
 
@@ -309,7 +315,7 @@ public:
 private:
     /// \name Callback related methods
     ///
-    /// See the description of \c DomainTree<T, DT>::find() at \ref callback
+    /// See the description of \c DomainTree<T>::find() at \ref callback
     /// about callbacks.
     ///
     /// These methods never throw an exception.
@@ -360,7 +366,7 @@ public:
     /// (which should be absolute), it will return \c NULL.
     ///
     /// This method never throws an exception.
-    const DomainTreeNode<T, DT>* getUpperNode() const;
+    const DomainTreeNode<T>* getUpperNode() const;
 
 private:
     /// \brief return the next node which is bigger than current node
@@ -378,7 +384,7 @@ private:
     /// returns \c NULL.
     ///
     /// This method never throws an exception.
-    const DomainTreeNode<T, DT>* successor() const;
+    const DomainTreeNode<T>* successor() const;
 
     /// \brief return the next node which is smaller than current node
     /// in the same subtree
@@ -395,7 +401,7 @@ private:
     /// returns \c NULL.
     ///
     /// This method never throws an exception.
-    const DomainTreeNode<T, DT>* predecessor() const;
+    const DomainTreeNode<T>* predecessor() const;
 
     /// \brief private shared implementation of successor and predecessor
     ///
@@ -408,11 +414,11 @@ private:
     /// The overhead of the member pointers should be optimised out, as this
     /// will probably get completely inlined into predecessor and successor
     /// methods.
-    const DomainTreeNode<T, DT>*
-        abstractSuccessor(typename DomainTreeNode<T, DT>::DomainTreeNodePtr
-                              DomainTreeNode<T, DT>::*left,
-                          typename DomainTreeNode<T, DT>::DomainTreeNodePtr
-                              DomainTreeNode<T, DT>::*right)
+    const DomainTreeNode<T>*
+        abstractSuccessor(typename DomainTreeNode<T>::DomainTreeNodePtr
+                          DomainTreeNode<T>::*left,
+                          typename DomainTreeNode<T>::DomainTreeNodePtr
+                          DomainTreeNode<T>::*right)
         const;
 
     /// \name Data to maintain the rbtree structure.
@@ -425,29 +431,29 @@ private:
     //@{
     DomainTreeNodePtr parent_;
     /// \brief Access the parent_ as bare pointer.
-    DomainTreeNode<T, DT>* getParent() {
+    DomainTreeNode<T>* getParent() {
         return (parent_.get());
     }
     /// \brief Access the parent_ as bare pointer, const.
-    const DomainTreeNode<T, DT>* getParent() const {
+    const DomainTreeNode<T>* getParent() const {
         return (parent_.get());
     }
     DomainTreeNodePtr left_;
     /// \brief Access the left_ as bare pointer.
-    DomainTreeNode<T, DT>* getLeft() {
+    DomainTreeNode<T>* getLeft() {
         return (left_.get());
     }
     /// \brief Access the left_ as bare pointer, const.
-    const DomainTreeNode<T, DT>* getLeft() const {
+    const DomainTreeNode<T>* getLeft() const {
         return (left_.get());
     }
     DomainTreeNodePtr right_;
     /// \brief Access the right_ as bare pointer.
-    DomainTreeNode<T, DT>* getRight() {
+    DomainTreeNode<T>* getRight() {
         return (right_.get());
     }
     /// \brief Access the right_ as bare pointer, const.
-    const DomainTreeNode<T, DT>* getRight() const {
+    const DomainTreeNode<T>* getRight() const {
         return (right_.get());
     }
     //@}
@@ -463,11 +469,11 @@ private:
     ///     avoiding storage of the same domain labels multiple times.
     DomainTreeNodePtr down_;
     /// \brief Access the down_ as bare pointer.
-    DomainTreeNode<T, DT>* getDown() {
+    DomainTreeNode<T>* getDown() {
         return (down_.get());
     }
     /// \brief Access the down_ as bare pointer, const.
-    const DomainTreeNode<T, DT>* getDown() const {
+    const DomainTreeNode<T>* getDown() const {
         return (down_.get());
     }
 
@@ -494,8 +500,8 @@ private:
     BOOST_STATIC_ASSERT((1 << 9) > dns::LabelSequence::MAX_SERIALIZED_LENGTH);
 };
 
-template <typename T, typename DT>
-DomainTreeNode<T, DT>::DomainTreeNode(size_t labels_capacity) :
+template <typename T>
+DomainTreeNode<T>::DomainTreeNode(size_t labels_capacity) :
     parent_(NULL),
     left_(NULL),
     right_(NULL),
@@ -506,14 +512,14 @@ DomainTreeNode<T, DT>::DomainTreeNode(size_t labels_capacity) :
 {
 }
 
-template <typename T, typename DT>
-DomainTreeNode<T, DT>::~DomainTreeNode() {
+template <typename T>
+DomainTreeNode<T>::~DomainTreeNode() {
 }
 
-template <typename T, typename DT>
-const DomainTreeNode<T, DT>*
-DomainTreeNode<T, DT>::getUpperNode() const {
-    const DomainTreeNode<T, DT>* current = this;
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::getUpperNode() const {
+    const DomainTreeNode<T>* current = this;
 
     // current would never be equal to NULL here (in a correct tree
     // implementation)
@@ -524,12 +530,11 @@ DomainTreeNode<T, DT>::getUpperNode() const {
     return (current->getParent());
 }
 
-template <typename T, typename DT>
-const DomainTreeNode<T, DT>*
-DomainTreeNode<T, DT>::abstractSuccessor(typename DomainTreeNode<T, DT>::DomainTreeNodePtr
-                                            DomainTreeNode<T, DT>::*left,
-                                        typename DomainTreeNode<T, DT>::DomainTreeNodePtr
-                                            DomainTreeNode<T, DT>::*right)
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::abstractSuccessor(
+    typename DomainTreeNode<T>::DomainTreeNodePtr DomainTreeNode<T>::*left,
+    typename DomainTreeNode<T>::DomainTreeNodePtr DomainTreeNode<T>::*right)
     const
 {
     // This function is written as a successor. It becomes predecessor if
@@ -537,12 +542,12 @@ DomainTreeNode<T, DT>::abstractSuccessor(typename DomainTreeNode<T, DT>::DomainT
     // the left pointer points to right and vice versa. Don't get confused
     // by the idea, just imagine the pointers look into a mirror.
 
-    const DomainTreeNode<T, DT>* current = this;
+    const DomainTreeNode<T>* current = this;
     // If it has right node, the successor is the left-most node of the right
     // subtree.
     if ((current->*right).get() != NULL) {
         current = (current->*right).get();
-        const DomainTreeNode<T, DT>* left_n;
+        const DomainTreeNode<T>* left_n;
         while ((left_n = (current->*left).get()) != NULL) {
             current = left_n;
         }
@@ -552,7 +557,7 @@ DomainTreeNode<T, DT>::abstractSuccessor(typename DomainTreeNode<T, DT>::DomainT
     // Otherwise go up until we find the first left branch on our path to
     // root.  If found, the parent of the branch is the successor.
     // Otherwise, we return the null node
-    const DomainTreeNode<T, DT>* parent = current->getParent();
+    const DomainTreeNode<T>* parent = current->getParent();
     while ((!current->isSubTreeRoot()) &&
            (current == (parent->*right).get())) {
         current = parent;
@@ -566,19 +571,19 @@ DomainTreeNode<T, DT>::abstractSuccessor(typename DomainTreeNode<T, DT>::DomainT
     }
 }
 
-template <typename T, typename DT>
-const DomainTreeNode<T, DT>*
-DomainTreeNode<T, DT>::successor() const {
-    return (abstractSuccessor(&DomainTreeNode<T, DT>::left_,
-                              &DomainTreeNode<T, DT>::right_));
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::successor() const {
+    return (abstractSuccessor(&DomainTreeNode<T>::left_,
+                              &DomainTreeNode<T>::right_));
 }
 
-template <typename T, typename DT>
-const DomainTreeNode<T, DT>*
-DomainTreeNode<T, DT>::predecessor() const {
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::predecessor() const {
     // Swap the left and right pointers for the abstractSuccessor
-    return (abstractSuccessor(&DomainTreeNode<T, DT>::right_,
-                              &DomainTreeNode<T, DT>::left_));
+    return (abstractSuccessor(&DomainTreeNode<T>::right_,
+                              &DomainTreeNode<T>::left_));
 }
 
 /// \brief DomainTreeNodeChain stores detailed information of \c
@@ -615,11 +620,11 @@ DomainTreeNode<T, DT>::predecessor() const {
 /// DomainTree.
 /// This is the reason why manipulation methods such as \c push() and \c pop()
 /// are private (and not shown in the doxygen document).
-template <typename T, typename DT>
+template <typename T>
 class DomainTreeNodeChain {
     /// DomainTreeNodeChain is initialized by DomainTree, only DomainTree has
     /// knowledge to manipulate it.
-    friend class DomainTree<T, DT>;
+    friend class DomainTree<T>;
 public:
     /// \name Constructors and Assignment Operator.
     ///
@@ -639,8 +644,8 @@ public:
     {}
 
 private:
-    DomainTreeNodeChain(const DomainTreeNodeChain<T, DT>&);
-    DomainTreeNodeChain<T, DT>& operator=(const DomainTreeNodeChain<T, DT>&);
+    DomainTreeNodeChain(const DomainTreeNodeChain<T>&);
+    DomainTreeNodeChain<T>& operator=(const DomainTreeNodeChain<T>&);
     //@}
 
 public:
@@ -668,7 +673,7 @@ public:
     /// tree is empty), this method returns \c NULL.
     ///
     /// \exception None
-    const DomainTreeNode<T, DT>* getLastComparedNode() const {
+    const DomainTreeNode<T>* getLastComparedNode() const {
         return (last_compared_);
     }
 
@@ -708,7 +713,7 @@ public:
                       "called on an empty chain");
         }
 
-        const DomainTreeNode<T, DT>* top_node = top();
+        const DomainTreeNode<T>* top_node = top();
         isc::dns::Name absolute_name = top_node->getName();
         int node_count = node_count_ - 1;
         while (node_count > 0) {
@@ -736,7 +741,7 @@ private:
     /// root node of DomainTree
     ///
     /// \exception None
-    const DomainTreeNode<T, DT>* top() const {
+    const DomainTreeNode<T>* top() const {
         assert(!isEmpty());
         return (nodes_[node_count_ - 1]);
     }
@@ -759,7 +764,7 @@ private:
     /// otherwise the node should be the root node of DomainTree.
     ///
     /// \exception None
-    void push(const DomainTreeNode<T, DT>* node) {
+    void push(const DomainTreeNode<T>* node) {
         assert(node_count_ < RBT_MAX_LEVEL);
         nodes_[node_count_++] = node;
     }
@@ -771,8 +776,8 @@ private:
     const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS;
 
     int node_count_;
-    const DomainTreeNode<T, DT>* nodes_[RBT_MAX_LEVEL];
-    const DomainTreeNode<T, DT>* last_compared_;
+    const DomainTreeNode<T>* nodes_[RBT_MAX_LEVEL];
+    const DomainTreeNode<T>* last_compared_;
     isc::dns::NameComparisonResult last_comparison_;
 };
 
@@ -784,11 +789,21 @@ private:
  *  \brief \c DomainTree class represents all the domains with the same suffix.
  *      It can be used to store the domains in one zone, for example.
  *
- *  DomainTree is a generic map from domain names to any kind of
- *  data. Internally, it uses a red-black tree. However, it isn't one
- *  tree containing everything.  Subdomains are trees, so this structure
- *  is recursive - trees inside trees.  But, from the interface point of
- *  view, it is opaque data structure.
+ * DomainTree is a generic map from domain names to any kind of
+ * data. Internally, it uses a red-black tree. However, it isn't one
+ * tree containing everything.  Subdomains are trees, so this structure
+ * is recursive - trees inside trees.  But, from the interface point of
+ * view, it is opaque data structure.
+ *
+ * The data of DomainTree are set by the application via the
+ * DomainTreeNode::setData() method.  The ownership of the data isn't
+ * transferred to the DomainTree; if the application replaces existing
+ * data for a specific name in DomainTree by setData(), the application is
+ * responsible for releasing any resource for the old data.  When the
+ * application destroys the entire DomainTree by the \c destroy() method,
+ * it needs to pass a deleter object for any remained data in the DomainTree.
+ * The DomainTree will call that object with all the data in the tree so that
+ * the application complete the cleanup about the remaining data.
  *
  *  \c DomainTree splits the domain space into hierarchy red black trees; nodes
  * in one tree has the same base name. The benefit of this struct is that:
@@ -807,12 +822,8 @@ private:
  * the \c insert() method will still return \c ALREADYEXISTS regardless of
  * the search policy.
  *
- * The template parameters taken by \c DomainTree are \c T (the type of
- * data which is stored by the tree) and \c DT (a type whose instance is
- * used to destroy data stored in the tree). <code>operator()</code> is
- * called on a \c DT instance and passed a pointer to the data
- * (<code>T*</code>) to be destroyed. This method should be written to
- * accept \c NULL arguments.
+ * The template parameter taken by \c DomainTree is \c T (the type of
+ * data which is stored by the tree).
  *
  * \anchor diagram
  *
@@ -848,9 +859,9 @@ private:
  *  \todo
  *  - add remove interface
  */
-template <typename T, typename DT>
+template <typename T>
 class DomainTree : public boost::noncopyable {
-    friend class DomainTreeNode<T, DT>;
+    friend class DomainTreeNode<T>;
 public:
     /// \brief The return value for the \c find() and insert() methods
     enum Result {
@@ -876,8 +887,8 @@ public:
     static DomainTree* create(util::MemorySegment& mem_sgmt,
                               bool return_empty_node = false)
     {
-        void* p = mem_sgmt.allocate(sizeof(DomainTree<T, DT>));
-        return (new(p) DomainTree<T, DT>(return_empty_node));
+        void* p = mem_sgmt.allocate(sizeof(DomainTree<T>));
+        return (new(p) DomainTree<T>(return_empty_node));
     }
 
     /// \brief Destruct and deallocate \c DomainTree
@@ -885,6 +896,12 @@ public:
     /// This method also destroys and deallocates all nodes inserted to the
     /// tree.
     ///
+    /// The template parameter, \c DataDeleter, is a type whose instance is
+    /// used to destroy data stored in the tree nodes.  It must have a
+    /// <code>operator()</code> method, which is called on a \c DataDeleter
+    /// instance and passed a pointer to the data (<code>T*</code>) to be
+    /// destroyed. This method should be written to accept a \c NULL argument.
+    ///
     /// \note The memory segment (\c mem_sgmt) must be the same one that
     /// was originally used to allocate memory for the tree (and for all
     /// nodes inserted to the tree, due to the requirement of \c insert()),
@@ -905,11 +922,15 @@ public:
     /// \param tree A non NULL pointer to a valid \c DomainTree object
     /// that was originally created by the \c create() method (the behavior
     /// is undefined if this condition isn't met).
+    /// \param deleter A deleter functor or function to delete node data.
+    template <typename DataDeleter>
     static void destroy(util::MemorySegment& mem_sgmt,
-                        DomainTree<T, DT>* tree) {
-        tree->deleteAllNodes(mem_sgmt);
-        tree->~DomainTree<T, DT>();
-        mem_sgmt.deallocate(tree, sizeof(DomainTree<T, DT>));
+                        DomainTree<T>* tree,
+                        DataDeleter deleter)
+    {
+        tree->deleteAllNodes(mem_sgmt, deleter);
+        tree->~DomainTree<T>();
+        mem_sgmt.deallocate(tree, sizeof(DomainTree<T>));
     }
 
 private:
@@ -926,7 +947,7 @@ private:
     /// \brief The destructor.
     ///
     /// An object of this class is always expected to be destroyed explicitly
-    /// by \c destroy(), so the constructor is hidden as private.
+    /// by \c destroy(), so the destructor is hidden as private.
     ///
     /// \note DomainTree is not intended to be inherited so the destructor
     /// is not virtual
@@ -987,8 +1008,8 @@ public:
     ///
     /// Acts as described in the \ref find section.
     Result find(const isc::dns::Name& name,
-                DomainTreeNode<T, DT>** node) const {
-        DomainTreeNodeChain<T, DT> node_path;
+                DomainTreeNode<T>** node) const {
+        DomainTreeNodeChain<T> node_path;
         const isc::dns::LabelSequence ls(name);
         return (find<void*>(ls, node, node_path, NULL, NULL));
     }
@@ -998,9 +1019,9 @@ public:
     /// Acts as described in the \ref find section, but returns immutable node
     /// pointer.
     Result find(const isc::dns::Name& name,
-                const DomainTreeNode<T, DT>** node) const {
-        DomainTreeNodeChain<T, DT> node_path;
-        DomainTreeNode<T, DT> *target_node = NULL;
+                const DomainTreeNode<T>** node) const {
+        DomainTreeNodeChain<T> node_path;
+        DomainTreeNode<T> *target_node = NULL;
         const isc::dns::LabelSequence ls(name);
         Result ret = (find<void*>(ls, &target_node, node_path, NULL, NULL));
         if (ret != NOTFOUND) {
@@ -1012,8 +1033,8 @@ public:
     /// \brief Simple find, with node_path tracking
     ///
     /// Acts as described in the \ref find section.
-    Result find(const isc::dns::Name& name, DomainTreeNode<T, DT>** node,
-                DomainTreeNodeChain<T, DT>& node_path) const
+    Result find(const isc::dns::Name& name, DomainTreeNode<T>** node,
+                DomainTreeNodeChain<T>& node_path) const
     {
         const isc::dns::LabelSequence ls(name);
         return (find<void*>(ls, node, node_path, NULL, NULL));
@@ -1023,10 +1044,10 @@ public:
     ///
     /// Acts as described in the \ref find section, but returns immutable node
     /// pointer.
-    Result find(const isc::dns::Name& name, const DomainTreeNode<T, DT>** node,
-                DomainTreeNodeChain<T, DT>& node_path) const
+    Result find(const isc::dns::Name& name, const DomainTreeNode<T>** node,
+                DomainTreeNodeChain<T>& node_path) const
     {
-        DomainTreeNode<T, DT> *target_node = NULL;
+        DomainTreeNode<T> *target_node = NULL;
         const isc::dns::LabelSequence ls(name);
         Result ret = (find<void*>(ls, &target_node, node_path, NULL, NULL));
         if (ret != NOTFOUND) {
@@ -1041,12 +1062,12 @@ public:
     /// node pointer.
     template <typename CBARG>
     Result find(const isc::dns::Name& name,
-                const DomainTreeNode<T, DT>** node,
-                DomainTreeNodeChain<T, DT>& node_path,
-                bool (*callback)(const DomainTreeNode<T, DT>&, CBARG),
+                const DomainTreeNode<T>** node,
+                DomainTreeNodeChain<T>& node_path,
+                bool (*callback)(const DomainTreeNode<T>&, CBARG),
                 CBARG callback_arg) const
     {
-        DomainTreeNode<T, DT>* target_node = NULL;
+        DomainTreeNode<T>* target_node = NULL;
         const isc::dns::LabelSequence ls(name);
         Result ret = find(ls, &target_node, node_path, callback,
                           callback_arg);
@@ -1130,9 +1151,9 @@ public:
     ///     \c true, it returns immediately with the current node.
     template <typename CBARG>
     Result find(const isc::dns::LabelSequence& target_labels_orig,
-                DomainTreeNode<T, DT>** node,
-                DomainTreeNodeChain<T, DT>& node_path,
-                bool (*callback)(const DomainTreeNode<T, DT>&, CBARG),
+                DomainTreeNode<T>** node,
+                DomainTreeNodeChain<T>& node_path,
+                bool (*callback)(const DomainTreeNode<T>&, CBARG),
                 CBARG callback_arg) const;
 
     /// \brief Simple find returning immutable node.
@@ -1141,12 +1162,12 @@ public:
     /// node pointer.
     template <typename CBARG>
     Result find(const isc::dns::LabelSequence& target_labels,
-                const DomainTreeNode<T, DT>** node,
-                DomainTreeNodeChain<T, DT>& node_path,
-                bool (*callback)(const DomainTreeNode<T, DT>&, CBARG),
+                const DomainTreeNode<T>** node,
+                DomainTreeNodeChain<T>& node_path,
+                bool (*callback)(const DomainTreeNode<T>&, CBARG),
                 CBARG callback_arg) const
     {
-        DomainTreeNode<T, DT>* target_node = NULL;
+        DomainTreeNode<T>* target_node = NULL;
         Result ret = find(target_labels, &target_node, node_path,
                           callback, callback_arg);
         if (ret != NOTFOUND) {
@@ -1179,8 +1200,8 @@ public:
     ///
     /// \return An \c DomainTreeNode that is next bigger than \c node;
     /// if \c node is the largest, \c NULL will be returned.
-    const DomainTreeNode<T, DT>*
-    nextNode(DomainTreeNodeChain<T, DT>& node_path) const;
+    const DomainTreeNode<T>*
+    nextNode(DomainTreeNodeChain<T>& node_path) const;
 
     /// \brief return the next smaller node in DNSSEC order from a node
     ///     searched by DomainTree::find().
@@ -1204,8 +1225,8 @@ public:
     ///
     /// \return An \c DomainTreeNode that is next smaller than \c node;
     /// if \c node is the smallest, \c NULL will be returned.
-    const DomainTreeNode<T, DT>*
-    previousNode(DomainTreeNodeChain<T, DT>& node_path) const;
+    const DomainTreeNode<T>*
+    previousNode(DomainTreeNodeChain<T>& node_path) const;
 
     /// \brief Get the total number of nodes in the tree
     ///
@@ -1270,7 +1291,7 @@ public:
     ///  - ALREADYEXISTS There was already a node of that name, so it was not
     ///     added.
     Result insert(util::MemorySegment& mem_sgmt, const isc::dns::Name& name,
-                  DomainTreeNode<T, DT>** inserted_node);
+                  DomainTreeNode<T>** inserted_node);
 
     /// \brief Delete all tree nodes.
     ///
@@ -1279,7 +1300,8 @@ public:
     /// \param mem_sgmt The \c MemorySegment object used to insert the nodes
     /// (which was also used for creating the tree due to the requirement of
     /// \c inert()).
-    void deleteAllNodes(util::MemorySegment& mem_sgmt);
+    template <typename DataDeleter>
+    void deleteAllNodes(util::MemorySegment& mem_sgmt, DataDeleter deleter);
 
     /// \brief Swaps two tree's contents.
     ///
@@ -1289,7 +1311,7 @@ public:
     ///
     /// This acts the same as many std::*.swap functions, exchanges the
     /// contents. This doesn't throw anything.
-    void swap(DomainTree<T, DT>& other) {
+    void swap(DomainTree<T>& other) {
         std::swap(root_, other.root_);
         std::swap(node_count_, other.node_count_);
     }
@@ -1299,31 +1321,32 @@ private:
     /// \name DomainTree balance functions
     //@{
     void
-    insertRebalance(typename DomainTreeNode<T, DT>::DomainTreeNodePtr* root,
-                    DomainTreeNode<T, DT>* node);
+    insertRebalance(typename DomainTreeNode<T>::DomainTreeNodePtr* root,
+                    DomainTreeNode<T>* node);
 
-    DomainTreeNode<T, DT>*
-    rightRotate(typename DomainTreeNode<T, DT>::DomainTreeNodePtr* root,
-                DomainTreeNode<T, DT>* node);
+    DomainTreeNode<T>*
+    rightRotate(typename DomainTreeNode<T>::DomainTreeNodePtr* root,
+                DomainTreeNode<T>* node);
 
-    DomainTreeNode<T, DT>*
-    leftRotate(typename DomainTreeNode<T, DT>::DomainTreeNodePtr* root,
-               DomainTreeNode<T, DT>* node);
+    DomainTreeNode<T>*
+    leftRotate(typename DomainTreeNode<T>::DomainTreeNodePtr* root,
+               DomainTreeNode<T>* node);
     //@}
 
     /// \name Helper functions
     //@{
     /// \brief delete tree whose root is equal to node
+    template <typename DataDeleter>
     void deleteHelper(util::MemorySegment& mem_sgmt,
-                      DomainTreeNode<T, DT> *node,
-                      const DT& deleter);
+                      DomainTreeNode<T> *node,
+                      const DataDeleter& deleter);
 
     /// \brief Print the information of given DomainTreeNode.
-    void dumpTreeHelper(std::ostream& os, const DomainTreeNode<T, DT>* node,
+    void dumpTreeHelper(std::ostream& os, const DomainTreeNode<T>* node,
                         unsigned int depth) const;
 
     /// \brief Print the information of given DomainTreeNode for dot.
-    int dumpDotHelper(std::ostream& os, const DomainTreeNode<T, DT>* node,
+    int dumpDotHelper(std::ostream& os, const DomainTreeNode<T>* node,
                       int* nodecount, bool show_pointers) const;
 
     /// \brief Indentation helper function for dumpTree
@@ -1336,78 +1359,80 @@ private:
     /// the entire tree.  This ensures that a pointer to a node keeps its
     /// semantics even if the tree structure is changed (as long as the node
     /// itself remains valid).
-    void nodeFission(util::MemorySegment& mem_sgmt, DomainTreeNode<T, DT>& node,
+    void nodeFission(util::MemorySegment& mem_sgmt, DomainTreeNode<T>& node,
                      const isc::dns::LabelSequence& new_prefix,
                      const isc::dns::LabelSequence& new_suffix);
     //@}
 
-    typename DomainTreeNode<T, DT>::DomainTreeNodePtr root_;
+    typename DomainTreeNode<T>::DomainTreeNodePtr root_;
     /// the node count of current tree
     unsigned int node_count_;
     /// search policy for domaintree
     const bool needsReturnEmptyNode_;
 };
 
-template <typename T, typename DT>
-DomainTree<T, DT>::DomainTree(bool returnEmptyNode) :
+template <typename T>
+DomainTree<T>::DomainTree(bool returnEmptyNode) :
     root_(NULL),
     node_count_(0),
     needsReturnEmptyNode_(returnEmptyNode)
 {
 }
 
-template <typename T, typename DT>
-DomainTree<T, DT>::~DomainTree() {
+template <typename T>
+DomainTree<T>::~DomainTree() {
     assert(node_count_ == 0);
 }
 
-template <typename T, typename DT>
+template <typename T>
+template <typename DataDeleter>
 void
-DomainTree<T, DT>::deleteHelper(util::MemorySegment& mem_sgmt,
-                                DomainTreeNode<T, DT>* root,
-                                const DT& deleter) {
+DomainTree<T>::deleteHelper(util::MemorySegment& mem_sgmt,
+                            DomainTreeNode<T>* root,
+                            const DataDeleter& deleter)
+{
     while (root != NULL) {
         // If there is a left, right or down node, walk into it and
         // iterate.
         if (root->getLeft() != NULL) {
-            DomainTreeNode<T, DT>* node = root;
+            DomainTreeNode<T>* node = root;
             root = root->getLeft();
             node->left_ = NULL;
         } else if (root->getRight() != NULL) {
-            DomainTreeNode<T, DT>* node = root;
+            DomainTreeNode<T>* node = root;
             root = root->getRight();
             node->right_ = NULL;
         } else if (root->getDown() != NULL) {
-            DomainTreeNode<T, DT>* node = root;
+            DomainTreeNode<T>* node = root;
             root = root->getDown();
             node->down_ = NULL;
         } else {
             // There are no left, right or down nodes, so we can
             // free this one and go back to its parent.
-            DomainTreeNode<T, DT>* node = root;
+            DomainTreeNode<T>* node = root;
             root = root->getParent();
-            deleter(mem_sgmt, node->data_.get());
-            DomainTreeNode<T, DT>::destroy(mem_sgmt, node);
+            deleter(node->data_.get());
+            DomainTreeNode<T>::destroy(mem_sgmt, node);
             --node_count_;
         }
     }
 }
 
-template <typename T, typename DT>
+template <typename T>
 template <typename CBARG>
-typename DomainTree<T, DT>::Result
-DomainTree<T, DT>::find(const isc::dns::LabelSequence& target_labels_orig,
-                       DomainTreeNode<T, DT>** target,
-                       DomainTreeNodeChain<T, DT>& node_path,
-                       bool (*callback)(const DomainTreeNode<T, DT>&, CBARG),
-                       CBARG callback_arg) const
+typename DomainTree<T>::Result
+DomainTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
+                    DomainTreeNode<T>** target,
+                    DomainTreeNodeChain<T>& node_path,
+                    bool (*callback)(const DomainTreeNode<T>&, CBARG),
+                    CBARG callback_arg) const
 {
     if (!node_path.isEmpty()) {
         isc_throw(isc::BadValue,
                   "DomainTree::find is given a non empty chain");
     }
 
-    DomainTreeNode<T, DT>* node = root_.get();
+    DomainTreeNode<T>* node = root_.get();
     Result ret = NOTFOUND;
     dns::LabelSequence target_labels(target_labels_orig);
 
@@ -1435,7 +1460,7 @@ DomainTree<T, DT>::find(const isc::dns::LabelSequence& target_labels_orig,
                     ret = PARTIALMATCH;
                     *target = node;
                     if (callback != NULL &&
-                        node->getFlag(DomainTreeNode<T, DT>::FLAG_CALLBACK)) {
+                        node->getFlag(DomainTreeNode<T>::FLAG_CALLBACK)) {
                         if ((callback)(*node, callback_arg)) {
                             break;
                         }
@@ -1454,20 +1479,20 @@ DomainTree<T, DT>::find(const isc::dns::LabelSequence& target_labels_orig,
     return (ret);
 }
 
-template <typename T, typename DT>
-const DomainTreeNode<T, DT>*
-DomainTree<T, DT>::nextNode(DomainTreeNodeChain<T, DT>& node_path) const {
+template <typename T>
+const DomainTreeNode<T>*
+DomainTree<T>::nextNode(DomainTreeNodeChain<T>& node_path) const {
     if (node_path.isEmpty()) {
         isc_throw(isc::BadValue,
                   "DomainTree::nextNode is given an empty chain");
     }
 
-    const DomainTreeNode<T, DT>* node = node_path.top();
+    const DomainTreeNode<T>* node = node_path.top();
     // if node has sub domain, the next domain is the smallest
     // domain in sub domain tree
-    const DomainTreeNode<T, DT>* down = node->getDown();
+    const DomainTreeNode<T>* down = node->getDown();
     if (down != NULL) {
-        const DomainTreeNode<T, DT>* left_most = down;
+        const DomainTreeNode<T>* left_most = down;
         while (left_most->getLeft() != NULL) {
             left_most = left_most->getLeft();
         }
@@ -1481,7 +1506,7 @@ DomainTree<T, DT>::nextNode(DomainTreeNodeChain<T, DT>& node_path) const {
     // up node doesn't have successor we gonna keep moving to up
     // level
     while (!node_path.isEmpty()) {
-        const DomainTreeNode<T, DT>* up_node_successor =
+        const DomainTreeNode<T>* up_node_successor =
             node_path.top()->successor();
         node_path.pop();
         if (up_node_successor != NULL) {
@@ -1493,9 +1518,9 @@ DomainTree<T, DT>::nextNode(DomainTreeNodeChain<T, DT>& node_path) const {
     return (NULL);
 }
 
-template <typename T, typename DT>
-const DomainTreeNode<T, DT>*
-DomainTree<T, DT>::previousNode(DomainTreeNodeChain<T, DT>& node_path) const {
+template <typename T>
+const DomainTreeNode<T>*
+DomainTree<T>::previousNode(DomainTreeNodeChain<T>& node_path) const {
     if (getNodeCount() == 0) {
         // Special case for empty trees. It would look every time like
         // we didn't search, because the last compared is empty. This is
@@ -1535,13 +1560,13 @@ DomainTree<T, DT>::previousNode(DomainTreeNodeChain<T, DT>& node_path) const {
                 // compared one (it is either the compared one, or some
                 // subdomain of it). There probably is not an easy trick
                 // for this, so we just find the correct place.
-                const DomainTreeNode<T, DT>* current(node_path.last_compared_);
+                const DomainTreeNode<T>* current(node_path.last_compared_);
                 while (current != NULL) {
                     node_path.push(current);
                     // Go a level down and as much right there as possible
                     current = current->getDown();
                     if (current != NULL) {
-                        const DomainTreeNode<T, DT>* right;
+                        const DomainTreeNode<T>* right;
                         while ((right = current->getRight()) != NULL) {
                             current = right;
                         }
@@ -1592,7 +1617,7 @@ DomainTree<T, DT>::previousNode(DomainTreeNodeChain<T, DT>& node_path) const {
         return (NULL);
     }
 
-    const DomainTreeNode<T, DT>* node(node_path.top());
+    const DomainTreeNode<T>* node(node_path.top());
 
     // Try going left in this tree
     node = node->predecessor();
@@ -1615,13 +1640,13 @@ DomainTree<T, DT>::previousNode(DomainTreeNodeChain<T, DT>& node_path) const {
     node_path.push(node);
 
     // Try going as deep as possible, keeping on the right side of the trees
-    const DomainTreeNode<T, DT>* down;
+    const DomainTreeNode<T>* down;
     while ((down = node->getDown()) != NULL) {
         // Move to the tree below
         node = down;
         if (node != NULL) {
             // And get as much to the right of the tree as possible
-            const DomainTreeNode<T, DT>* right;
+            const DomainTreeNode<T>* right;
             while ((right = node->getRight()) != NULL) {
                 node = right;
             }
@@ -1636,15 +1661,15 @@ DomainTree<T, DT>::previousNode(DomainTreeNodeChain<T, DT>& node_path) const {
     return (node);
 }
 
-template <typename T, typename DT>
-typename DomainTree<T, DT>::Result
-DomainTree<T, DT>::insert(util::MemorySegment& mem_sgmt,
-                          const isc::dns::Name& target_name,
-                          DomainTreeNode<T, DT>** new_node)
+template <typename T>
+typename DomainTree<T>::Result
+DomainTree<T>::insert(util::MemorySegment& mem_sgmt,
+                      const isc::dns::Name& target_name,
+                      DomainTreeNode<T>** new_node)
 {
-    DomainTreeNode<T, DT>* parent = NULL;
-    DomainTreeNode<T, DT>* current = root_.get();
-    DomainTreeNode<T, DT>* up_node = NULL;
+    DomainTreeNode<T>* parent = NULL;
+    DomainTreeNode<T>* current = root_.get();
+    DomainTreeNode<T>* up_node = NULL;
     isc::dns::LabelSequence target_labels(target_name);
 
     int order = -1;
@@ -1688,17 +1713,17 @@ DomainTree<T, DT>::insert(util::MemorySegment& mem_sgmt,
         }
     }
 
-    typename DomainTreeNode<T, DT>::DomainTreeNodePtr* current_root =
+    typename DomainTreeNode<T>::DomainTreeNodePtr* current_root =
         (up_node != NULL) ? &(up_node->down_) : &root_;
     // Once a new node is created, no exception will be thrown until the end
     // of the function, so we can simply create and hold a new node pointer.
-    DomainTreeNode<T, DT>* node = DomainTreeNode<T, DT>::create(mem_sgmt,
-                                                              target_labels);
+    DomainTreeNode<T>* node = DomainTreeNode<T>::create(mem_sgmt,
+                                                        target_labels);
     node->parent_ = parent;
     if (parent == NULL) {
         *current_root = node;
         // node is the new root of sub tree, so its init color is BLACK
-        node->setColor(DomainTreeNode<T, DT>::BLACK);
+        node->setColor(DomainTreeNode<T>::BLACK);
         node->setSubTreeRoot(true);
         node->parent_ = up_node;
     } else if (order < 0) {
@@ -1717,28 +1742,30 @@ DomainTree<T, DT>::insert(util::MemorySegment& mem_sgmt,
     return (SUCCESS);
 }
 
-template <typename T, typename DT>
+template <typename T>
+template <typename DataDeleter>
 void
-DomainTree<T, DT>::deleteAllNodes(util::MemorySegment& mem_sgmt) {
-    const DT deleter;
+DomainTree<T>::deleteAllNodes(util::MemorySegment& mem_sgmt,
+                              DataDeleter deleter)
+{
     deleteHelper(mem_sgmt, root_.get(), deleter);
     root_ = NULL;
 }
 
-template <typename T, typename DT>
+template <typename T>
 void
-DomainTree<T, DT>::nodeFission(util::MemorySegment& mem_sgmt,
-                               DomainTreeNode<T, DT>& node,
-                               const isc::dns::LabelSequence& new_prefix,
-                               const isc::dns::LabelSequence& new_suffix)
+DomainTree<T>::nodeFission(util::MemorySegment& mem_sgmt,
+                           DomainTreeNode<T>& node,
+                           const isc::dns::LabelSequence& new_prefix,
+                           const isc::dns::LabelSequence& new_suffix)
 {
     // Create and reset the labels.
     // Once a new node is created, no exception will be thrown until
     // the end of the function, and it will keep consistent behavior
     // (i.e., a weak form of strong exception guarantee) even if code
     // after the call to this function throws an exception.
-    DomainTreeNode<T, DT>* up_node = DomainTreeNode<T, DT>::create(mem_sgmt,
-                                                                   new_suffix);
+    DomainTreeNode<T>* up_node = DomainTreeNode<T>::create(mem_sgmt,
+                                                           new_suffix);
     node.resetLabels(new_prefix);
 
     up_node->parent_ = node.getParent();
@@ -1772,7 +1799,7 @@ DomainTree<T, DT>::nodeFission(util::MemorySegment& mem_sgmt,
 
     // set color of both nodes; the initial subtree node color is BLACK
     up_node->setColor(node.getColor());
-    node.setColor(DomainTreeNode<T, DT>::BLACK);
+    node.setColor(DomainTreeNode<T>::BLACK);
 
     // set the subtree root flag of both nodes
     up_node->setSubTreeRoot(node.isSubTreeRoot());
@@ -1782,27 +1809,27 @@ DomainTree<T, DT>::nodeFission(util::MemorySegment& mem_sgmt,
 }
 
 
-template <typename T, typename DT>
+template <typename T>
 void
-DomainTree<T, DT>::insertRebalance
-    (typename DomainTreeNode<T, DT>::DomainTreeNodePtr* root,
-     DomainTreeNode<T, DT>* node)
+DomainTree<T>::insertRebalance
+    (typename DomainTreeNode<T>::DomainTreeNodePtr* root,
+     DomainTreeNode<T>* node)
 {
-    DomainTreeNode<T, DT>* uncle;
-    DomainTreeNode<T, DT>* parent;
+    DomainTreeNode<T>* uncle;
+    DomainTreeNode<T>* parent;
     while (node != (*root).get() &&
            ((parent = node->getParent())->getColor()) ==
-           DomainTreeNode<T, DT>::RED) {
+           DomainTreeNode<T>::RED) {
         // Here, node->parent_ is not NULL and it is also red, so
         // node->parent_->parent_ is also not NULL.
         if (parent == parent->getParent()->getLeft()) {
             uncle = parent->getParent()->getRight();
 
             if (uncle != NULL && uncle->getColor() ==
-                DomainTreeNode<T, DT>::RED) {
-                parent->setColor(DomainTreeNode<T, DT>::BLACK);
-                uncle->setColor(DomainTreeNode<T, DT>::BLACK);
-                parent->getParent()->setColor(DomainTreeNode<T, DT>::RED);
+                DomainTreeNode<T>::RED) {
+                parent->setColor(DomainTreeNode<T>::BLACK);
+                uncle->setColor(DomainTreeNode<T>::BLACK);
+                parent->getParent()->setColor(DomainTreeNode<T>::RED);
                 node = parent->getParent();
             } else {
                 if (node == parent->getRight()) {
@@ -1810,18 +1837,18 @@ DomainTree<T, DT>::insertRebalance
                     leftRotate(root, node);
                     parent = node->getParent();
                 }
-                parent->setColor(DomainTreeNode<T, DT>::BLACK);
-                parent->getParent()->setColor(DomainTreeNode<T, DT>::RED);
+                parent->setColor(DomainTreeNode<T>::BLACK);
+                parent->getParent()->setColor(DomainTreeNode<T>::RED);
                 rightRotate(root, parent->getParent());
             }
         } else {
             uncle = parent->getParent()->getLeft();
 
             if (uncle != NULL && uncle->getColor() ==
-                DomainTreeNode<T, DT>::RED) {
-                parent->setColor(DomainTreeNode<T, DT>::BLACK);
-                uncle->setColor(DomainTreeNode<T, DT>::BLACK);
-                parent->getParent()->setColor(DomainTreeNode<T, DT>::RED);
+                DomainTreeNode<T>::RED) {
+                parent->setColor(DomainTreeNode<T>::BLACK);
+                uncle->setColor(DomainTreeNode<T>::BLACK);
+                parent->getParent()->setColor(DomainTreeNode<T>::RED);
                 node = parent->getParent();
             } else {
                 if (node == parent->getLeft()) {
@@ -1829,31 +1856,31 @@ DomainTree<T, DT>::insertRebalance
                     rightRotate(root, node);
                     parent = node->getParent();
                 }
-                parent->setColor(DomainTreeNode<T, DT>::BLACK);
-                parent->getParent()->setColor(DomainTreeNode<T, DT>::RED);
+                parent->setColor(DomainTreeNode<T>::BLACK);
+                parent->getParent()->setColor(DomainTreeNode<T>::RED);
                 leftRotate(root, parent->getParent());
             }
         }
     }
 
-    (*root)->setColor(DomainTreeNode<T, DT>::BLACK);
+    (*root)->setColor(DomainTreeNode<T>::BLACK);
 }
 
 
-template <typename T, typename DT>
-DomainTreeNode<T, DT>*
-DomainTree<T, DT>::leftRotate
-    (typename DomainTreeNode<T, DT>::DomainTreeNodePtr* root,
-     DomainTreeNode<T, DT>* node)
+template <typename T>
+DomainTreeNode<T>*
+DomainTree<T>::leftRotate
+    (typename DomainTreeNode<T>::DomainTreeNodePtr* root,
+     DomainTreeNode<T>* node)
 {
-    DomainTreeNode<T, DT>* const right = node->getRight();
-    DomainTreeNode<T, DT>* const rleft = right->getLeft();
+    DomainTreeNode<T>* const right = node->getRight();
+    DomainTreeNode<T>* const rleft = right->getLeft();
     node->right_ = rleft;
     if (rleft != NULL) {
         rleft->parent_ = node;
     }
 
-    DomainTreeNode<T, DT>* const parent = node->getParent();
+    DomainTreeNode<T>* const parent = node->getParent();
     right->parent_ = parent;
 
     if (!node->isSubTreeRoot()) {
@@ -1874,20 +1901,20 @@ DomainTree<T, DT>::leftRotate
     return (node);
 }
 
-template <typename T, typename DT>
-DomainTreeNode<T, DT>*
-DomainTree<T, DT>::rightRotate
-    (typename DomainTreeNode<T, DT>::DomainTreeNodePtr* root,
-     DomainTreeNode<T, DT>* node)
+template <typename T>
+DomainTreeNode<T>*
+DomainTree<T>::rightRotate
+    (typename DomainTreeNode<T>::DomainTreeNodePtr* root,
+     DomainTreeNode<T>* node)
 {
-    DomainTreeNode<T, DT>* const left = node->getLeft();
-    DomainTreeNode<T, DT>* const lright = left->getRight();
+    DomainTreeNode<T>* const left = node->getLeft();
+    DomainTreeNode<T>* const lright = left->getRight();
     node->left_ = lright;
     if (lright != NULL) {
         lright->parent_ = node;
     }
 
-    DomainTreeNode<T, DT>* const parent = node->getParent();
+    DomainTreeNode<T>* const parent = node->getParent();
     left->parent_ = parent;
 
     if (!node->isSubTreeRoot()) {
@@ -1909,19 +1936,19 @@ DomainTree<T, DT>::rightRotate
 }
 
 
-template <typename T, typename DT>
+template <typename T>
 void
-DomainTree<T, DT>::dumpTree(std::ostream& os, unsigned int depth) const {
+DomainTree<T>::dumpTree(std::ostream& os, unsigned int depth) const {
     indent(os, depth);
     os << "tree has " << node_count_ << " node(s)\n";
     dumpTreeHelper(os, root_.get(), depth);
 }
 
-template <typename T, typename DT>
+template <typename T>
 void
-DomainTree<T, DT>::dumpTreeHelper(std::ostream& os,
-                                 const DomainTreeNode<T, DT>* node,
-                                 unsigned int depth) const
+DomainTree<T>::dumpTreeHelper(std::ostream& os,
+                              const DomainTreeNode<T>* node,
+                              unsigned int depth) const
 {
     if (node == NULL) {
         indent(os, depth);
@@ -1931,7 +1958,7 @@ DomainTree<T, DT>::dumpTreeHelper(std::ostream& os,
 
     indent(os, depth);
     os << node->getLabels() << " ("
-       << ((node->getColor() == DomainTreeNode<T, DT>::BLACK) ? "black" : "red")
+       << ((node->getColor() == DomainTreeNode<T>::BLACK) ? "black" : "red")
        << ")";
     if (node->isEmpty()) {
         os << " [invisible]";
@@ -1941,7 +1968,7 @@ DomainTree<T, DT>::dumpTreeHelper(std::ostream& os,
     }
     os << "\n";
 
-    const DomainTreeNode<T, DT>* down = node->getDown();
+    const DomainTreeNode<T>* down = node->getDown();
     if (down != NULL) {
         indent(os, depth + 1);
         os << "begin down from " << node->getLabels() << "\n";
@@ -1953,16 +1980,16 @@ DomainTree<T, DT>::dumpTreeHelper(std::ostream& os,
     dumpTreeHelper(os, node->getRight(), depth + 1);
 }
 
-template <typename T, typename DT>
+template <typename T>
 void
-DomainTree<T, DT>::indent(std::ostream& os, unsigned int depth) {
+DomainTree<T>::indent(std::ostream& os, unsigned int depth) {
     static const unsigned int INDENT_FOR_EACH_DEPTH = 5;
     os << std::string(depth * INDENT_FOR_EACH_DEPTH, ' ');
 }
 
-template <typename T, typename DT>
+template <typename T>
 void
-DomainTree<T, DT>::dumpDot(std::ostream& os, bool show_pointers) const {
+DomainTree<T>::dumpDot(std::ostream& os, bool show_pointers) const {
     int nodecount = 0;
 
     os << "digraph g {\n";
@@ -1971,11 +1998,11 @@ DomainTree<T, DT>::dumpDot(std::ostream& os, bool show_pointers) const {
     os << "}\n";
 }
 
-template <typename T, typename DT>
+template <typename T>
 int
-DomainTree<T, DT>::dumpDotHelper(std::ostream& os,
-                                const DomainTreeNode<T, DT>* node,
-                                int* nodecount, bool show_pointers) const
+DomainTree<T>::dumpDotHelper(std::ostream& os,
+                             const DomainTreeNode<T>* node,
+                             int* nodecount, bool show_pointers) const
 {
     if (node == NULL) {
         return 0;
@@ -1995,7 +2022,7 @@ DomainTree<T, DT>::dumpDotHelper(std::ostream& os,
     }
     os << "\"] [";
 
-    if (node->getColor() == DomainTreeNode<T, DT>::RED) {
+    if (node->getColor() == DomainTreeNode<T>::RED) {
         os << "color=red";
     } else {
         os << "color=black";
diff --git a/src/lib/datasrc/memory/rdata_encoder.cc b/src/lib/datasrc/memory/rdata_encoder.cc
deleted file mode 100644
index 1c27d85..0000000
--- a/src/lib/datasrc/memory/rdata_encoder.cc
+++ /dev/null
@@ -1,602 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <exceptions/exceptions.h>
-
-#include <util/buffer.h>
-
-#include <dns/name.h>
-#include <dns/labelsequence.h>
-#include <dns/messagerenderer.h>
-#include <dns/rdata.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-
-#include "rdata_encoder.h"
-
-#include <boost/static_assert.hpp>
-
-#include <cassert>
-#include <cstring>
-#include <vector>
-
-#include <stdint.h>
-
-using namespace isc::dns;
-using std::vector;
-
-namespace isc {
-namespace datasrc {
-namespace memory {
-
-namespace {
-/// Specification of a single RDATA field in terms of internal encoding.
-struct RdataFieldSpec {
-    enum FieldType {
-        FIXEDLEN_DATA = 0,      // fixed-length data field
-        VARLEN_DATA,            // variable-length data field
-        DOMAIN_NAME             // domain name
-    };
-
-    const FieldType type;       // field type
-
-    // The length of fixed-length data field.  Only valid for FIXEDLEN_DATA.
-    // For type DOMAIN_NAME, set it to 0.
-    const uint16_t fixeddata_len;
-
-    // Attributes of the name.  Only valid for DOMAIN_NAME.
-    // For type _DATA, set it to NAMEATTR_NONE.
-    const RdataNameAttributes name_attributes;
-};
-
-/// Specification of RDATA in terms of internal encoding.
-///
-/// The fields must be a sequence of:
-/// <0 or 1 fixed/var-len data field>,
-/// <1 or more domain name fields>,
-/// <1 fixed/var-len data field>,
-/// <1 or more domain name fields>,
-/// <1 fixed/var-len data field>,
-/// ...and so on.
-/// There must not be more than one consecutive data fields (i.e., without
-/// interleaved by a domain name); it would just be inefficient in terms of
-/// memory footprint and iterating over the fields, and it would break
-/// some assumption within the encoder implementation.  For consecutive
-/// data fields in the DNS protocol, if all fields have fixed lengths, they
-/// should be combined into a single fixed-length field (like the last 20
-/// bytes of SOA RDATA).  If there's a variable length field, they should be
-/// combined into a single variable-length field (such as DNSKEY, which has
-/// 3 fixed-length fields followed by one variable-length field).
-struct RdataEncodeSpec {
-    const uint16_t field_count; // total number of fields (# of fields member)
-    const uint16_t name_count;  // number of domain name fields
-    const uint16_t varlen_count; // number of variable-length data fields
-    const RdataFieldSpec* const fields; // list of field specs
-};
-
-// Many types of RDATA can be treated as a single-field, variable length
-// field (in terms of our encoding).  The following define such most general
-// form of field spec.
-const RdataFieldSpec generic_data_fields[] = {
-    {RdataFieldSpec::VARLEN_DATA, 0, NAMEATTR_NONE}
-};
-const uint16_t n_generic_data_fields =
-    sizeof(generic_data_fields) / sizeof(RdataFieldSpec);
-const RdataEncodeSpec generic_data_spec = {
-    n_generic_data_fields, 0, 1, generic_data_fields
-};
-
-// RDATA consist of a single IPv4 address field.
-const RdataFieldSpec single_ipv4_fields[] = {
-    {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint32_t), NAMEATTR_NONE}
-};
-const uint16_t n_ipv4_fields =
-    sizeof(single_ipv4_fields) / sizeof(RdataFieldSpec);
-
-// RDATA consist of a single IPv6 address field.
-const RdataFieldSpec single_ipv6_fields[] = {
-    {RdataFieldSpec::FIXEDLEN_DATA, 16, NAMEATTR_NONE} // 128bits = 16 bytes
-};
-const uint16_t n_ipv6_fields =
-    sizeof(single_ipv6_fields) / sizeof(RdataFieldSpec);
-
-// There are several RR types that consist of a single domain name.
-const RdataFieldSpec single_noattr_name_fields[] = {
-    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
-};
-const RdataFieldSpec single_compressible_name_fields[] = {
-    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE}
-};
-const RdataFieldSpec single_compadditional_name_fields[] = {
-    {RdataFieldSpec::DOMAIN_NAME, 0,
-     static_cast<RdataNameAttributes>(
-         static_cast<unsigned int>(NAMEATTR_COMPRESSIBLE) |
-         static_cast<unsigned int>(NAMEATTR_ADDITIONAL))}
-};
-const uint16_t n_single_name_fields =
-    sizeof(single_noattr_name_fields) / sizeof(RdataFieldSpec);
-
-// RDATA consisting of two names.  There are some of this type.
-const RdataFieldSpec double_compressible_name_fields[] = {
-    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE},
-    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE}
-};
-const RdataFieldSpec double_noattr_name_fields[] = {
-    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE},
-    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
-};
-const uint16_t n_double_name_fields =
-    sizeof(double_compressible_name_fields) / sizeof(RdataFieldSpec);
-
-// SOA specific: two compressible names + 5*32-bit data
-const RdataFieldSpec soa_fields[] = {
-    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE},
-    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE},
-    {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint32_t) * 5, NAMEATTR_NONE}
-};
-const uint16_t n_soa_fields = sizeof(soa_fields) / sizeof(RdataFieldSpec);
-
-// MX specific: 16-bit data + compressible/additional name
-const RdataFieldSpec mx_fields[] = {
-    {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint16_t), NAMEATTR_NONE},
-    {RdataFieldSpec::DOMAIN_NAME, 0,
-     static_cast<RdataNameAttributes>(
-         static_cast<unsigned int>(NAMEATTR_COMPRESSIBLE) |
-         static_cast<unsigned int>(NAMEATTR_ADDITIONAL))}
-};
-const uint16_t n_mx_fields = sizeof(mx_fields) / sizeof(RdataFieldSpec);
-
-// AFSDB specific: 16-bit data + no-attribute name
-const RdataFieldSpec afsdb_fields[] = {
-    {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint16_t), NAMEATTR_NONE},
-    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
-};
-const uint16_t n_afsdb_fields = sizeof(afsdb_fields) / sizeof(RdataFieldSpec);
-
-// SRV specific: 3*16-bit data + additional name
-const RdataFieldSpec srv_fields[] = {
-    {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint16_t) * 3, NAMEATTR_NONE},
-    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_ADDITIONAL}
-};
-const uint16_t n_srv_fields = sizeof(srv_fields) / sizeof(RdataFieldSpec);
-
-// NAPTR specific: (multi-field) variable data + (additional) name
-// NAPTR requires complicated additional section handling; for now, we skip
-// the additional handling completely.
-const RdataFieldSpec naptr_fields[] = {
-    {RdataFieldSpec::VARLEN_DATA, 0, NAMEATTR_NONE},
-    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
-};
-const uint16_t n_naptr_fields = sizeof(naptr_fields) / sizeof(RdataFieldSpec);
-
-// NSEC specific: no-attribute name + varlen data
-const RdataFieldSpec nsec_fields[] = {
-    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE},
-    {RdataFieldSpec::VARLEN_DATA, 0, NAMEATTR_NONE}
-};
-const uint16_t n_nsec_fields = sizeof(nsec_fields) / sizeof(RdataFieldSpec);
-
-// Class IN encode specs.  This gives a shortcut to the encode spec for
-// some well-known types of RDATA specific to class IN (most of which are
-// generic and can be used for other classes).  The array index is the
-// RR type code.
-const RdataEncodeSpec encode_spec_list_in[] = {
-    generic_data_spec,                         // #0: (NONE)
-    {n_ipv4_fields, 0, 0, single_ipv4_fields},   // #1: A
-    {n_single_name_fields, 1, 0, single_compadditional_name_fields}, // #2: NS
-    generic_data_spec,          // #3
-    generic_data_spec,          // #4
-    {n_single_name_fields, 1, 0, single_compressible_name_fields}, // #5: CNAME
-    {n_soa_fields, 2, 0, soa_fields}, // #6: SOA
-    generic_data_spec,                // #7
-    generic_data_spec,                // #8
-    generic_data_spec,                // #9
-    generic_data_spec,                // #10
-    generic_data_spec,                // #11
-    {n_single_name_fields, 1, 0, single_compressible_name_fields}, // #12: PTR
-    generic_data_spec,          // #13: HINFO
-    {n_double_name_fields, 2, 0, double_compressible_name_fields}, // #14:HINFO
-    {n_mx_fields, 1, 0, mx_fields}, // #15: MX
-    generic_data_spec, // #16: TXT
-    {n_double_name_fields, 2, 0, double_noattr_name_fields}, // 17: RP
-    {n_afsdb_fields, 1, 0, afsdb_fields}, // #18: AFSDB
-    // #19-#26
-    generic_data_spec, generic_data_spec, generic_data_spec, generic_data_spec,
-    generic_data_spec, generic_data_spec, generic_data_spec, generic_data_spec,
-    generic_data_spec,                // #27
-    {n_ipv6_fields, 0, 0, single_ipv6_fields},   // #28: AAAA
-    // #29-#32
-    generic_data_spec, generic_data_spec, generic_data_spec, generic_data_spec,
-    {n_srv_fields, 1, 0, srv_fields},   // #33: SRV
-    generic_data_spec,                  // #34
-    {n_naptr_fields, 1, 1, naptr_fields}, // #35: NAPTR
-    generic_data_spec,                  // #36
-    generic_data_spec,                  // #37
-    generic_data_spec,                  // #38
-    {n_single_name_fields, 1, 0, single_noattr_name_fields}, // #39 DNAME
-    generic_data_spec,                  // #40
-    generic_data_spec,                  // #41 (OPT)
-    generic_data_spec,                  // #42
-    generic_data_spec, // #43: DS (this is opaque for encoding purposes)
-    generic_data_spec, // #44: SSHFP (this is opaque for encoding purposes)
-    generic_data_spec,                  // #45
-    generic_data_spec, // #46: RRSIG (this is opaque for encoding purposes)
-    {n_nsec_fields, 1, 1, nsec_fields} // #47: NSEC
-
-    // All others can be treated as single-field variable length data, at
-    // least for currently supported RR types.
-};
-
-// # of entries in encode_spec_list_in
-const size_t encode_spec_list_in_size =
-    sizeof(encode_spec_list_in) / sizeof(encode_spec_list_in[0]);
-BOOST_STATIC_ASSERT(encode_spec_list_in_size == 48);
-
-inline
-const RdataEncodeSpec&
-getRdataEncodeSpec(RRClass rrclass, RRType rrtype) {
-    // Special case: for classes other than IN, we treat RDATA of RR types
-    // that are class-IN specific as generic opaque data.
-    if (rrclass != RRClass::IN() &&
-        (rrtype == RRType::A() || rrtype == RRType::AAAA() ||
-         rrtype == RRType::SRV())) {
-        return (generic_data_spec);
-    }
-
-    // Otherwise, if the type is in the pre-defined range, we use the defined
-    // spec; otherwise we treat it as opaque data.
-    const uint16_t typecode = rrtype.getCode();
-    if (typecode < encode_spec_list_in_size) {
-        return (encode_spec_list_in[rrtype.getCode()]);
-    }
-    return (generic_data_spec);
-}
-
-// This class is a helper for RdataEncoder to divide the content of RDATA
-// fields for encoding by "abusing" the  message rendering logic.
-// The idea is to identify domain name fields in the writeName() method,
-// while keeping track of the size and position of other types of data
-// around the names.
-//
-// 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() : last_data_pos_(0), encode_spec_(NULL),
-                           current_field_(0)
-    {}
-    virtual ~RdataFieldComposer() {}
-    virtual bool isTruncated() const { return (false); }
-    virtual size_t getLengthLimit() const { return (65535); }
-    virtual CompressMode getCompressMode() const { return (CASE_INSENSITIVE); }
-    virtual void setTruncated() {}
-    virtual void setLengthLimit(size_t) {}
-    virtual void setCompressMode(CompressMode) {}
-
-    // Called for each domain name in the RDATA, from the RDATA's toWire()
-    // implementation.
-    virtual void writeName(const Name& name, bool compress) {
-        // First, see if we have other data already stored in the renderer's
-        // buffer, and handle it appropriately.
-        updateOtherData();
-
-        // Then, we should still have a field in the spec, and it must be a
-        // domain name field.
-        if (current_field_ >= encode_spec_->field_count) {
-            isc_throw(BadValue,
-                      "RDATA encoder encounters an unexpected name data: " <<
-                      name);
-        }
-        const RdataFieldSpec& field =
-            encode_spec_->fields[current_field_++];
-        // Since we know we've passed any prior data field, the next field
-        // must be a domain name as long as it exists; otherwise it's a bug
-        // in the spec (not a bogus input).  So we assert() that condition.
-        assert(field.type == RdataFieldSpec::DOMAIN_NAME);
-
-        // It would be compressed iff the field has that attribute.
-        if (compress !=
-            ((field.name_attributes & NAMEATTR_COMPRESSIBLE) != 0)) {
-            isc_throw(BadValue, "RDATA encoder error, inconsistent name "
-                      "compression policy: " << name);
-        }
-
-        const LabelSequence labels(name);
-        labels.serialize(labels_placeholder_, sizeof(labels_placeholder_));
-        writeData(labels_placeholder_, labels.getSerializedLength());
-
-        last_data_pos_ += labels.getSerializedLength();
-    }
-    // Clear all internal states and resources for a new set of RDATA.
-    void clearLocal(const RdataEncodeSpec* encode_spec) {
-        AbstractMessageRenderer::clear();
-        encode_spec_ = encode_spec;
-        data_lengths_.clear();
-        last_data_pos_ = 0;
-    }
-    // Called at the beginning of an RDATA.
-    void startRdata() {
-        current_field_ = 0;
-    }
-    // Called at the end of an RDATA.
-    void endRdata() {
-        // Handle any remaining data (there should be no more name).  Then
-        // we should reach the end of the fields.
-        updateOtherData();
-        if (current_field_ != encode_spec_->field_count) {
-            isc_throw(BadValue,
-                      "RDATA encoder didn't find all expected fields");
-        }
-    }
-
-    // Hold the lengths of variable length fields, in the order of their
-    // appearance.  For convenience, allow the encoder to refer to it
-    // directly.
-    vector<uint16_t> data_lengths_;
-
-private:
-    // 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, checking consistency with the field spec, and
-    // if it contains variable-length field, record its length.
-    size_t last_data_pos_;
-    void updateOtherData() {
-        // If we've reached the end of the fields or we are expecting a
-        // domain name, there's nothing to do here.
-        if (current_field_ >= encode_spec_->field_count ||
-            encode_spec_->fields[current_field_].type ==
-            RdataFieldSpec::DOMAIN_NAME) {
-            return;
-        }
-
-        const size_t cur_pos = getLength();
-        const size_t data_len = cur_pos - last_data_pos_;
-
-        const RdataFieldSpec& field = encode_spec_->fields[current_field_];
-        if (field.type == RdataFieldSpec::FIXEDLEN_DATA) {
-            // The data length of a fixed length field must be the one
-            // specified in the field spec.
-            if (data_len != field.fixeddata_len) {
-                isc_throw(BadValue,
-                          "RDATA encoding: available data too short for the "
-                          "type");
-            }
-        } else {
-            // For encoding purposes, a variable-length data field is
-            // a single field covering all data, even if it may
-            // consist of multiple fields as DNS RDATA (e.g. TXT).
-            if (data_len > 0xffff) {
-                isc_throw(RdataEncodingError, "RDATA field is too large: "
-                          << data_len << " bytes");
-            }
-            data_lengths_.push_back(data_len);
-        }
-
-        ++current_field_;
-        last_data_pos_ = cur_pos;
-    }
-
-    // The RDATA field spec of the current session.  Set at the beginning of
-    // each session.
-    const RdataEncodeSpec* encode_spec_;
-    // the RDATA field (for encoding) currently handled.  Reset to 0 for
-    // each RDATA of the session.
-    size_t current_field_;
-    // Placeholder to convert a name object to a label sequence.
-    uint8_t labels_placeholder_[LabelSequence::MAX_SERIALIZED_LENGTH];
-};
-} // end of unnamed namespace
-
-struct RdataEncoder::RdataEncoderImpl {
-    RdataEncoderImpl() : encode_spec_(NULL), rrsig_buffer_(0),
-                         rdata_count_(0)
-    {}
-
-    const RdataEncodeSpec* encode_spec_; // encode spec of current RDATA set
-    RdataFieldComposer field_composer_;
-    util::OutputBuffer rrsig_buffer_;
-    size_t rdata_count_;
-    vector<uint16_t> rrsig_lengths_;
-};
-
-RdataEncoder::RdataEncoder() :
-    impl_(new RdataEncoderImpl)
-{}
-
-RdataEncoder::~RdataEncoder() {
-    delete impl_;
-}
-
-void
-RdataEncoder::start(RRClass rrclass, RRType rrtype) {
-    if (rrtype == RRType::RRSIG()) {
-        isc_throw(BadValue, "RRSIG cannot be encoded as main RDATA type");
-    }
-
-    impl_->encode_spec_ = &getRdataEncodeSpec(rrclass, rrtype);
-    impl_->field_composer_.clearLocal(impl_->encode_spec_);
-    impl_->rrsig_buffer_.clear();
-    impl_->rdata_count_ = 0;
-    impl_->rrsig_lengths_.clear();
-}
-
-void
-RdataEncoder::addRdata(const rdata::Rdata& rdata) {
-    if (impl_->encode_spec_ == NULL) {
-        isc_throw(InvalidOperation,
-                  "RdataEncoder::addRdata performed before start");
-    }
-
-    impl_->field_composer_.startRdata();
-    rdata.toWire(impl_->field_composer_);
-    impl_->field_composer_.endRdata();
-    ++impl_->rdata_count_;
-}
-
-void
-RdataEncoder::addSIGRdata(const rdata::Rdata& sig_rdata) {
-    if (impl_->encode_spec_ == NULL) {
-        isc_throw(InvalidOperation,
-                  "RdataEncoder::addSIGRdata performed before start");
-    }
-    const size_t cur_pos = impl_->rrsig_buffer_.getLength();
-    sig_rdata.toWire(impl_->rrsig_buffer_);
-    const size_t rrsig_datalen = impl_->rrsig_buffer_.getLength() - cur_pos;
-    if (rrsig_datalen > 0xffff) {
-        isc_throw(RdataEncodingError, "RRSIG is too large: "
-                  << rrsig_datalen << " bytes");
-    }
-    impl_->rrsig_lengths_.push_back(rrsig_datalen);
-}
-
-size_t
-RdataEncoder::getStorageLength() const {
-    if (impl_->encode_spec_ == NULL) {
-        isc_throw(InvalidOperation,
-                  "RdataEncoder::getStorageLength performed before start");
-    }
-
-    return (sizeof(uint16_t) * impl_->field_composer_.data_lengths_.size() +
-            sizeof(uint16_t) * impl_->rrsig_lengths_.size() +
-            impl_->rrsig_buffer_.getLength() +
-            impl_->field_composer_.getLength());
-}
-
-void
-RdataEncoder::encode(void* buf, size_t buf_len) const {
-    if (impl_->encode_spec_ == NULL) {
-        isc_throw(InvalidOperation,
-                  "RdataEncoder::encode performed before start");
-    }
-    if (buf == NULL) {
-        isc_throw(BadValue,
-                  "RdataEncoder::encode NULL buffer is given");
-    }
-    if (getStorageLength() > buf_len) {
-        isc_throw(BadValue, "RdataEncoder::encode short buffer given");
-    }
-
-    uint8_t* const dp_beg = reinterpret_cast<uint8_t*>(buf);
-    uint8_t* dp = dp_beg;
-    uint16_t* lenp = reinterpret_cast<uint16_t*>(buf);
-
-    // Encode list of lengths for variable length fields (if any)
-    if (!impl_->field_composer_.data_lengths_.empty()) {
-        const size_t varlen_fields_len =
-            impl_->field_composer_.data_lengths_.size() * sizeof(uint16_t);
-        std::memcpy(lenp, &impl_->field_composer_.data_lengths_[0],
-                    varlen_fields_len);
-        lenp += impl_->field_composer_.data_lengths_.size();
-        dp += varlen_fields_len;
-    }
-    // Encode list of lengths for RRSIGs (if any)
-    if (!impl_->rrsig_lengths_.empty()) {
-        const size_t rrsigs_len =
-            impl_->rrsig_lengths_.size() * sizeof(uint16_t);
-        std::memcpy(lenp, &impl_->rrsig_lengths_[0], rrsigs_len);
-        dp += rrsigs_len;
-    }
-    // Encode main RDATA
-    std::memcpy(dp, impl_->field_composer_.getData(),
-                impl_->field_composer_.getLength());
-    dp += impl_->field_composer_.getLength();
-    // Encode RRSIGs, if any
-    std::memcpy(dp, impl_->rrsig_buffer_.getData(),
-                impl_->rrsig_buffer_.getLength());
-    dp += impl_->rrsig_buffer_.getLength();
-
-    // The validation at the entrance must ensure this
-    assert(buf_len >= dp - dp_beg);
-}
-
-namespace testing {
-void
-foreachRdataField(RRClass rrclass, RRType rrtype,
-                  size_t rdata_count,
-                  const vector<uint8_t>& encoded_data,
-                  const vector<uint16_t>& varlen_list,
-                  NameCallback name_callback, DataCallback data_callback)
-{
-    const RdataEncodeSpec& encode_spec = getRdataEncodeSpec(rrclass, rrtype);
-
-    size_t off = 0;
-    size_t varlen_count = 0;
-    size_t name_count = 0;
-    for (size_t count = 0; count < rdata_count; ++count) {
-        for (size_t i = 0; i < encode_spec.field_count; ++i) {
-            const RdataFieldSpec& field_spec = encode_spec.fields[i];
-            switch (field_spec.type) {
-            case RdataFieldSpec::FIXEDLEN_DATA:
-                if (data_callback) {
-                    data_callback(&encoded_data.at(off),
-                                  field_spec.fixeddata_len);
-                }
-                off += field_spec.fixeddata_len;
-                break;
-            case RdataFieldSpec::VARLEN_DATA:
-            {
-                const size_t varlen = varlen_list.at(varlen_count);
-                if (data_callback && varlen > 0) {
-                    data_callback(&encoded_data.at(off), varlen);
-                }
-                off += varlen;
-                ++varlen_count;
-                break;
-            }
-            case RdataFieldSpec::DOMAIN_NAME:
-            {
-                ++name_count;
-                const LabelSequence labels(&encoded_data.at(off));
-                if (name_callback) {
-                    name_callback(labels, field_spec.name_attributes);
-                }
-                off += labels.getSerializedLength();
-                break;
-            }
-            }
-        }
-    }
-    assert(name_count == encode_spec.name_count * rdata_count);
-    assert(varlen_count == encode_spec.varlen_count * rdata_count);
-}
-
-void
-foreachRRSig(const vector<uint8_t>& encoded_data,
-             const vector<uint16_t>& rrsiglen_list,
-             DataCallback data_callback)
-{
-    size_t rrsig_totallen = 0;
-    for (vector<uint16_t>::const_iterator it = rrsiglen_list.begin();
-         it != rrsiglen_list.end();
-         ++it) {
-        rrsig_totallen += *it;
-    }
-    assert(encoded_data.size() >= rrsig_totallen);
-
-    const uint8_t* dp = &encoded_data[encoded_data.size() - rrsig_totallen];
-    for (size_t i = 0; i < rrsiglen_list.size(); ++i) {
-        data_callback(dp, rrsiglen_list[i]);
-        dp += rrsiglen_list[i];
-    }
-}
-} // namespace testing
-
-} // namespace memory
-} // namespace datasrc
-} // datasrc isc
diff --git a/src/lib/datasrc/memory/rdata_encoder.h b/src/lib/datasrc/memory/rdata_encoder.h
deleted file mode 100644
index 7524f94..0000000
--- a/src/lib/datasrc/memory/rdata_encoder.h
+++ /dev/null
@@ -1,319 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef DATASRC_MEMORY_RDATA_ENCODER_H
-#define DATASRC_MEMORY_RDATA_ENCODER_H 1
-
-#include <exceptions/exceptions.h>
-
-#include <dns/labelsequence.h>
-#include <dns/rdata.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-
-#include <boost/function.hpp>
-#include <boost/noncopyable.hpp>
-
-#include <vector>
-
-/// \file rdata_encoder.h
-/// \brief Set of utility classes for encoding RDATA in memory efficient way.
-///
-/// This file defines a set of interfaces (classes, types, constants) to
-/// manipulate a given set of RDATA of the same type (normally associated with
-/// an RRset) that may be accompanied with RRSIGs in a memory efficient way.
-///
-/// The entire set of RDATA is stored in a packed form in a contiguous
-/// memory region.  It's opaque data, without containing non trivial
-/// data structures, so it can be located anywhere in the memory or even
-/// dumped to a file.
-///
-/// Two main classes are provided: one is
-/// \c isc::datasrc::memory::RdataEncoder, which allows
-/// the application to create encoded data for a set of RDATA;
-/// the other (TBD) provides an interface to iterate over encoded set of
-/// RDATA for purposes such as data lookups or rendering the data into the
-/// wire format to create a DNS message.
-///
-/// The actual encoding detail is private information to the implementation,
-/// and the application shouldn't assume anything about that except that
-/// each RDATA is considered to consist of one or more generic fields,
-/// and each field is typed as either opaque data or a domain name.
-/// A domain name field has additional attributes
-/// (see \c isc::datasrc::memory::RdataNameAttributes)
-/// so the application can change how the name should be handled in terms
-/// of the DNS protocol (e.g., whether it's subject to name compression).
-///
-/// The following are the current implementation of internal encoding, shown
-/// only for reference.  Applications must not assume this particular form
-/// for the encoded data; in fact, it can change in a future version of the
-/// implementation.
-/// \verbatim
-// The encoded data begin with a series of 16-bit length fields (values are
-// stored in the host byte order).  The sequence may be empty.
-// uint16_t n1_1: size of 1st variable len field (if any) of 1st RDATA
-// uint16_t n1_2: size of 2nd variable len field of 1st RDATA
-// ...
-// uint16_t nN_M: size of last (Mth) variable len field of last (Nth) RDATA
-// uint16_t ns1: size of 1st RRSIG (if any) data
-// ...
-// uint16_t nsL: size of last (Lth) RRSIG data
-// A sequence of packed data fields follows:
-// uint8_t[]: data field value, length specified by nI_J (in case it's
-//            variable-length) or by the per type field spec (in case it's
-//            fixed-length).
-// or
-// opaque data, LabelSequence::getSerializedLength() bytes: data for a name
-// uint8_t[ns1]: 1st RRSIG data
-// ...
-// uint8_t[nsL]: last RRSIG data
-// \endverbatim
-///
-/// As described above, this implementation treats RRSIGs as opaque data
-/// that don't contain any domain names.  Technically, it has a "signer"
-/// domain name field in the sense of RFC4034.  In practice, however, this
-/// field is essentially mere data; it's not subject to name compression,
-/// and since it's very likely to be a subdomain of (or equal to) the
-/// owner name of the corresponding RR (or, if used in a DNS message,
-/// some domain name that already appears before this field), so it won't
-/// be a target of name compression either.  By treating the entire RRSIG
-/// as single-field data we can make the implementation simpler, and probably
-/// make it faster in rendering it into a DNS message.
-
-namespace isc {
-namespace datasrc {
-namespace memory {
-
-/// \brief General error in RDATA encoding.
-///
-/// This is thrown when \c RdataEncoder encounters a rare, unsupported
-/// situation. a method is called for a name or RRset which
-/// is not in or below the zone.
-class RdataEncodingError : public Exception {
-public:
-    RdataEncodingError(const char* file, size_t line, const char* what) :
-        Exception(file, line, what) {}
-};
-
-/// \brief Attributes of domain name fields of encoded RDATA.
-///
-/// The enum values define special traits of the name that can affect how
-/// it should be handled in rendering or query processing.
-enum RdataNameAttributes {
-    NAMEATTR_NONE = 0,          ///< No special attributes
-    NAMEATTR_COMPRESSIBLE = 1,  ///< Name should be compressed when rendered
-    NAMEATTR_ADDITIONAL = (NAMEATTR_COMPRESSIBLE << 1) ///< Name requires
-                                                      ///< Additional section
-                                                      ///< handling
-};
-
-/// \brief RDATA encoder.
-///
-/// This class provides interfaces to encode a set of RDATA of a specific
-/// RR class and type, possibly with their RRSIG RDATAs, in a memory-efficient
-/// format.  In many cases these sets of RDATA come from a specific (signed
-/// or unsigned) RRset.
-///
-/// It is expected for a single \c RdataEncoder object to be used multiple
-/// times for different sets of RDATA, such as in loading an entire zone
-/// into memory.  Each encoding session begins with the \c start() method,
-/// which sets the context for the specific RR class and type to be encoded.
-/// Any number of calls to \c addRdata() or \c addSIGRdata() follow, each
-/// of which updates the internal state of the encoder with the encoding
-/// information for the given RDATA or RRSIG RDATA, respectively.
-/// The \c addRdata() is expected to be called with an
-/// \c isc::dns::rdata::Rdata object
-/// of the specified class and type, and \c addRdata() checks the consistency
-/// for the purpose of encoding (but it's not completely type safe; for
-/// example, it wouldn't distinguish TXT RDATA and HINFO RDATA.
-/// Likewise, an \c isc::dns::rdata::Rdata given to \c addSIGRdata() is
-/// expected to be of RRSIG, but the method does not check the assumption).
-///
-/// After passing the complete set of RDATA and their RRSIG, the application
-/// is expected to call \c getStorageLength() to know the size of storage
-/// that is sufficient to store all encoded data.  Normally the application
-/// would allocate a memory region of that size, and then call \c encode()
-/// with the prepared region.  The \c encode() method dumps encoded data
-/// to the given memory region.
-///
-/// The caller can reuse the \c RdataEncoder object for another set of RDATA
-/// by repeating the session from \c start().
-class RdataEncoder : boost::noncopyable {
-public:
-    /// \brief Default constructor.
-    RdataEncoder();
-
-    /// \brief The destrcutor.
-    ~RdataEncoder();
-
-    /// \brief Start the encoding session.
-    ///
-    /// It re-initializes the internal encoder state for a new encoding
-    /// session.  The \c rrclass and \c rrtype parameters specify the
-    /// type of RDATA to be encoded in the new session.  Note that if the
-    /// set of RDATA is signed, \c rrtype always specifies the "signed" type;
-    /// it must not be RRSIG.
-    ///
-    /// \throw BadValue RRSIG is specified for rrtype.
-    ///
-    /// \param rrclass The RR class of RDATA to be encoded in the session.
-    /// \param rrtype The RR type of RDATA to be encoded in the session.
-    void start(dns::RRClass rrclass, dns::RRType rrtype);
-
-    /// \brief Add an RDATA for encoding.
-    ///
-    /// This method updates internal state of the \c RdataEncoder() with the
-    /// given RDATA so it will be part of the encoded data in a subsequent
-    /// call to \c encode().
-    ///
-    /// The given \c rdata must be of the RR class and type specified at
-    /// the prior call to \c start().  This method checks the assumption
-    /// to some extent, but the check is not complete; this is generally
-    /// the responsibility of the caller.
-    ///
-    /// The caller can destroy \c rdata after this call is completed.
-    ///
-    /// \note This implementation does not support RDATA (or any subfield of
-    /// it) whose size exceeds 65535 bytes (max uint16_t value).  Such RDATA
-    /// may not necessarily be considered invalid in terms of protocol
-    /// specification, but in practice it's mostly useless because the
-    /// corresponding RR won't fit in any valid DNS message.
-    ///
-    /// As long as the \c rdata is of the correct type and its size is normal,
-    /// this method should normally be exception free.  If it throws, however,
-    /// it doesn't always provide the strong exception guarantee.  In general,
-    /// the caller needs to either destroy the encoder object or restart a
-    /// new session from \c start() should this method throws an exception.
-    ///
-    /// \throw InvalidOperation called before start().
-    /// \throw BadValue inconsistent data found.
-    /// \throw RdataEncodingError A very unusual case, such as over 64KB RDATA.
-    /// \throw std::bad_alloc Internal memory allocation failure.
-    ///
-    /// \param rdata An RDATA to be encoded in the session.
-    void addRdata(const dns::rdata::Rdata& rdata);
-
-    /// \brief Add an RRSIG RDATA for encoding.
-    ///
-    /// This method updates internal state of the \c RdataEncoder() with the
-    /// given RDATA, which is assumed to be of type RRSIG that covers the
-    /// type specified at the time of \c start() for the encoding session.
-    /// The corresponding data for the RRSIG RDATA will be encoded in a
-    /// subsequent call to \c encode().
-    ///
-    /// The passed \c sig_rdata is expected to be of type RRSIG and cover
-    /// the RR type specified at the call to \c start() to this encoding
-    /// session.  But this method does not check if it is the case at all;
-    /// it could even accept any type of RDATA as opaque data.  It's caller's
-    /// responsibility to ensure the assumption.
-    ///
-    /// The caller can destroy \c rdata after this call is completed.
-    ///
-    /// \note Like addRdata(), this implementation does not support
-    /// RRSIG RDATA whose size (in the form of wire format) exceeds 65535
-    /// bytes.
-    ///
-    /// The same note about exception safety as \c addRdata() applies.
-    ///
-    /// \throw InvalidOperation called before start().
-    /// \throw RdataEncodingError A very unusual case, such as over 64KB RDATA.
-    /// \throw std::bad_alloc Internal memory allocation failure.
-    ///
-    /// \param sig_rdata An RDATA to be encoded in the session.  Supposed to
-    /// be of type RRSIG.
-    void addSIGRdata(const dns::rdata::Rdata& sig_rdata);
-
-    /// \brief Return the length of space for encoding for the session.
-    ///
-    /// It returns the size of the encoded data that would be generated for
-    /// the set of RDATA (and RRSIGs) in the encoder at the call of this
-    /// method.  It's ensured that a buffer of that size can be safely passed
-    /// to \c encode() unless there's no other "add" method is called by then.
-    ///
-    /// As long as this method is called after start(), it never throws.
-    ///
-    /// \throw InvalidOperation called before start().
-    ///
-    /// \return The expected size of the encoded data at the time of the call.
-    size_t getStorageLength() const;
-
-    /// \brief Encode RDATAs of the session to a buffer.
-    ///
-    /// This method dumps encoded data for the stored set of RDATA and
-    /// their RRSIGs to a given buffer.  The buffer must have a size
-    /// at least as large as the return value of a prior call to
-    /// \c getStorageLength() (it may be larger than that).
-    ///
-    /// The given buffer must be aligned at the natural boundary for
-    /// 16-bit integers.  The method doesn't check this condition; it's
-    /// caller's responsibility to ensure that.  Note: the alignment
-    /// requirement may change in a future version of this implementation.
-    ///
-    /// As long as this method is called after start() and the buffer is
-    /// valid with a sufficient size, this method never throws.
-    ///
-    /// \throw InvalidOperation called before start().
-    /// \throw BadValue buffer is NULL or it's too short for the encoded data.
-    ///
-    /// \param buf A pointer to the buffer to which encoded data are to be
-    /// dumped.
-    /// \param buf_len The size of the buffer in bytes.
-    void encode(void* buf, size_t buf_len) const;
-
-private:
-    struct RdataEncoderImpl;
-    RdataEncoderImpl* impl_;
-};
-
-// We use the following quick-hack version of "foreach"
-// operators until we implement the complete versions.  The plan is to
-// update the test cases that use these functions with the complete
-// functions/classes, and then remove the entire namespace.
-namespace testing {
-// Callbacks used in foreachRdataField.
-typedef boost::function<void(const dns::LabelSequence&,
-                             RdataNameAttributes)> NameCallback;
-typedef boost::function<void(const uint8_t*, size_t)> DataCallback;
-
-// Iterate over each field (in terms of the internal encoding) of each
-// RDATA stored in encoded_data, and call the given callback for each
-// data (for domain name fields, name_callback will be called; for
-// normal data fields data_callback will be called).  rdata_count is
-// the number of RDATAs.  If the encoded data contain variable-length
-// data fields, varlen_list should store a sequence of their lengths,
-// in the order of the appearance.
-void foreachRdataField(dns::RRClass rrclass, dns::RRType rrtype,
-                       size_t rdata_count,
-                       const std::vector<uint8_t>& encoded_data,
-                       const std::vector<uint16_t>& varlen_list,
-                       NameCallback name_callback, DataCallback data_callback);
-
-// Iterate over each RRSIG stored in encoded_data, and call the given
-// callback for each.  rrsiglen_list should store a sequence of their lengths,
-// in the order of the appearance.  Its size is the number of RRSIGs.
-// The list can be empty, in which case this function does nothing.
-void foreachRRSig(const std::vector<uint8_t>& encoded_data,
-                  const std::vector<uint16_t>& rrsiglen_list,
-                  DataCallback data_callback);
-}
-
-} // namespace memory
-} // namespace datasrc
-} // namespace isc
-
-#endif // DATASRC_MEMORY_RDATA_ENCODER_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/datasrc/memory/rdata_serialization.cc b/src/lib/datasrc/memory/rdata_serialization.cc
new file mode 100644
index 0000000..841bc13
--- /dev/null
+++ b/src/lib/datasrc/memory/rdata_serialization.cc
@@ -0,0 +1,631 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "rdata_serialization.h"
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+
+#include <dns/name.h>
+#include <dns/labelsequence.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <cassert>
+#include <cstring>
+#include <vector>
+#include <boost/static_assert.hpp>
+
+using namespace isc::dns;
+using std::vector;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+#include "rdata_serialization_priv.cc"
+
+namespace {
+
+// Many types of RDATA can be treated as a single-field, variable length
+// field (in terms of our encoding).  The following define such most general
+// form of field spec.
+const RdataFieldSpec generic_data_fields[] = {
+    {RdataFieldSpec::VARLEN_DATA, 0, NAMEATTR_NONE}
+};
+const uint16_t n_generic_data_fields =
+    sizeof(generic_data_fields) / sizeof(RdataFieldSpec);
+const RdataEncodeSpec generic_data_spec = {
+    n_generic_data_fields, 0, 1, generic_data_fields
+};
+
+// RDATA consist of a single IPv4 address field.
+const RdataFieldSpec single_ipv4_fields[] = {
+    {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint32_t), NAMEATTR_NONE}
+};
+const uint16_t n_ipv4_fields =
+    sizeof(single_ipv4_fields) / sizeof(RdataFieldSpec);
+
+// RDATA consist of a single IPv6 address field.
+const RdataFieldSpec single_ipv6_fields[] = {
+    {RdataFieldSpec::FIXEDLEN_DATA, 16, NAMEATTR_NONE} // 128bits = 16 bytes
+};
+const uint16_t n_ipv6_fields =
+    sizeof(single_ipv6_fields) / sizeof(RdataFieldSpec);
+
+// There are several RR types that consist of a single domain name.
+const RdataFieldSpec single_noattr_name_fields[] = {
+    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
+};
+const RdataFieldSpec single_compressible_name_fields[] = {
+    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE}
+};
+const RdataFieldSpec single_compadditional_name_fields[] = {
+    {RdataFieldSpec::DOMAIN_NAME, 0,
+     static_cast<RdataNameAttributes>(
+         static_cast<unsigned int>(NAMEATTR_COMPRESSIBLE) |
+         static_cast<unsigned int>(NAMEATTR_ADDITIONAL))}
+};
+const uint16_t n_single_name_fields =
+    sizeof(single_noattr_name_fields) / sizeof(RdataFieldSpec);
+
+// RDATA consisting of two names.  There are some of this type.
+const RdataFieldSpec double_compressible_name_fields[] = {
+    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE},
+    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE}
+};
+const RdataFieldSpec double_noattr_name_fields[] = {
+    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE},
+    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
+};
+const uint16_t n_double_name_fields =
+    sizeof(double_compressible_name_fields) / sizeof(RdataFieldSpec);
+
+// SOA specific: two compressible names + 5*32-bit data
+const RdataFieldSpec soa_fields[] = {
+    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE},
+    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE},
+    {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint32_t) * 5, NAMEATTR_NONE}
+};
+const uint16_t n_soa_fields = sizeof(soa_fields) / sizeof(RdataFieldSpec);
+
+// MX specific: 16-bit data + compressible/additional name
+const RdataFieldSpec mx_fields[] = {
+    {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint16_t), NAMEATTR_NONE},
+    {RdataFieldSpec::DOMAIN_NAME, 0,
+     static_cast<RdataNameAttributes>(
+         static_cast<unsigned int>(NAMEATTR_COMPRESSIBLE) |
+         static_cast<unsigned int>(NAMEATTR_ADDITIONAL))}
+};
+const uint16_t n_mx_fields = sizeof(mx_fields) / sizeof(RdataFieldSpec);
+
+// AFSDB specific: 16-bit data + no-attribute name
+const RdataFieldSpec afsdb_fields[] = {
+    {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint16_t), NAMEATTR_NONE},
+    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
+};
+const uint16_t n_afsdb_fields = sizeof(afsdb_fields) / sizeof(RdataFieldSpec);
+
+// SRV specific: 3*16-bit data + additional name
+const RdataFieldSpec srv_fields[] = {
+    {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint16_t) * 3, NAMEATTR_NONE},
+    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_ADDITIONAL}
+};
+const uint16_t n_srv_fields = sizeof(srv_fields) / sizeof(RdataFieldSpec);
+
+// NAPTR specific: (multi-field) variable data + (additional) name
+// NAPTR requires complicated additional section handling; for now, we skip
+// the additional handling completely.
+const RdataFieldSpec naptr_fields[] = {
+    {RdataFieldSpec::VARLEN_DATA, 0, NAMEATTR_NONE},
+    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
+};
+const uint16_t n_naptr_fields = sizeof(naptr_fields) / sizeof(RdataFieldSpec);
+
+// NSEC specific: no-attribute name + varlen data
+const RdataFieldSpec nsec_fields[] = {
+    {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE},
+    {RdataFieldSpec::VARLEN_DATA, 0, NAMEATTR_NONE}
+};
+const uint16_t n_nsec_fields = sizeof(nsec_fields) / sizeof(RdataFieldSpec);
+
+// Class IN encode specs.  This gives a shortcut to the encode spec for
+// some well-known types of RDATA specific to class IN (most of which are
+// generic and can be used for other classes).  The array index is the
+// RR type code.
+const RdataEncodeSpec encode_spec_list_in[] = {
+    generic_data_spec,                         // #0: (NONE)
+    {n_ipv4_fields, 0, 0, single_ipv4_fields},   // #1: A
+    {n_single_name_fields, 1, 0, single_compadditional_name_fields}, // #2: NS
+    generic_data_spec,          // #3
+    generic_data_spec,          // #4
+    {n_single_name_fields, 1, 0, single_compressible_name_fields}, // #5: CNAME
+    {n_soa_fields, 2, 0, soa_fields}, // #6: SOA
+    generic_data_spec,                // #7
+    generic_data_spec,                // #8
+    generic_data_spec,                // #9
+    generic_data_spec,                // #10
+    generic_data_spec,                // #11
+    {n_single_name_fields, 1, 0, single_compressible_name_fields}, // #12: PTR
+    generic_data_spec,          // #13: HINFO
+    {n_double_name_fields, 2, 0, double_compressible_name_fields}, // #14:HINFO
+    {n_mx_fields, 1, 0, mx_fields}, // #15: MX
+    generic_data_spec, // #16: TXT
+    {n_double_name_fields, 2, 0, double_noattr_name_fields}, // 17: RP
+    {n_afsdb_fields, 1, 0, afsdb_fields}, // #18: AFSDB
+    // #19-#26
+    generic_data_spec, generic_data_spec, generic_data_spec, generic_data_spec,
+    generic_data_spec, generic_data_spec, generic_data_spec, generic_data_spec,
+    generic_data_spec,                // #27
+    {n_ipv6_fields, 0, 0, single_ipv6_fields},   // #28: AAAA
+    // #29-#32
+    generic_data_spec, generic_data_spec, generic_data_spec, generic_data_spec,
+    {n_srv_fields, 1, 0, srv_fields},   // #33: SRV
+    generic_data_spec,                  // #34
+    {n_naptr_fields, 1, 1, naptr_fields}, // #35: NAPTR
+    generic_data_spec,                  // #36
+    generic_data_spec,                  // #37
+    generic_data_spec,                  // #38
+    {n_single_name_fields, 1, 0, single_noattr_name_fields}, // #39 DNAME
+    generic_data_spec,                  // #40
+    generic_data_spec,                  // #41 (OPT)
+    generic_data_spec,                  // #42
+    generic_data_spec, // #43: DS (this is opaque for encoding purposes)
+    generic_data_spec, // #44: SSHFP (this is opaque for encoding purposes)
+    generic_data_spec,                  // #45
+    generic_data_spec, // #46: RRSIG (this is opaque for encoding purposes)
+    {n_nsec_fields, 1, 1, nsec_fields} // #47: NSEC
+
+    // All others can be treated as single-field variable length data, at
+    // least for currently supported RR types.
+};
+
+// # of entries in encode_spec_list_in
+const size_t encode_spec_list_in_size =
+    sizeof(encode_spec_list_in) / sizeof(encode_spec_list_in[0]);
+BOOST_STATIC_ASSERT(encode_spec_list_in_size == 48);
+
+}
+
+/// \brief Get the spec for given class and type
+const RdataEncodeSpec&
+getRdataEncodeSpec(const RRClass& rrclass, const RRType& rrtype) {
+    // Special case: for classes other than IN, we treat RDATA of RR types
+    // that are class-IN specific as generic opaque data.
+    if (rrclass != RRClass::IN() &&
+        (rrtype == RRType::A() || rrtype == RRType::AAAA() ||
+         rrtype == RRType::SRV())) {
+        return (generic_data_spec);
+    }
+
+    // Otherwise, if the type is in the pre-defined range, we use the defined
+    // spec; otherwise we treat it as opaque data.
+    const uint16_t typecode = rrtype.getCode();
+    if (typecode < encode_spec_list_in_size) {
+        return (encode_spec_list_in[rrtype.getCode()]);
+    }
+    return (generic_data_spec);
+}
+
+namespace {
+
+// This class is a helper for RdataEncoder to divide the content of RDATA
+// fields for encoding by "abusing" the  message rendering logic.
+// The idea is to identify domain name fields in the writeName() method,
+// while keeping track of the size and position of other types of data
+// around the names.
+//
+// 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() : last_data_pos_(0), encode_spec_(NULL),
+                           current_field_(0)
+    {}
+    virtual ~RdataFieldComposer() {}
+    virtual bool isTruncated() const { return (false); }
+    virtual size_t getLengthLimit() const { return (65535); }
+    virtual CompressMode getCompressMode() const { return (CASE_INSENSITIVE); }
+    virtual void setTruncated() {}
+    virtual void setLengthLimit(size_t) {}
+    virtual void setCompressMode(CompressMode) {}
+
+    // Called for each domain name in the RDATA, from the RDATA's toWire()
+    // implementation.
+    virtual void writeName(const Name& name, bool compress) {
+        // First, see if we have other data already stored in the renderer's
+        // buffer, and handle it appropriately.
+        updateOtherData();
+
+        // Then, we should still have a field in the spec, and it must be a
+        // domain name field.
+        if (current_field_ >= encode_spec_->field_count) {
+            isc_throw(BadValue,
+                      "RDATA encoder encounters an unexpected name data: " <<
+                      name);
+        }
+        const RdataFieldSpec& field =
+            encode_spec_->fields[current_field_++];
+        // Since we know we've passed any prior data field, the next field
+        // must be a domain name as long as it exists; otherwise it's a bug
+        // in the spec (not a bogus input).  So we assert() that condition.
+        assert(field.type == RdataFieldSpec::DOMAIN_NAME);
+
+        // It would be compressed iff the field has that attribute.
+        if (compress !=
+            ((field.name_attributes & NAMEATTR_COMPRESSIBLE) != 0)) {
+            isc_throw(BadValue, "RDATA encoder error, inconsistent name "
+                      "compression policy: " << name);
+        }
+
+        const LabelSequence labels(name);
+        labels.serialize(labels_placeholder_, sizeof(labels_placeholder_));
+        writeData(labels_placeholder_, labels.getSerializedLength());
+
+        last_data_pos_ += labels.getSerializedLength();
+    }
+    // Clear all internal states and resources for a new set of RDATA.
+    void clearLocal(const RdataEncodeSpec* encode_spec) {
+        AbstractMessageRenderer::clear();
+        encode_spec_ = encode_spec;
+        data_lengths_.clear();
+        last_data_pos_ = 0;
+    }
+    // Called at the beginning of an RDATA.
+    void startRdata() {
+        current_field_ = 0;
+    }
+    // Called at the end of an RDATA.
+    void endRdata() {
+        // Handle any remaining data (there should be no more name).  Then
+        // we should reach the end of the fields.
+        updateOtherData();
+        if (current_field_ != encode_spec_->field_count) {
+            isc_throw(BadValue,
+                      "RDATA encoder didn't find all expected fields");
+        }
+    }
+
+    // Hold the lengths of variable length fields, in the order of their
+    // appearance.  For convenience, allow the encoder to refer to it
+    // directly.
+    vector<uint16_t> data_lengths_;
+
+private:
+    // 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, checking consistency with the field spec, and
+    // if it contains variable-length field, record its length.
+    size_t last_data_pos_;
+    void updateOtherData() {
+        // If we've reached the end of the fields or we are expecting a
+        // domain name, there's nothing to do here.
+        if (current_field_ >= encode_spec_->field_count ||
+            encode_spec_->fields[current_field_].type ==
+            RdataFieldSpec::DOMAIN_NAME) {
+            return;
+        }
+
+        const size_t cur_pos = getLength();
+        const size_t data_len = cur_pos - last_data_pos_;
+
+        const RdataFieldSpec& field = encode_spec_->fields[current_field_];
+        if (field.type == RdataFieldSpec::FIXEDLEN_DATA) {
+            // The data length of a fixed length field must be the one
+            // specified in the field spec.
+            if (data_len != field.fixeddata_len) {
+                isc_throw(BadValue,
+                          "RDATA encoding: available data too short for the "
+                          "type");
+            }
+        } else {
+            // For encoding purposes, a variable-length data field is
+            // a single field covering all data, even if it may
+            // consist of multiple fields as DNS RDATA (e.g. TXT).
+            if (data_len > 0xffff) {
+                isc_throw(RdataEncodingError, "RDATA field is too large: "
+                          << data_len << " bytes");
+            }
+            data_lengths_.push_back(data_len);
+        }
+
+        ++current_field_;
+        last_data_pos_ = cur_pos;
+    }
+
+    // The RDATA field spec of the current session.  Set at the beginning of
+    // each session.
+    const RdataEncodeSpec* encode_spec_;
+    // the RDATA field (for encoding) currently handled.  Reset to 0 for
+    // each RDATA of the session.
+    size_t current_field_;
+    // Placeholder to convert a name object to a label sequence.
+    uint8_t labels_placeholder_[LabelSequence::MAX_SERIALIZED_LENGTH];
+};
+
+} // end of unnamed namespace
+
+struct RdataEncoder::RdataEncoderImpl {
+    RdataEncoderImpl() : encode_spec_(NULL), rrsig_buffer_(0),
+                         rdata_count_(0)
+    {}
+
+    const RdataEncodeSpec* encode_spec_; // encode spec of current RDATA set
+    RdataFieldComposer field_composer_;
+    util::OutputBuffer rrsig_buffer_;
+    size_t rdata_count_;
+    vector<uint16_t> rrsig_lengths_;
+};
+
+RdataEncoder::RdataEncoder() :
+    impl_(new RdataEncoderImpl)
+{}
+
+RdataEncoder::~RdataEncoder() {
+    delete impl_;
+}
+
+void
+RdataEncoder::start(RRClass rrclass, RRType rrtype) {
+    if (rrtype == RRType::RRSIG()) {
+        isc_throw(BadValue, "RRSIG cannot be encoded as main RDATA type");
+    }
+
+    impl_->encode_spec_ = &getRdataEncodeSpec(rrclass, rrtype);
+    impl_->field_composer_.clearLocal(impl_->encode_spec_);
+    impl_->rrsig_buffer_.clear();
+    impl_->rdata_count_ = 0;
+    impl_->rrsig_lengths_.clear();
+}
+
+void
+RdataEncoder::addRdata(const rdata::Rdata& rdata) {
+    if (impl_->encode_spec_ == NULL) {
+        isc_throw(InvalidOperation,
+                  "RdataEncoder::addRdata performed before start");
+    }
+
+    impl_->field_composer_.startRdata();
+    rdata.toWire(impl_->field_composer_);
+    impl_->field_composer_.endRdata();
+    ++impl_->rdata_count_;
+}
+
+void
+RdataEncoder::addSIGRdata(const rdata::Rdata& sig_rdata) {
+    if (impl_->encode_spec_ == NULL) {
+        isc_throw(InvalidOperation,
+                  "RdataEncoder::addSIGRdata performed before start");
+    }
+    const size_t cur_pos = impl_->rrsig_buffer_.getLength();
+    sig_rdata.toWire(impl_->rrsig_buffer_);
+    const size_t rrsig_datalen = impl_->rrsig_buffer_.getLength() - cur_pos;
+    if (rrsig_datalen > 0xffff) {
+        isc_throw(RdataEncodingError, "RRSIG is too large: "
+                  << rrsig_datalen << " bytes");
+    }
+    impl_->rrsig_lengths_.push_back(rrsig_datalen);
+}
+
+size_t
+RdataEncoder::getStorageLength() const {
+    if (impl_->encode_spec_ == NULL) {
+        isc_throw(InvalidOperation,
+                  "RdataEncoder::getStorageLength performed before start");
+    }
+
+    return (sizeof(uint16_t) * impl_->field_composer_.data_lengths_.size() +
+            sizeof(uint16_t) * impl_->rrsig_lengths_.size() +
+            impl_->rrsig_buffer_.getLength() +
+            impl_->field_composer_.getLength());
+}
+
+void
+RdataEncoder::encode(void* buf, size_t buf_len) const {
+    if (impl_->encode_spec_ == NULL) {
+        isc_throw(InvalidOperation,
+                  "RdataEncoder::encode performed before start");
+    }
+    if (buf == NULL) {
+        isc_throw(BadValue,
+                  "RdataEncoder::encode NULL buffer is given");
+    }
+    if (getStorageLength() > buf_len) {
+        isc_throw(BadValue, "RdataEncoder::encode short buffer given");
+    }
+
+    uint8_t* const dp_beg = reinterpret_cast<uint8_t*>(buf);
+    uint8_t* dp = dp_beg;
+    uint16_t* lenp = reinterpret_cast<uint16_t*>(buf);
+
+    // Encode list of lengths for variable length fields (if any)
+    if (!impl_->field_composer_.data_lengths_.empty()) {
+        const size_t varlen_fields_len =
+            impl_->field_composer_.data_lengths_.size() * sizeof(uint16_t);
+        std::memcpy(lenp, &impl_->field_composer_.data_lengths_[0],
+                    varlen_fields_len);
+        lenp += impl_->field_composer_.data_lengths_.size();
+        dp += varlen_fields_len;
+    }
+    // Encode list of lengths for RRSIGs (if any)
+    if (!impl_->rrsig_lengths_.empty()) {
+        const size_t rrsigs_len =
+            impl_->rrsig_lengths_.size() * sizeof(uint16_t);
+        std::memcpy(lenp, &impl_->rrsig_lengths_[0], rrsigs_len);
+        dp += rrsigs_len;
+    }
+    // Encode main RDATA
+    std::memcpy(dp, impl_->field_composer_.getData(),
+                impl_->field_composer_.getLength());
+    dp += impl_->field_composer_.getLength();
+    // Encode RRSIGs, if any
+    std::memcpy(dp, impl_->rrsig_buffer_.getData(),
+                impl_->rrsig_buffer_.getLength());
+    dp += impl_->rrsig_buffer_.getLength();
+
+    // The validation at the entrance must ensure this
+    assert(buf_len >= dp - dp_beg);
+}
+
+RdataReader::RdataReader(const RRClass& rrclass, const RRType& rrtype,
+                         const void* data,
+                         size_t rdata_count, size_t sig_count,
+                         const NameAction& name_action,
+                         const DataAction& data_action) :
+    name_action_(name_action),
+    data_action_(data_action),
+    spec_(getRdataEncodeSpec(rrclass, rrtype)),
+    var_count_total_(spec_.varlen_count * rdata_count),
+    sig_count_(sig_count),
+    spec_count_(spec_.field_count * rdata_count),
+    // The lenghts are stored first
+    lengths_(reinterpret_cast<const uint16_t*>(data)),
+    // And the data just after all the lengths
+    data_(reinterpret_cast<const uint8_t*>(data) +
+          (var_count_total_ + sig_count_) * sizeof(uint16_t)),
+    sigs_(NULL)
+{
+    rewind();
+}
+
+void
+RdataReader::rewind() {
+    data_pos_ = 0;
+    spec_pos_ = 0;
+    length_pos_ = 0;
+    sig_data_pos_ = 0;
+    sig_pos_ = 0;
+}
+
+RdataReader::Boundary
+RdataReader::nextInternal(const NameAction& name_action,
+                          const DataAction& data_action)
+{
+    if (spec_pos_ < spec_count_) {
+        const RdataFieldSpec& spec(spec_.fields[(spec_pos_++) %
+                                                spec_.field_count]);
+        if (spec.type == RdataFieldSpec::DOMAIN_NAME) {
+            const LabelSequence sequence(data_ + data_pos_);
+            data_pos_ += sequence.getSerializedLength();
+            name_action(sequence, spec.name_attributes);
+        } else {
+            const size_t length(spec.type == RdataFieldSpec::FIXEDLEN_DATA ?
+                                spec.fixeddata_len : lengths_[length_pos_++]);
+            const uint8_t* const pos = data_ + data_pos_;
+            data_pos_ += length;
+            data_action(pos, length);
+        }
+        return (spec_pos_ % spec_.field_count == 0 ?
+                RDATA_BOUNDARY : NO_BOUNDARY);
+    } else {
+        sigs_ = data_ + data_pos_;
+        return (RRSET_BOUNDARY);
+    }
+}
+
+RdataReader::Boundary
+RdataReader::next() {
+    return (nextInternal(name_action_, data_action_));
+}
+
+void
+RdataReader::emptyNameAction(const LabelSequence&, RdataNameAttributes) {
+    // Do nothing here.
+}
+
+void
+RdataReader::emptyDataAction(const void*, size_t) {
+    // Do nothing here.
+}
+
+RdataReader::Boundary
+RdataReader::nextSig() {
+    if (sig_pos_ < sig_count_) {
+        if (sigs_ == NULL) {
+            // We didn't find where the signatures start yet. We do it
+            // by iterating the whole data and then returning the state
+            // back.
+            const size_t data_pos = data_pos_;
+            const size_t spec_pos = spec_pos_;
+            const size_t length_pos = length_pos_;
+            // When the next() gets to the last item, it sets the sigs_
+            while (nextInternal(emptyNameAction, emptyDataAction) !=
+                   RRSET_BOUNDARY) {}
+            assert(sigs_ != NULL);
+            // Return the state
+            data_pos_ = data_pos;
+            spec_pos_ = spec_pos;
+            length_pos_ = length_pos;
+        }
+        // Extract the result
+        const size_t length = lengths_[var_count_total_ + sig_pos_];
+        const uint8_t* const pos = sigs_ + sig_data_pos_;
+        // Move the position of iterator.
+        sig_data_pos_ += lengths_[var_count_total_ + sig_pos_];
+        ++sig_pos_;
+        // Call the callback
+        data_action_(pos, length);
+        return (RDATA_BOUNDARY);
+    } else {
+        return (RRSET_BOUNDARY);
+    }
+}
+
+size_t
+RdataReader::getSize() const {
+    size_t storage_size = 0;    // this will be the end result
+    size_t data_pos = 0;
+    size_t length_pos = 0;
+
+    // Go over all data fields, adding their lengths to storage_size
+    for (size_t spec_pos = 0; spec_pos < spec_count_; ++spec_pos) {
+        const RdataFieldSpec& spec =
+            spec_.fields[spec_pos % spec_.field_count];
+        if (spec.type == RdataFieldSpec::DOMAIN_NAME) {
+            const size_t seq_len =
+                LabelSequence(data_ + data_pos).getSerializedLength();
+            data_pos += seq_len;
+            storage_size += seq_len;
+        } else {
+            const size_t data_len =
+                (spec.type == RdataFieldSpec::FIXEDLEN_DATA ?
+                 spec.fixeddata_len : lengths_[length_pos++]);
+            data_pos += data_len;
+            storage_size += data_len;
+        }
+    }
+    // Same for all RRSIG data
+    for (size_t sig_pos = 0; sig_pos < sig_count_; ++sig_pos) {
+        const size_t sig_data_len = lengths_[length_pos++];
+        storage_size += sig_data_len;
+    }
+
+    // Finally, add the size for 16-bit length fields
+    storage_size += (var_count_total_ * sizeof(uint16_t) +
+                     sig_count_ * sizeof(uint16_t));
+
+    return (storage_size);
+}
+
+} // namespace memory
+} // namespace datasrc
+} // datasrc isc
diff --git a/src/lib/datasrc/memory/rdata_serialization.h b/src/lib/datasrc/memory/rdata_serialization.h
new file mode 100644
index 0000000..183276f
--- /dev/null
+++ b/src/lib/datasrc/memory/rdata_serialization.h
@@ -0,0 +1,526 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_MEMORY_RDATA_ENCODER_H
+#define DATASRC_MEMORY_RDATA_ENCODER_H 1
+
+#include <exceptions/exceptions.h>
+
+#include <dns/labelsequence.h>
+#include <dns/rdata.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <boost/function.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <vector>
+
+/// \file rdata_serialization.h
+///
+/// This file defines a set of interfaces (classes, types, constants) to
+/// manipulate a given set of RDATA of the same type (normally associated with
+/// an RRset) that may be accompanied with RRSIGs in a memory efficient way.
+///
+/// The entire set of RDATA is stored in a packed form in a contiguous
+/// memory region.  It's opaque data, without containing non trivial
+/// data structures, so it can be located anywhere in the memory or even
+/// dumped to a file.
+///
+/// Two main classes are provided: one is
+/// \c isc::datasrc::memory::RdataEncoder, which allows
+/// the application to create encoded data for a set of RDATA;
+/// the isc::datasrc::memory::RdataReader provides an interface to iterate
+/// over encoded set of RDATA for purposes such as data lookups or rendering
+/// the data into the wire format to create a DNS message.
+///
+/// The actual encoding detail is private information to the implementation,
+/// and the application shouldn't assume anything about that except that
+/// each RDATA is considered to consist of one or more generic fields,
+/// and each field is typed as either opaque data or a domain name.
+/// A domain name field has additional attributes
+/// (see \c isc::datasrc::memory::RdataNameAttributes)
+/// so the application can change how the name should be handled in terms
+/// of the DNS protocol (e.g., whether it's subject to name compression).
+///
+/// The following are the current implementation of internal encoding, shown
+/// only for reference.  Applications must not assume this particular form
+/// for the encoded data; in fact, it can change in a future version of the
+/// implementation.
+/// \verbatim
+// The encoded data begin with a series of 16-bit length fields (values are
+// stored in the host byte order).  The sequence may be empty.
+// uint16_t n1_1: size of 1st variable len field (if any) of 1st RDATA
+// uint16_t n1_2: size of 2nd variable len field of 1st RDATA
+// ...
+// uint16_t nN_M: size of last (Mth) variable len field of last (Nth) RDATA
+// uint16_t ns1: size of 1st RRSIG (if any) data
+// ...
+// uint16_t nsL: size of last (Lth) RRSIG data
+// A sequence of packed data fields follows:
+// uint8_t[]: data field value, length specified by nI_J (in case it's
+//            variable-length) or by the per type field spec (in case it's
+//            fixed-length).
+// or
+// opaque data, LabelSequence::getSerializedLength() bytes: data for a name
+// uint8_t[ns1]: 1st RRSIG data
+// ...
+// uint8_t[nsL]: last RRSIG data
+// \endverbatim
+///
+/// As described above, this implementation treats RRSIGs as opaque data
+/// that don't contain any domain names.  Technically, it has a "signer"
+/// domain name field in the sense of RFC4034.  In practice, however, this
+/// field is essentially mere data; it's not subject to name compression,
+/// and since it's very likely to be a subdomain of (or equal to) the
+/// owner name of the corresponding RR (or, if used in a DNS message,
+/// some domain name that already appears before this field), so it won't
+/// be a target of name compression either.  By treating the entire RRSIG
+/// as single-field data we can make the implementation simpler, and probably
+/// make it faster in rendering it into a DNS message.
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+/// \brief General error in RDATA encoding.
+///
+/// This is thrown when \c RdataEncoder encounters a rare, unsupported
+/// situation.
+class RdataEncodingError : public Exception {
+public:
+    RdataEncodingError(const char* file, size_t line, const char* what) :
+        Exception(file, line, what) {}
+};
+
+/// \brief RDATA encoder.
+///
+/// This class provides interfaces to encode a set of RDATA of a specific
+/// RR class and type, possibly with their RRSIG RDATAs, in a memory-efficient
+/// format.  In many cases these sets of RDATA come from a specific (signed
+/// or unsigned) RRset.
+///
+/// It is expected for a single \c RdataEncoder object to be used multiple
+/// times for different sets of RDATA, such as in loading an entire zone
+/// into memory.  Each encoding session begins with the \c start() method,
+/// which sets the context for the specific RR class and type to be encoded.
+/// Any number of calls to \c addRdata() or \c addSIGRdata() follow, each
+/// of which updates the internal state of the encoder with the encoding
+/// information for the given RDATA or RRSIG RDATA, respectively.
+/// The \c addRdata() is expected to be called with an
+/// \c isc::dns::rdata::Rdata object
+/// of the specified class and type, and \c addRdata() checks the consistency
+/// for the purpose of encoding (but it's not completely type safe; for
+/// example, it wouldn't distinguish TXT RDATA and HINFO RDATA.
+/// Likewise, an \c isc::dns::rdata::Rdata given to \c addSIGRdata() is
+/// expected to be of RRSIG, but the method does not check the assumption).
+///
+/// After passing the complete set of RDATA and their RRSIG, the application
+/// is expected to call \c getStorageLength() to know the size of storage
+/// that is sufficient to store all encoded data.  Normally the application
+/// would allocate a memory region of that size, and then call \c encode()
+/// with the prepared region.  The \c encode() method dumps encoded data
+/// to the given memory region.
+///
+/// The caller can reuse the \c RdataEncoder object for another set of RDATA
+/// by repeating the session from \c start().
+class RdataEncoder : boost::noncopyable {
+public:
+    /// \brief Default constructor.
+    RdataEncoder();
+
+    /// \brief The destrcutor.
+    ~RdataEncoder();
+
+    /// \brief Start the encoding session.
+    ///
+    /// It re-initializes the internal encoder state for a new encoding
+    /// session.  The \c rrclass and \c rrtype parameters specify the
+    /// type of RDATA to be encoded in the new session.  Note that if the
+    /// set of RDATA is signed, \c rrtype always specifies the "signed" type;
+    /// it must not be RRSIG.
+    ///
+    /// \throw BadValue RRSIG is specified for rrtype.
+    ///
+    /// \param rrclass The RR class of RDATA to be encoded in the session.
+    /// \param rrtype The RR type of RDATA to be encoded in the session.
+    void start(dns::RRClass rrclass, dns::RRType rrtype);
+
+    /// \brief Add an RDATA for encoding.
+    ///
+    /// This method updates internal state of the \c RdataEncoder() with the
+    /// given RDATA so it will be part of the encoded data in a subsequent
+    /// call to \c encode().
+    ///
+    /// The given \c rdata must be of the RR class and type specified at
+    /// the prior call to \c start().  This method checks the assumption
+    /// to some extent, but the check is not complete; this is generally
+    /// the responsibility of the caller.
+    ///
+    /// The caller can destroy \c rdata after this call is completed.
+    ///
+    /// \note This implementation does not support RDATA (or any subfield of
+    /// it) whose size exceeds 65535 bytes (max uint16_t value).  Such RDATA
+    /// may not necessarily be considered invalid in terms of protocol
+    /// specification, but in practice it's mostly useless because the
+    /// corresponding RR won't fit in any valid DNS message.
+    ///
+    /// As long as the \c rdata is of the correct type and its size is normal,
+    /// this method should normally be exception free.  If it throws, however,
+    /// it doesn't always provide the strong exception guarantee.  In general,
+    /// the caller needs to either destroy the encoder object or restart a
+    /// new session from \c start() should this method throws an exception.
+    ///
+    /// \throw InvalidOperation called before start().
+    /// \throw BadValue inconsistent data found.
+    /// \throw RdataEncodingError A very unusual case, such as over 64KB RDATA.
+    /// \throw std::bad_alloc Internal memory allocation failure.
+    ///
+    /// \param rdata An RDATA to be encoded in the session.
+    void addRdata(const dns::rdata::Rdata& rdata);
+
+    /// \brief Add an RRSIG RDATA for encoding.
+    ///
+    /// This method updates internal state of the \c RdataEncoder() with the
+    /// given RDATA, which is assumed to be of type RRSIG that covers the
+    /// type specified at the time of \c start() for the encoding session.
+    /// The corresponding data for the RRSIG RDATA will be encoded in a
+    /// subsequent call to \c encode().
+    ///
+    /// The passed \c sig_rdata is expected to be of type RRSIG and cover
+    /// the RR type specified at the call to \c start() to this encoding
+    /// session.  But this method does not check if it is the case at all;
+    /// it could even accept any type of RDATA as opaque data.  It's caller's
+    /// responsibility to ensure the assumption.
+    ///
+    /// The caller can destroy \c rdata after this call is completed.
+    ///
+    /// \note Like addRdata(), this implementation does not support
+    /// RRSIG RDATA whose size (in the form of wire format) exceeds 65535
+    /// bytes.
+    ///
+    /// The same note about exception safety as \c addRdata() applies.
+    ///
+    /// \throw InvalidOperation called before start().
+    /// \throw RdataEncodingError A very unusual case, such as over 64KB RDATA.
+    /// \throw std::bad_alloc Internal memory allocation failure.
+    ///
+    /// \param sig_rdata An RDATA to be encoded in the session.  Supposed to
+    /// be of type RRSIG.
+    void addSIGRdata(const dns::rdata::Rdata& sig_rdata);
+
+    /// \brief Return the length of space for encoding for the session.
+    ///
+    /// It returns the size of the encoded data that would be generated for
+    /// the set of RDATA (and RRSIGs) in the encoder at the call of this
+    /// method.  It's ensured that a buffer of that size can be safely passed
+    /// to \c encode() unless there's no other "add" method is called by then.
+    ///
+    /// As long as this method is called after start(), it never throws.
+    ///
+    /// \throw InvalidOperation called before start().
+    ///
+    /// \return The expected size of the encoded data at the time of the call.
+    size_t getStorageLength() const;
+
+    /// \brief Encode RDATAs of the session to a buffer.
+    ///
+    /// This method dumps encoded data for the stored set of RDATA and
+    /// their RRSIGs to a given buffer.  The buffer must have a size
+    /// at least as large as the return value of a prior call to
+    /// \c getStorageLength() (it may be larger than that).
+    ///
+    /// The given buffer must be aligned at the natural boundary for
+    /// 16-bit integers.  The method doesn't check this condition; it's
+    /// caller's responsibility to ensure that.  Note: the alignment
+    /// requirement may change in a future version of this implementation.
+    ///
+    /// As long as this method is called after start() and the buffer is
+    /// valid with a sufficient size, this method never throws.
+    ///
+    /// \throw InvalidOperation called before start().
+    /// \throw BadValue buffer is NULL or it's too short for the encoded data.
+    ///
+    /// \param buf A pointer to the buffer to which encoded data are to be
+    /// dumped.
+    /// \param buf_len The size of the buffer in bytes.
+    void encode(void* buf, size_t buf_len) const;
+
+private:
+    struct RdataEncoderImpl;
+    RdataEncoderImpl* impl_;
+};
+
+/// \brief Attributes of domain name fields of encoded RDATA.
+///
+/// The enum values define special traits of the name that can affect how
+/// it should be handled in rendering or query processing.
+enum RdataNameAttributes {
+    NAMEATTR_NONE = 0,          ///< No special attributes
+    NAMEATTR_COMPRESSIBLE = 1,  ///< Name should be compressed when rendered
+    NAMEATTR_ADDITIONAL = (NAMEATTR_COMPRESSIBLE << 1) ///< Name requires
+                                                      ///< Additional section
+                                                      ///< handling
+};
+
+// forward declaration, defined in a private implementation file.
+struct RdataEncodeSpec;
+
+/// \brief Class to read serialized rdata
+///
+/// This class allows you to read the data encoded by RdataEncoder.
+/// It is rather low-level -- it provides sequence of data fields.
+/// Each field is either opaque data, passed as a pointer and length,
+/// or a name, in the form of dns::LabelSequence (which is always
+/// absolute) and attributes.
+///
+/// Conceptually, these fields correspond to consecutive regions in
+/// wire-format representation of the RDATA, varying the type of above
+/// two cases depending on whether the region corresponds to a domain
+/// name or other data.  For example, for an MX RDATA the field
+/// sequence will be
+/// - 2 bytes of opaque data (which corresponds to the MX preference)
+/// - a domain name (which corresponds to the MX name)
+///
+/// If the encoded data contain multiple MX RDATAs, the same type of
+/// sequence continues for the number of RDATAs.  Note that the opaque
+/// data field does not always corresponds to a specific RDATA field
+/// as is the 2-byte preference field of MX.  For example, the field
+/// sequence for an SOA RDATA in terms of RdataEncoder will be:
+/// - a domain name (which corresponds to the SOA MNAME)
+/// - a domain name (which corresponds to the SOA RNAME)
+/// - 20 bytes of opaque data (for the rest of fields)
+///
+/// So, if you want to construct a general purpose dns::Rdata object
+/// from the field sequence, you'll need to build the complete
+/// wire-format data, and then construct a dns::Rdata object from it.
+///
+/// To use it, contstruct it with the data you got from RDataEncoder,
+/// provide it with callbacks and then iterate through the data.
+/// The callbacks are called with the data fields contained in the
+/// data.
+///
+/// \code
+/// void handleName(const dns::LabelSequence& labels, unsigned int flags) {
+///     ...
+/// }
+/// void handleData(const void* data, size_t size) {
+///     ...
+/// }
+///
+/// RdataReader reader(RRClass::IN(), RRType::AAAA(), size, data,
+///                    rdata_count, sig_count, &handleName, &handleData);
+/// reader.iterate();
+/// \endcode
+///
+/// If you need to do the iteration per RDATA basis rather than per data field
+/// basis, you can use \c iterateRdata() as follows:
+///
+/// \code
+/// for (size_t i = 0; i < rdata_count; ++i)
+///     // maybe do something related to this RDATA
+///     reader.iterateRdata(); // specified actions called for this RDATA
+///     // maybe do some other thing related to this RDATA
+/// }
+/// if (reader.iterateRdata()) {
+///     isc_throw(Unexpected, "Inconsistent data");
+/// }
+/// \endcode
+///
+/// The check after the loop is primarily for consistency
+/// validation, but it would also help a possible subsequent call
+/// to \c iterateAllSigs() if you also want to iterate over RRSIGs;
+/// the final call to \c iterateRdata() updates the internal state of the
+/// reader object so \c iterateAllSigs() can find the RRSIG data more
+/// efficiently.  \c iterateAllSigs() will work correctly even with out
+/// this small optimization, but checking the consistency is a good practice
+/// anyway, and the optimization is an additional bonus.
+///
+/// \note It is caller's responsibility to pass valid data here. This means
+///     the data returned by RdataEncoder and the corresponding class and type.
+///     If this is not the case, all the kinds of pointer hell might get loose.
+class RdataReader {
+public:
+    /// \brief Function called on each name encountered in the data.
+    typedef boost::function<void(const dns::LabelSequence&,
+                                 RdataNameAttributes)> NameAction;
+    /// \brief Function called on each data field in the data.
+    typedef boost::function<void(const void*, size_t)> DataAction;
+
+    /// \brief An NameAction that does intentionally nothing.
+    ///
+    /// This static method can be used as the name action parameter to
+    /// construct \c RdataReader when the caller does not have to anything
+    /// for name fields.
+    static void emptyNameAction(const dns::LabelSequence&,
+                                RdataNameAttributes);
+
+    /// \brief An DataAction that does intentionally nothing.
+    ///
+    /// This static method can be used as the data action parameter to
+    /// construct \c RdataReader when the caller does not have to anything
+    /// for opaque data fields.
+    static void emptyDataAction(const void*, size_t);
+
+    /// \brief Constructor
+    ///
+    /// This constructs the reader on top of some serialized data.
+    /// It does not copy the data, you have to make sure the data
+    /// is valid for the whole life of this object and that they
+    /// don't change.
+    ///
+    /// \param rrclass The class the encoded rdata belongs to.
+    /// \param rrtype The type of the encode rdata.
+    /// \param data The actual data.
+    /// \param rdata_count The number of Rdata encoded in the data.
+    /// \param sig_count The number of RRSig rdata bundled with the data.
+    /// \param name_action The callback to be called on each encountered name.
+    /// \param data_action The callback to be called on each data chunk.
+    RdataReader(const dns::RRClass& rrclass, const dns::RRType& rrtype,
+                const void* data, size_t rdata_count, size_t sig_count,
+                const NameAction& name_action, const DataAction& data_action);
+
+    /// \brief Result of next() and nextSig()
+    ///
+    /// This specifies if there's any boundary in the data at the
+    /// place where the corresponding call to next() or nextSig()
+    /// finished.
+    enum Boundary {
+        NO_BOUNDARY,    ///< It is in the middle of Rdata
+        RDATA_BOUNDARY, ///< At the end of single Rdata
+        RRSET_BOUNDARY  ///< At the end of the RRset (past the end)
+    };
+
+    /// \brief Step to next data field.
+    ///
+    /// Iterate over the next field and call appropriate hook (name_action
+    /// or data_action, depending on the type) as passed to the constructor.
+    ///
+    /// \return It returns NO_BOUNDARY if the next call to next() will process
+    ///     data of the same rdata as this one. RDATA_BOUNDARY is returned when
+    ///     this field is the last of the current rdata. If there are no more
+    ///     data to process, no hook is called and RRSET_BOUNDARY is returned.
+    ///     Therefore, at the end of the whole data, once it processes the last
+    ///     field and returns RDATA_BOUNDARY and then it returns RRSET_BOUNDARY
+    ///     on the next call.
+    Boundary next();
+
+    /// \brief Call next() until the end.
+    ///
+    /// This is just convenience method to iterate through all the data.
+    /// It calls next until it reaches the end (it does not rewind beforehand,
+    /// therefore if you already called next() yourself, it does not start
+    /// at the beginning).
+    void iterate() {
+        while (nextInternal(name_action_, data_action_) != RRSET_BOUNDARY) {}
+    }
+
+    /// \brief Call next() until the end of current rdata.
+    ///
+    /// This is a convenience method to iterate until the end of current
+    /// rdata. Notice this may cause more than one field being processed,
+    /// as some rrtypes are more complex.
+    ///
+    /// \return If there was Rdata to iterate through.
+    bool iterateRdata() {
+        while (true) {
+            switch (nextInternal(name_action_, data_action_)) {
+                case NO_BOUNDARY: break;
+                case RDATA_BOUNDARY: return (true);
+                case RRSET_BOUNDARY: return (false);
+            }
+        }
+    }
+
+    /// \brief Step to next field of RRSig data.
+    ///
+    /// This is almost the same as next(), but it iterates through the
+    /// associated RRSig data, not the data for the given RRType.
+    Boundary nextSig();
+
+    /// \brief Iterate through all RRSig data.
+    ///
+    /// This is almost the same as iterate(), but it iterates through the
+    /// RRSig data instead.
+    void iterateAllSigs() {
+        while (nextSig() != RRSET_BOUNDARY) {}
+    }
+
+    /// \brief Iterate through the current RRSig Rdata.
+    ///
+    /// This is almote the same as iterateRdata, except it is for single
+    /// signature Rdata.
+    ///
+    /// In practice, this should process one DATA field.
+    bool iterateSingleSig() {
+        while (true) {
+            switch (nextSig()) {
+                case NO_BOUNDARY:
+                    isc_throw(isc::Unexpected, "NO_BOUNDARY inside an RRSig. "
+                              "Data corruption? Bug inside RdataReader?");
+                case RDATA_BOUNDARY: return (true);
+                case RRSET_BOUNDARY: return (false);
+            }
+        }
+    }
+
+    /// \brief Rewind the iterator to the beginnig of data.
+    ///
+    /// The following next() and nextSig() will start iterating from the
+    /// beginning again.
+    void rewind();
+
+    /// \brief Returns the size of associated data.
+    ///
+    /// This should be the same as the return value of
+    /// RdataEncoder::getStorageLength() for the same set of data.
+    /// The intended use of this method is to tell the caller the size of
+    /// data that were possibly dynamically allocated so that the caller can
+    /// use it for deallocation.
+    ///
+    /// This method only uses the parameters given at the construction of the
+    /// object, and does not rely on or modify other mutable states.
+    /// In practice, when the caller wants to call this method, that would be
+    /// the only purpose of that RdataReader object (although it doesn't have
+    /// to be so).
+    size_t getSize() const;
+private:
+    const NameAction name_action_;
+    const DataAction data_action_;
+    const RdataEncodeSpec& spec_;
+    // Total number of var-length fields, count of signatures
+    const size_t var_count_total_, sig_count_, spec_count_;
+    // Pointer to the beginning of length fields
+    const uint16_t* const lengths_;
+    // Pointer to the beginning of the data (after the lengths)
+    const uint8_t* const data_;
+    // Pointer to the first data signature
+    // Will be computed during the normal RR iteration
+    const uint8_t* sigs_;
+    // The positions in data.
+    size_t data_pos_, spec_pos_, length_pos_;
+    size_t sig_pos_, sig_data_pos_;
+    Boundary nextInternal(const NameAction& name_action,
+                          const DataAction& data_action);
+};
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_RDATA_ENCODER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/memory/rdata_serialization_priv.cc b/src/lib/datasrc/memory/rdata_serialization_priv.cc
new file mode 100644
index 0000000..2974079
--- /dev/null
+++ b/src/lib/datasrc/memory/rdata_serialization_priv.cc
@@ -0,0 +1,65 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// This file is directly included from the rdata_serialization.cc. It would
+// be part of the file if we didn't need to steal some definitions from here
+// for the tests (which include it too). To make the intent clear, it is
+// named .cc, not .h.
+
+struct RdataFieldSpec {
+    enum FieldType {
+        FIXEDLEN_DATA = 0,      // fixed-length data field
+        VARLEN_DATA,            // variable-length data field
+        DOMAIN_NAME             // domain name
+    };
+
+    const FieldType type;       // field type
+
+    // The length of fixed-length data field.  Only valid for FIXEDLEN_DATA.
+    // For type DOMAIN_NAME, set it to 0.
+    const uint16_t fixeddata_len;
+
+    // Attributes of the name.  Only valid for DOMAIN_NAME.
+    // For type _DATA, set it to NAMEATTR_NONE.
+    const RdataNameAttributes name_attributes;
+};
+
+/// Specification of RDATA in terms of internal encoding.
+///
+/// The fields must be a sequence of:
+/// <0 or 1 fixed/var-len data field>,
+/// <1 or more domain name fields>,
+/// <1 fixed/var-len data field>,
+/// <1 or more domain name fields>,
+/// <1 fixed/var-len data field>,
+/// ...and so on.
+/// There must not be more than one consecutive data fields (i.e., without
+/// interleaved by a domain name); it would just be inefficient in terms of
+/// memory footprint and iterating over the fields, and it would break
+/// some assumption within the encoder implementation.  For consecutive
+/// data fields in the DNS protocol, if all fields have fixed lengths, they
+/// should be combined into a single fixed-length field (like the last 20
+/// bytes of SOA RDATA).  If there's a variable length field, they should be
+/// combined into a single variable-length field (such as DNSKEY, which has
+/// 3 fixed-length fields followed by one variable-length field).
+struct RdataEncodeSpec {
+    const uint16_t field_count; // total number of fields (# of fields member)
+    const uint16_t name_count;  // number of domain name fields
+    const uint16_t varlen_count; // number of variable-length data fields
+    const RdataFieldSpec* const fields; // list of field specs
+};
+
+/// \brief Get the spec for given class and type
+const RdataEncodeSpec&
+getRdataEncodeSpec(const RRClass& rrclass, const RRType& rrtype);
diff --git a/src/lib/datasrc/memory/rdataset.cc b/src/lib/datasrc/memory/rdataset.cc
new file mode 100644
index 0000000..aae64f3
--- /dev/null
+++ b/src/lib/datasrc/memory/rdataset.cc
@@ -0,0 +1,178 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrset.h>
+
+#include "rdataset.h"
+#include "rdata_serialization.h"
+
+#include <boost/static_assert.hpp>
+
+#include <stdint.h>
+#include <cstring>
+#include <typeinfo>             // for bad_cast
+#include <new>                  // for the placement new
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+namespace {
+RRType
+getCoveredType(const Rdata& rdata) {
+    try {
+        const generic::RRSIG& rrsig_rdata =
+            dynamic_cast<const generic::RRSIG&>(rdata);
+        return (rrsig_rdata.typeCovered());
+    } catch (const std::bad_cast&) {
+        isc_throw(BadValue, "Non RRSIG is given where it's expected");
+    }
+}
+}
+
+RdataSet*
+RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
+                 ConstRRsetPtr rrset, ConstRRsetPtr sig_rrset)
+{
+    // Check basic validity
+    if (!rrset && !sig_rrset) {
+        isc_throw(BadValue, "Both RRset and RRSIG are NULL");
+    }
+    if (rrset && rrset->getRdataCount() == 0) {
+        isc_throw(BadValue, "Empty RRset");
+    }
+    if (sig_rrset && sig_rrset->getRdataCount() == 0) {
+        isc_throw(BadValue, "Empty SIG RRset");
+    }
+    if (rrset && sig_rrset && rrset->getClass() != sig_rrset->getClass()) {
+        isc_throw(BadValue, "RR class doesn't match between RRset and RRSIG");
+    }
+
+    // Check assumptions on the number of RDATAs
+    if (rrset && rrset->getRdataCount() > MAX_RDATA_COUNT) {
+        isc_throw(RdataSetError, "Too many RDATAs for RdataSet: "
+                  << rrset->getRdataCount() << ", must be <= "
+                  << MAX_RDATA_COUNT);
+    }
+    if (sig_rrset && sig_rrset->getRdataCount() > MAX_RRSIG_COUNT) {
+        isc_throw(RdataSetError, "Too many RRSIGs for RdataSet: "
+                  << sig_rrset->getRdataCount() << ", must be <= "
+                  << MAX_RRSIG_COUNT);
+    }
+
+    const RRClass rrclass = rrset ? rrset->getClass() : sig_rrset->getClass();
+    const RRType rrtype = rrset ? rrset->getType() :
+        getCoveredType(sig_rrset->getRdataIterator()->getCurrent());
+    const RRTTL rrttl = rrset ? rrset->getTTL() : sig_rrset->getTTL();
+
+    encoder.start(rrclass, rrtype);
+    if (rrset) {
+        for (RdataIteratorPtr it = rrset->getRdataIterator();
+             !it->isLast();
+             it->next()) {
+            encoder.addRdata(it->getCurrent());
+        }
+    }
+    if (sig_rrset) {
+        for (RdataIteratorPtr it = sig_rrset->getRdataIterator();
+             !it->isLast();
+             it->next())
+        {
+            if (getCoveredType(it->getCurrent()) != rrtype) {
+                isc_throw(BadValue, "Type covered doesn't match");
+            }
+            encoder.addSIGRdata(it->getCurrent());
+        }
+    }
+
+    const size_t rrsig_count = sig_rrset ? sig_rrset->getRdataCount() : 0;
+    const size_t ext_rrsig_count_len =
+        rrsig_count >= MANY_RRSIG_COUNT ? sizeof(uint16_t) : 0;
+    const size_t data_len = encoder.getStorageLength();
+    void* p = mem_sgmt.allocate(sizeof(RdataSet) + ext_rrsig_count_len +
+                                data_len);
+    RdataSet* rdataset = new(p) RdataSet(rrtype,
+                                         rrset ? rrset->getRdataCount() : 0,
+                                         rrsig_count, rrttl);
+    if (rrsig_count >= MANY_RRSIG_COUNT) {
+        *rdataset->getExtSIGCountBuf() = rrsig_count;
+    }
+    encoder.encode(rdataset->getDataBuf(), data_len);
+    return (rdataset);
+}
+
+void
+RdataSet::destroy(util::MemorySegment& mem_sgmt, RRClass rrclass,
+                  RdataSet* rdataset)
+{
+    const size_t data_len =
+        RdataReader(rrclass, rdataset->type,
+                    reinterpret_cast<const uint8_t*>(rdataset->getDataBuf()),
+                    rdataset->getRdataCount(), rdataset->getSigRdataCount(),
+                    &RdataReader::emptyNameAction,
+                    &RdataReader::emptyDataAction).getSize();
+    const size_t ext_rrsig_count_len =
+        rdataset->sig_rdata_count_ == MANY_RRSIG_COUNT ? sizeof(uint16_t) : 0;
+    rdataset->~RdataSet();
+    mem_sgmt.deallocate(rdataset,
+                        sizeof(RdataSet) + ext_rrsig_count_len + data_len);
+}
+
+namespace {
+// Convert the given RRTTL into the corresponding 32-bit unsigned integer,
+// in the network byte order.  We do not use htonl() to be as portable as
+// possible.
+uint32_t
+convertTTL(RRTTL ttl) {
+    const uint32_t ttl_val = ttl.getValue();
+    uint8_t buf[4];
+    buf[0] = (ttl_val & 0xff000000) >> 24;
+    buf[1] = (ttl_val & 0x00ff0000) >> 16;
+    buf[2] = (ttl_val & 0x0000ff00) >> 8;
+    buf[3] = (ttl_val & 0x000000ff);
+    uint32_t ret;
+    std::memcpy(&ret, buf, sizeof(ret));
+    return (ret);
+}
+}
+
+RdataSet::RdataSet(RRType type_param, size_t rdata_count,
+                   size_t sig_rdata_count, RRTTL ttl) :
+    type(type_param),
+    sig_rdata_count_(sig_rdata_count >= MANY_RRSIG_COUNT ?
+                     MANY_RRSIG_COUNT : sig_rdata_count),
+    rdata_count_(rdata_count), ttl_(convertTTL(ttl))
+{
+    // Make sure an RRType object is essentially a plain 16-bit value, so
+    // our assumption of the size of RdataSet holds.  If it's not the case
+    // we should use the bare value instead of the class object.
+    BOOST_STATIC_ASSERT(sizeof(type) == sizeof(uint16_t));
+
+    // Confirm we meet the alignment requirement for RdataEncoder
+    // ("this + 1" should be safely passed to the encoder).
+    BOOST_STATIC_ASSERT(sizeof(RdataSet) % sizeof(uint16_t) == 0);
+}
+
+} // namespace memory
+} // namespace datasrc
+} // datasrc isc
diff --git a/src/lib/datasrc/memory/rdataset.h b/src/lib/datasrc/memory/rdataset.h
new file mode 100644
index 0000000..b0b3b48
--- /dev/null
+++ b/src/lib/datasrc/memory/rdataset.h
@@ -0,0 +1,389 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_MEMORY_RDATASET_H
+#define DATASRC_MEMORY_RDATASET_H 1
+
+#include <util/memory_segment.h>
+
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+
+#include <boost/interprocess/offset_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <stdint.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+class RdataEncoder;
+
+/// \brief General error on creating RdataSet.
+///
+/// This is thrown when creating \c RdataSet encounters a rare, unsupported
+/// situation.
+class RdataSetError : public Exception {
+public:
+    RdataSetError(const char* file, size_t line, const char* what) :
+        Exception(file, line, what) {}
+};
+
+/// \brief Memory-efficient representation of RRset data with RRSIGs.
+///
+/// This class provides memory-efficient and lightweight interface to various
+/// attributes of an RRset, which may or may not be signed with RRSIGs.
+///
+/// This class is primarily intended to be used in the in-memory data source
+/// implementation, and is not supposed to be used by general applications.
+/// The major design goals is to keep required memory footprint for the given
+/// amount of data as small as possible, while providing a reasonably
+/// efficient interface to examine the data, focusing on zone lookup and DNS
+/// message rendering.
+///
+/// It encodes a specific set of RRset and (when signed) its RRSIGs, excluding
+/// the owner name and the RR class.  The owner name is supposed to be
+/// maintained by the application outside this class (the intended place to
+/// store this information is a \c DomainTree node, although this
+/// implementation does not rely on that intent).  The RR class must be the
+/// same in a single zone, and it's simply a waste if we have it with each
+/// RRset.  The RR class information is therefore expected to be maintained
+/// outside this class.
+///
+/// This class imposes some limitations on the number of RDATAs of the RRset
+/// and RRSIG: a (non RRSIG) RRset containing more than 8191 (2^13 - 1)
+/// or an RRSIG containing more than 65535 (2^16 - 1) RDATAs cannot be
+/// maintained in this class.  The former restriction comes from the
+/// following observation: any RR in wire format in a DNS message must at
+/// least contain 10 bytes of data (for RR type, class, TTL and RDATA length),
+/// and since a valid DNS message must not be larger than 65535 bytes,
+/// no valid DNS response can contain more than 6554 RRs.  So, in practice,
+/// it should be reasonable even if we reject very large RRsets that would
+/// not fit in a DNS message.  For the same reason we restrict the number of
+/// RRSIGs, although due to internal implementation details the limitation
+/// is more relaxed for RRSIGs.
+///
+/// \note (This is pure implementation details) By limiting the number of
+/// RDATAs so it will fit in a 13-bit integer, we can use 3 more bits in a
+/// 2-byte integer for other purposes.  We use this additional field to
+/// represent the number of RRSIGs up to 6, while using the value of 7 to mean
+/// there are more than 6 RRSIGs.  In the vast majority of real world
+/// deployment, an RRset should normally have only a few RRSIGs, and 6 should
+/// normally be more than sufficient.  So we can cover most practical cases
+/// regarding the number of records with this 2-byte field.
+///
+/// A set of objects of this class (which would be \c RdataSets of various
+/// types of the same owner name) will often be maintained in a single linked
+/// list.  The class has a member variable to make the link.
+///
+/// This class is designed so an instance can be stored in a shared
+/// memory region.  So it only contains straightforward data (e.g., it
+/// doesn't hold a pointer to an object of some base class that
+/// contains virtual methods), and the pointer member (see the
+/// previous paragraph) is represented as an offset pointer.  For the
+/// same reason this class should never have virtual methods (and as a
+/// result, should never be inherited in practice).  When this class
+/// is extended these properties must be preserved.
+///
+/// The \c RdataSet class itself only contains a subset of attributes that
+/// it is conceptually expected to contain.  The rest of the attributes
+/// are encoded in a consecutive memory region immediately following the main
+/// \c RdataSet object.  The memory layout would be as follows:
+/// \verbatim
+/// RdataSet object
+/// (optional) uint16_t: number of RRSIGs, if it's larger than 6 (see above)
+/// encoded RDATA (generated by RdataEncoder) \endverbatim
+///
+/// This is shown here only for reference purposes.  The application must not
+/// assume any particular format of data in this region directly; it must
+/// get access to it via public interfaces provided in the main \c RdataSet
+/// class.
+class RdataSet : boost::noncopyable {
+public:
+    /// \brief Allocate and construct \c RdataSet
+    ///
+    /// This static method allocates memory for a new \c RdataSet
+    /// object for the set of an RRset and (if it's supposed to be signed)
+    /// RRSIG from the given memory segment, constructs the object, and
+    /// returns a pointer to it.
+    ///
+    /// Normally the (non RRSIG) RRset is given (\c rrset is not NULL) while
+    /// its RRSIG (\c sig_rrset) may or may not be provided.  But it's also
+    /// expected that in some rare (mostly broken) cases there can be an RRSIG
+    /// RR in a zone without having the covered RRset in the zone.  To handle
+    /// such cases, this class allows to only hold RRSIG, in which case
+    /// \c rrset can be NULL.  At least \c rrset or \c sig_rrset must be
+    /// non NULL, however.  Also, if non NULL, the RRset must not be empty,
+    /// that is, it must contain at least one RDATA.
+    ///
+    /// The RR type of \c rrset must not be RRSIG; the RR type of \c sig_rrset
+    /// must be RRSIG.
+    ///
+    /// When both \c rrset and \c sig_rrset are provided (both are non
+    /// NULL), the latter must validly cover the former: the RR class
+    /// must be identical; the type covered field of any RDATA of \c
+    /// sig_rrset must be identical to the RR type of \c rrset.  The owner
+    /// name of these RRsets must also be identical, but this implementation
+    /// doesn't require it because \c RdataSet itself does not rely on the
+    /// owner name, and it should be pretty likely that this condition is met
+    /// in the context where this class is used (and, name comparison is
+    /// relatively expensive, and if we end up comparing them twice the
+    /// overhead can be non negligible).
+    ///
+    /// If any of the above conditions isn't met, an isc::BadValue exception
+    /// will be thrown; basically, there should be a bug in the caller if this
+    /// happens.
+    ///
+    /// Due to implementation limitations, this class cannot contain more than
+    /// 8191 RDATAs for the non RRISG RRset; also, it cannot contain more than
+    /// 65535 RRSIGs.  If the given RRset(s) fail to meet this condition,
+    /// an \c RdataSetError exception will be thrown.
+    ///
+    /// \throw isc::BadValue Given RRset(s) are invalid (see the description)
+    /// \throw RdataSetError Number of RDATAs exceed the limits
+    /// \throw std::bad_alloc Memory allocation fails.
+    ///
+    /// \param mem_sgmt A \c MemorySegment from which memory for the new
+    /// \c RdataSet is allocated.
+    /// \param encoder The RDATA encoder to encode \c rrset and \c sig_rrset
+    /// with the \c RdataSet to be created.
+    /// \param rrset A (non RRSIG) RRset from which the \c RdataSet is to be
+    /// created.  Can be NULL if sig_rrset is not.
+    /// \param sig_rrset An RRSIG RRset from which the \c RdataSet is to be
+    /// created.  Can be NULL if rrset is not.
+    ///
+    /// \return A pointer to the created \c RdataSet.
+    static RdataSet* create(util::MemorySegment& mem_sgmt,
+                            RdataEncoder& encoder,
+                            dns::ConstRRsetPtr rrset,
+                            dns::ConstRRsetPtr sig_rrset);
+
+    /// \brief Destruct and deallocate \c RdataSet
+    ///
+    /// Note that this method needs to know the expected RR class of the
+    /// \c RdataSet.  This is because the destruction may depend on the
+    /// internal data encoding that only \c RdataEncoder and \c RdataReader
+    /// know, and they need to know the corresponding RR class and type to
+    /// identify the internal data representation.  Since \c RdataSet itself
+    /// does not hold the class information, the caller needs to provide it.
+    /// Obviously, this RR class must be identical to the RR class of \c rrset
+    /// (when given) or of \c sig_rrset (when \c rrset isn't given) at the
+    /// \c create() time.
+    ///
+    /// \throw none
+    ///
+    /// \param mem_sgmt The \c MemorySegment that allocated memory for
+    /// \c node.
+    /// \param rrclass The RR class of the \c RdataSet to be destroyed.
+    /// \param rdataset A non NULL pointer to a valid \c RdataSet object
+    /// that was originally created by the \c create() method (the behavior
+    /// is undefined if this condition isn't met).
+    static void destroy(util::MemorySegment& mem_sgmt, dns::RRClass rrclass,
+                        RdataSet* rdataset);
+
+    /// \brief Find \c RdataSet of given RR type from a list (const version).
+    ///
+    /// This function is a convenient shortcut for commonly used operation of
+    /// finding a given type of \c RdataSet from a linked list of them.
+    ///
+    /// It follows the linked list of \c RdataSet objects (via their \c next
+    /// member) starting the given head, until it finds an object of the
+    /// given RR type.  If found, it returns a (bare) pointer to the object;
+    /// if not found in the entire list, it returns NULL.  The head pointer
+    /// can be NULL, in which case this function will simply return NULL.
+    ///
+    /// \note This function is defined as a (static) class method to
+    /// clarify its an operation for \c RdataSet objects and to make the
+    /// name shorter.  But its implementation does not depend on private
+    /// members of the class, and it should be kept if and when this method
+    /// needs to be extended, unless there's a reason other than simply
+    /// because it's already a member function.
+    ///
+    /// \param rdata_head A pointer to \c RdataSet from which the search
+    /// starts.  It can be NULL.
+    /// \param type The RRType of \c RdataSet to find.
+    /// \return A pointer to the found \c RdataSet or NULL if none found.
+    static const RdataSet*
+    find(const RdataSet* rdataset_head, const dns::RRType& type) {
+        return (find<const RdataSet>(rdataset_head, type));
+    }
+
+    /// \brief Find \c RdataSet of given RR type from a list (non const
+    /// version).
+    ///
+    /// This is similar to the const version, except it takes and returns non
+    /// const pointers.
+    static RdataSet*
+    find(RdataSet* rdataset_head, const dns::RRType& type) {
+        return (find<RdataSet>(rdataset_head, type));
+    }
+
+    typedef boost::interprocess::offset_ptr<RdataSet> RdataSetPtr;
+    typedef boost::interprocess::offset_ptr<const RdataSet> ConstRdataSetPtr;
+
+    // Note: the size and order of the members are carefully chosen to
+    // maximize efficiency.  Don't change them unless there's strong reason
+    // for that and the consequences are considered.
+    // For convenience (and since this class is mostly intended to be an
+    // internal definition for the in-memory data source implementation),
+    // we allow the application to get access to some members directly.
+    // Some others require some conversion to use in a meaningful way,
+    // for which we force the application to use accessor methods in order
+    // to prevent misuse.
+
+    RdataSetPtr next; ///< Pointer to the next \c RdataSet (when linked)
+    const dns::RRType type;     ///< The RR type of the \c RdataSet
+
+private:
+    const uint16_t sig_rdata_count_ : 3; // # of RRSIGs, up to 6 (7 means many)
+    const uint16_t rdata_count_ : 13; // # of RDATAs, up to 8191
+    const uint32_t ttl_;       // TTL of the RdataSet, net byte order
+
+    // Max number of normal RDATAs that can be stored in \c RdataSet.
+    // It's 2^13 - 1 = 8191.
+    static const size_t MAX_RDATA_COUNT = (1 << 13) - 1;
+
+    // Max number of RRSIGs that can be stored in \c RdataSet.
+    // It's 2^16 - 1 = 65535.
+    static const size_t MAX_RRSIG_COUNT = (1 << 16) - 1;
+
+    // Indicate the \c RdataSet contains many RRSIGs that require an additional
+    // field for the real number of RRSIGs.  It's 2^3 - 1 = 7.
+    static const size_t MANY_RRSIG_COUNT = (1 << 3) - 1;
+
+public:
+    /// \brief Return the bare pointer to the next node.
+    ///
+    /// In such an operation as iterating over a linked list of \c RdataSet
+    /// object, using this method is generally more efficient than using
+    /// the \c next member directly because it prevents unintentional
+    /// creation of offset pointer objects.  While the application can
+    /// get the same result by directly calling get() on \c next, it would
+    /// help encourage the use of more efficient usage if we provide an
+    /// explicit accessor.
+    const RdataSet* getNext() const { return (next.get()); }
+
+    /// \brief Return the bare pointer to the next node, mutable version.
+    RdataSet* getNext() { return (next.get()); }
+
+    /// \brief Return the number of RDATAs stored in the \c RdataSet.
+    size_t getRdataCount() const { return (rdata_count_); }
+
+    /// \brief Return the number of RRSIG RDATAs stored in the \c RdataSet.
+    size_t getSigRdataCount() const {
+        if (sig_rdata_count_ < MANY_RRSIG_COUNT) {
+            return (sig_rdata_count_);
+        } else {
+            return (*getExtSIGCountBuf());
+        }
+    }
+
+    /// \brief Return a pointer to the TTL data of the \c RdataSet.
+    ///
+    /// The returned pointer points to a memory region that is valid at least
+    /// for 32 bits, storing the TTL of the \c RdataSet in the network byte
+    /// order.  It returns opaque data to make it clear that unless the wire
+    /// format data is necessary (e.g., when rendering it in a DNS message),
+    /// it should be converted to, e.g., an \c RRTTL object explicitly.
+    ///
+    /// \throw none
+    const void* getTTLData() const { return (&ttl_); }
+
+    /// \brief Accessor to the memory region for encoded RDATAs.
+    ///
+    /// The only valid usage of the returned pointer is to pass it to
+    /// the constructor of \c RdataReader.
+    ///
+    /// \throw none
+    const void* getDataBuf() const {
+        return (getDataBuf<const void, const RdataSet>(this));
+    }
+
+private:
+    /// \brief Accessor to the memory region for encoded RDATAs, mutable
+    /// version.
+    ///
+    /// This version is only used within the class implementation, so it's
+    /// defined as private.
+    void* getDataBuf() {
+        return (getDataBuf<void, RdataSet>(this));
+    }
+
+    // Implementation of getDataBuf().  Templated to unify the mutable and
+    // immutable versions.
+    template <typename RetType, typename ThisType>
+    static RetType* getDataBuf(ThisType* rdataset) {
+        if (rdataset->sig_rdata_count_ < MANY_RRSIG_COUNT) {
+            return (rdataset + 1);
+        } else {
+            return (rdataset->getExtSIGCountBuf() + 1);
+        }
+    }
+
+    /// \brief Accessor to the memory region for the RRSIG count field for
+    /// a large number of RRSIGs.
+    ///
+    /// These are used only internally and defined as private.
+    const uint16_t* getExtSIGCountBuf() const {
+        return (reinterpret_cast<const uint16_t*>(this + 1));
+    }
+    uint16_t* getExtSIGCountBuf() {
+        return (reinterpret_cast<uint16_t*>(this + 1));
+    }
+
+    // Shared by both mutable and immutable versions of find()
+    template <typename RdataSetType>
+    static RdataSetType*
+    find(RdataSetType* rdataset_head, const dns::RRType& type) {
+        for (RdataSetType* rdataset = rdataset_head;
+             rdataset != NULL;
+             rdataset = rdataset->getNext()) // use getNext() for efficiency
+        {
+            if (rdataset->type == type) {
+                return (rdataset);
+            }
+        }
+        return (NULL);
+    }
+
+    /// \brief The constructor.
+    ///
+    /// An object of this class is always expected to be created by the
+    /// allocator (\c create()), so the constructor is hidden as private.
+    ///
+    /// It never throws an exception.
+    RdataSet(dns::RRType type, size_t rdata_count, size_t sig_rdata_count,
+             dns::RRTTL ttl);
+
+    /// \brief The destructor.
+    ///
+    /// An object of this class is always expected to be destroyed explicitly
+    /// by \c destroy(), so the destructor is hidden as private.
+    ///
+    /// This currently does nothing, but is explicitly defined to clarify
+    /// it's intentionally defined as private.
+    ~RdataSet() {}
+};
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_RDATASET_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/memory/segment_object_holder.h b/src/lib/datasrc/memory/segment_object_holder.h
new file mode 100644
index 0000000..384f4ef
--- /dev/null
+++ b/src/lib/datasrc/memory/segment_object_holder.h
@@ -0,0 +1,63 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_MEMORY_SEGMENT_OBJECT_HOLDER_H
+#define DATASRC_MEMORY_SEGMENT_OBJECT_HOLDER_H 1
+
+#include <util/memory_segment.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+namespace detail {
+
+// A simple holder to create and use some objects in this implementation
+// in an exception safe manner.   It works like std::auto_ptr but much
+// more simplified.
+// template parameter T is the type of object allocated by mem_sgmt.
+// template parameter ARG_T is the type that will be passed to destroy()
+// (deleter functor, etc).  It must be copyable.
+template <typename T, typename ARG_T>
+class SegmentObjectHolder {
+public:
+    SegmentObjectHolder(util::MemorySegment& mem_sgmt, T* obj, ARG_T arg) :
+        mem_sgmt_(mem_sgmt), obj_(obj), arg_(arg)
+    {}
+    ~SegmentObjectHolder() {
+        if (obj_ != NULL) {
+            T::destroy(mem_sgmt_, obj_, arg_);
+        }
+    }
+    T* get() { return (obj_); }
+    T* release() {
+        T* ret = obj_;
+        obj_ = NULL;
+        return (ret);
+    }
+private:
+    util::MemorySegment& mem_sgmt_;
+    T* obj_;
+    ARG_T arg_;
+};
+
+} // detail
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_SEGMENT_OBJECT_HOLDER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/memory/tests/Makefile.am b/src/lib/datasrc/memory/tests/Makefile.am
index 2275d84..22fd4bf 100644
--- a/src/lib/datasrc/memory/tests/Makefile.am
+++ b/src/lib/datasrc/memory/tests/Makefile.am
@@ -18,9 +18,13 @@ if HAVE_GTEST
 TESTS += run_unittests
 
 run_unittests_SOURCES = run_unittests.cc
-run_unittests_SOURCES += rdata_encoder_unittest.cc
+run_unittests_SOURCES += rdata_serialization_unittest.cc
+run_unittests_SOURCES += rdataset_unittest.cc
 run_unittests_SOURCES += domaintree_unittest.cc
 run_unittests_SOURCES += zone_table_unittest.cc
+run_unittests_SOURCES += zone_data_unittest.cc
+run_unittests_SOURCES += memory_segment_test.h
+run_unittests_SOURCES += segment_object_holder_unittest.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
@@ -28,6 +32,8 @@ run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 run_unittests_LDADD = $(builddir)/../libdatasrc_memory.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libb10-testutils.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 run_unittests_LDADD += $(GTEST_LDADD)
 endif
diff --git a/src/lib/datasrc/memory/tests/domaintree_unittest.cc b/src/lib/datasrc/memory/tests/domaintree_unittest.cc
index cfb223a..ab6cb1e 100644
--- a/src/lib/datasrc/memory/tests/domaintree_unittest.cc
+++ b/src/lib/datasrc/memory/tests/domaintree_unittest.cc
@@ -59,18 +59,13 @@ const size_t Name::MAX_LABELS;
 
 namespace {
 
-class DeleterType {
-public:
-    DeleterType() {}
-
-    void operator()(util::MemorySegment&, int* i) const {
-        delete i;
-    }
-};
+void deleteData(int* i) {
+    delete i;
+}
 
-typedef DomainTree<int, DeleterType> TestDomainTree;
-typedef DomainTreeNode<int, DeleterType> TestDomainTreeNode;
-typedef DomainTreeNodeChain<int, DeleterType> TestDomainTreeNodeChain;
+typedef DomainTree<int> TestDomainTree;
+typedef DomainTreeNode<int> TestDomainTreeNode;
+typedef DomainTreeNodeChain<int> TestDomainTreeNodeChain;
 
 class TreeHolder {
 public:
@@ -78,7 +73,7 @@ public:
         mem_sgmt_(mem_sgmt), tree_(tree)
     {}
     ~TreeHolder() {
-        TestDomainTree::destroy(mem_sgmt_, tree_);
+        TestDomainTree::destroy(mem_sgmt_, tree_, deleteData);
     }
     TestDomainTree* get() { return (tree_); }
 private:
@@ -102,12 +97,13 @@ protected:
         int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
         for (int i = 0; i < name_count; ++i) {
             dtree.insert(mem_sgmt_, Name(domain_names[i]), &dtnode);
-            dtnode->setData(mem_sgmt_, new int(i + 1));
+            // Check the node doesn't have any data initially.
+            EXPECT_EQ(static_cast<int*>(NULL),
+                      dtnode->setData(new int(i + 1)));
 
             dtree_expose_empty_node.insert(mem_sgmt_, Name(domain_names[i]),
                                             &dtnode);
-            dtnode->setData(mem_sgmt_, new int(i + 1));
-
+            EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(i + 1)));
         }
     }
 
@@ -125,13 +121,22 @@ TEST_F(DomainTreeTest, nodeCount) {
 
     // Delete all nodes, then the count should be set to 0.  This also tests
     // the behavior of deleteAllNodes().
-    dtree.deleteAllNodes(mem_sgmt_);
+    dtree.deleteAllNodes(mem_sgmt_, deleteData);
     EXPECT_EQ(0, dtree.getNodeCount());
 }
 
 TEST_F(DomainTreeTest, setGetData) {
-    dtnode->setData(mem_sgmt_, new int(11));
+    // set new data to an existing node.  It should have some data.
+    int* newdata = new int(11);
+    int* olddata = dtnode->setData(newdata);
+    EXPECT_NE(static_cast<int*>(NULL), olddata);
+    deleteData(olddata);
     EXPECT_EQ(11, *(dtnode->getData()));
+
+    // clear the node.  we should get the new data back we just passed.
+    olddata = dtnode->setData(NULL);
+    EXPECT_EQ(newdata, olddata);
+    deleteData(olddata);
 }
 
 TEST_F(DomainTreeTest, insertNames) {
@@ -151,7 +156,9 @@ TEST_F(DomainTreeTest, insertNames) {
                                                   Name("example.com"),
                                                   &dtnode));
     EXPECT_EQ(17, dtree.getNodeCount());
-    dtnode->setData(mem_sgmt_, new int(12));
+    // add data to it; also make sure it doesn't have data right now
+    // (otherwise it would leak)
+    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(12)));
 
     // return ALREADYEXISTS, since node "example.com" already has
     // been explicitly inserted
@@ -381,7 +388,7 @@ performCallbackTest(TestDomainTree& dtree,
     EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt,
                                                   Name("callback.example"),
                                                   &dtnode));
-    dtnode->setData(mem_sgmt, new int(1));
+    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(1)));
     EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
 
     // enable/re-disable callback
@@ -397,7 +404,7 @@ performCallbackTest(TestDomainTree& dtree,
     EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt,
                                                   Name("sub.callback.example"),
                                                   &subdtnode));
-    subdtnode->setData(mem_sgmt, new int(2));
+    EXPECT_EQ(static_cast<int*>(NULL), subdtnode->setData(new int(2)));
     TestDomainTreeNode* parentdtnode;
     EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt,
                                                         Name("example"),
@@ -997,7 +1004,7 @@ TEST_F(DomainTreeTest, root) {
     TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
     TestDomainTree& root(*tree_holder.get());
     root.insert(mem_sgmt_, Name::ROOT_NAME(), &dtnode);
-    dtnode->setData(mem_sgmt_, new int(1));
+    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(1)));
 
     EXPECT_EQ(TestDomainTree::EXACTMATCH,
               root.find(Name::ROOT_NAME(), &cdtnode));
@@ -1009,7 +1016,7 @@ TEST_F(DomainTreeTest, root) {
     // Insert a new name that better matches the query name.  find() should
     // find the better one.
     root.insert(mem_sgmt_, Name("com"), &dtnode);
-    dtnode->setData(mem_sgmt_, new int(2));
+    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(2)));
     EXPECT_EQ(TestDomainTree::PARTIALMATCH,
               root.find(Name("example.com"), &cdtnode));
     EXPECT_EQ(dtnode, cdtnode);
diff --git a/src/lib/datasrc/memory/tests/memory_segment_test.h b/src/lib/datasrc/memory/tests/memory_segment_test.h
new file mode 100644
index 0000000..3195a9b
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/memory_segment_test.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_MEMORY_SEGMENT_TEST_H
+#define DATASRC_MEMORY_SEGMENT_TEST_H 1
+
+#include <util/memory_segment_local.h>
+
+#include <cstddef>              // for size_t
+#include <new>                  // for bad_alloc
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+namespace test {
+
+// A special memory segment that can be used for tests.  It normally behaves
+// like a "local" memory segment.  If "throw count" is set to non 0 via
+// setThrowCount(), it continues the normal behavior until the specified
+// number of calls to allocate(), exclusive, and throws an exception at the
+// next call.  For example, if count is set to 3, the next two calls to
+// allocate() will succeed, and the 3rd call will fail with an exception.
+// This segment object can be used after the exception is thrown, and the
+// count is internally reset to 0.
+class MemorySegmentTest : public isc::util::MemorySegmentLocal {
+public:
+    MemorySegmentTest() : throw_count_(0) {}
+    virtual void* allocate(std::size_t size) {
+        if (throw_count_ > 0) {
+            if (--throw_count_ == 0) {
+                throw std::bad_alloc();
+            }
+        }
+        return (isc::util::MemorySegmentLocal::allocate(size));
+    }
+    void setThrowCount(std::size_t count) { throw_count_ = count; }
+
+private:
+    std::size_t throw_count_;
+};
+
+} // namespace test
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_SEGMENT_TEST_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/memory/tests/rdata_encoder_unittest.cc b/src/lib/datasrc/memory/tests/rdata_encoder_unittest.cc
deleted file mode 100644
index 2854c37..0000000
--- a/src/lib/datasrc/memory/tests/rdata_encoder_unittest.cc
+++ /dev/null
@@ -1,481 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <exceptions/exceptions.h>
-
-#include <util/buffer.h>
-
-#include <dns/name.h>
-#include <dns/labelsequence.h>
-#include <dns/messagerenderer.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-
-#include <datasrc/memory/rdata_encoder.h>
-
-#include <util/unittests/wiredata.h>
-
-#include <gtest/gtest.h>
-
-#include <boost/bind.hpp>
-#include <boost/foreach.hpp>
-
-#include <cstring>
-#include <set>
-#include <string>
-#include <vector>
-
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc::memory;
-using namespace isc::datasrc::memory::testing;
-
-using isc::util::unittests::matchWireData;
-using std::string;
-using std::vector;
-
-namespace {
-// This defines a tuple of test data used in test_rdata_list below.
-struct TestRdata {
-    const char* const rrclass;  // RR class, textual form
-    const char* const rrtype;   // RR type, textual form
-    const char* const rdata;    // textual RDATA
-    const size_t n_varlen_fields; // expected # of variable-len fields
-};
-
-// This test data consist of (almost) all supported types of RDATA (+ some
-// unusual and corner cases).
-const TestRdata test_rdata_list[] = {
-    {"IN", "A", "192.0.2.1", 0},
-    {"IN", "NS", "ns.example.com", 0},
-    {"IN", "CNAME", "cname.example.com", 0},
-    {"IN", "SOA", "ns.example.com root.example.com 0 0 0 0 0", 0},
-    {"IN", "PTR", "reverse.example.com", 0},
-    {"IN", "HINFO", "\"cpu-info\" \"OS-info\"", 1},
-    {"IN", "MINFO", "root.example.com mbox.example.com", 0},
-    {"IN", "MX", "10 mx.example.com", 0},
-    {"IN", "TXT", "\"test1\" \"test 2\"", 1},
-    {"IN", "RP", "root.example.com. rp-text.example.com", 0},
-    {"IN", "AFSDB", "1 afsdb.example.com", 0},
-    {"IN", "AAAA", "2001:db8::1", 0},
-    {"IN", "SRV", "1 0 10 target.example.com", 0},
-    {"IN", "NAPTR", "100 50 \"s\" \"http\" \"\" _http._tcp.example.com", 1},
-    {"IN", "DNAME", "dname.example.com", 0},
-    {"IN", "DS", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
-    {"IN", "SSHFP", "1 1 dd465c09cfa51fb45020cc83316fff", 1},
-    // We handle RRSIG separately, so it's excluded from the list
-    {"IN", "NSEC", "next.example.com. A AAAA NSEC RRSIG", 1},
-    {"IN", "DNSKEY", "256 3 5 FAKEFAKE", 1},
-    {"IN", "DHCID", "FAKEFAKE", 1},
-    {"IN", "NSEC3", "1 1 12 AABBCCDD FAKEFAKE A RRSIG", 1},
-    {"IN", "NSEC3PARAM", "1 0 12 AABBCCDD", 1},
-    {"IN", "SPF", "v=spf1 +mx a:colo.example.com/28 -all", 1},
-    {"IN", "DLV", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
-    {"IN", "TYPE65000", "\\# 3 010203", 1}, // some "custom" type
-    {"IN", "TYPE65535", "\\# 0", 1},        // max RR type, 0-length RDATA
-    {"CH", "A", "\\# 2 0102", 1}, // A RR for non-IN class; varlen data
-    {"CH", "NS", "ns.example.com", 0}, // class CH, generic data
-    {"CH", "TXT", "BIND10", 1},        // ditto
-    {"HS", "A", "\\# 5 0102030405", 1}, // A RR for non-IN class; varlen data
-    {NULL, NULL, NULL, 0}
-};
-
-// The following two functions will be used to generate wire format data
-// from encoded representation of each RDATA.
-void
-renderNameField(MessageRenderer* renderer, bool additional_required,
-                const LabelSequence& labels, RdataNameAttributes attributes)
-{
-    EXPECT_EQ(additional_required,
-              (attributes & NAMEATTR_ADDITIONAL) != 0);
-    renderer->writeName(labels, (attributes & NAMEATTR_COMPRESSIBLE) != 0);
-}
-
-void
-renderDataField(MessageRenderer* renderer, const uint8_t* data,
-                size_t data_len)
-{
-    renderer->writeData(data, data_len);
-}
-
-class RdataEncoderTest : public ::testing::Test {
-protected:
-    RdataEncoderTest() : a_rdata_(createRdata(RRType::A(), RRClass::IN(),
-                                              "192.0.2.53")),
-                         aaaa_rdata_(createRdata(RRType::AAAA(), RRClass::IN(),
-                                                 "2001:db8::53")),
-                         rrsig_rdata_(createRdata(
-                                          RRType::RRSIG(), RRClass::IN(),
-                                          "A 5 2 3600 20120814220826 "
-                                          "20120715220826 12345 com. FAKE"))
-    {}
-
-    // This helper test method constructs encodes the given list of RDATAs
-    // (in rdata_list), and then iterates over the data, rendering the fields
-    // in the wire format.  It then compares the wire data with the one
-    // generated by the normal libdns++ interface to see the encoding/decoding
-    // works as intended.
-    void checkEncode(RRClass rrclass, RRType rrtype,
-                     const vector<ConstRdataPtr>& rdata_list,
-                     size_t expected_varlen_fields,
-                     const vector<ConstRdataPtr>& rrsig_list);
-    // A wraper for RdataEncoder::encode() with buffer overrun check.
-    void encodeWrapper(size_t data_len);
-
-    void addRdataCommon(const vector<ConstRdataPtr>& rrsigs);
-    void addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs);
-
-    // Some commonly used RDATA
-    const ConstRdataPtr a_rdata_;
-    const ConstRdataPtr aaaa_rdata_;
-    const ConstRdataPtr rrsig_rdata_;
-
-    RdataEncoder encoder_;
-    vector<uint8_t> encoded_data_;
-    MessageRenderer expected_renderer_;
-    MessageRenderer actual_renderer_;
-    vector<ConstRdataPtr> rdata_list_;
-};
-
-
-void
-RdataEncoderTest::encodeWrapper(size_t data_len) {
-    // make sure the data buffer is large enough for the canary
-    encoded_data_.resize(data_len + 2);
-    // set the canary data
-    encoded_data_.at(data_len) = 0xde;
-    encoded_data_.at(data_len + 1) = 0xad;
-    // encode, then check the canary is intact
-    encoder_.encode(&encoded_data_[0], data_len);
-    EXPECT_EQ(0xde, encoded_data_.at(data_len));
-    EXPECT_EQ(0xad, encoded_data_.at(data_len + 1));
-    // shrink the data buffer to the originally expected size (some tests
-    // expect that).  the actual encoded data should be intact.
-    encoded_data_.resize(data_len);
-}
-
-void
-RdataEncoderTest::checkEncode(RRClass rrclass, RRType rrtype,
-                              const vector<ConstRdataPtr>& rdata_list,
-                              size_t expected_varlen_fields,
-                              const vector<ConstRdataPtr>& rrsig_list =
-                              vector<ConstRdataPtr>())
-{
-    // These two names will be rendered before and after the test RDATA,
-    // to check in case the RDATA contain a domain name whether it's
-    // compressed or not correctly.  The names in the RDATA should basically
-    // a subdomain of example.com, so it can be compressed due to dummy_name.
-    // Likewise, dummy_name2 should be able to be fully compressed due to
-    // the name in the RDATA.
-    const Name dummy_name("com");
-    const Name dummy_name2("example.com");
-
-    // The set of RR types that require additional section processing.
-    // We'll pass it to renderNameField to check the stored attribute matches
-    // our expectation.
-    std::set<RRType> need_additionals;
-    need_additionals.insert(RRType::NS());
-    need_additionals.insert(RRType::MX());
-    need_additionals.insert(RRType::SRV());
-    expected_renderer_.clear();
-    actual_renderer_.clear();
-    encoded_data_.clear();
-
-    const bool additional_required =
-        (need_additionals.find(rrtype) != need_additionals.end());
-
-    // Build expected wire-format data
-    expected_renderer_.writeName(dummy_name);
-    BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
-        rdata->toWire(expected_renderer_);
-    }
-    expected_renderer_.writeName(dummy_name2);
-    BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
-        rdata->toWire(expected_renderer_);
-    }
-
-    // Then build wire format data using the encoded data.
-    // 1st dummy name
-    actual_renderer_.writeName(dummy_name);
-
-    // Create encoded data
-    encoder_.start(rrclass, rrtype);
-    BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
-        encoder_.addRdata(*rdata);
-    }
-    BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
-        encoder_.addSIGRdata(*rdata);
-    }
-    encodeWrapper(encoder_.getStorageLength());
-
-    // If this type of RDATA is expected to contain variable-length fields,
-    // we brute force the encoded data, exploiting our knowledge of actual
-    // encoding, then adjust the encoded data excluding the list of length
-    // fields.  This is ugly, but we should be able to eliminate this hack
-    // at #2096.
-    vector<uint16_t> varlen_list;
-    if (expected_varlen_fields > 0) {
-        const size_t varlen_list_size =
-            rdata_list.size() * expected_varlen_fields * sizeof(uint16_t);
-        ASSERT_LE(varlen_list_size, encoded_data_.size());
-        varlen_list.resize(rdata_list.size() * expected_varlen_fields);
-        std::memcpy(&varlen_list[0], &encoded_data_[0], varlen_list_size);
-        encoded_data_.assign(encoded_data_.begin() + varlen_list_size,
-                             encoded_data_.end());
-    }
-
-    // If RRSIGs are given, we need to extract the list of the RRSIG lengths
-    // and adjust encoded_data_ further (this will be unnecessary at #2096,
-    // too).
-    vector<uint16_t> rrsiglen_list;
-    if (rrsig_list.size() > 0) {
-        const size_t rrsig_len_size = rrsig_list.size() * sizeof(uint16_t);
-        ASSERT_LE(rrsig_len_size, encoded_data_.size());
-        rrsiglen_list.resize(rrsig_list.size() * rrsig_len_size);
-        std::memcpy(&rrsiglen_list[0], &encoded_data_[0], rrsig_len_size);
-        encoded_data_.assign(encoded_data_.begin() + rrsig_len_size,
-                             encoded_data_.end());
-    }
-
-    // Create wire-format data from the encoded data
-    foreachRdataField(rrclass, rrtype, rdata_list.size(), encoded_data_,
-                      varlen_list,
-                      boost::bind(renderNameField, &actual_renderer_,
-                                  additional_required, _1, _2),
-                      boost::bind(renderDataField, &actual_renderer_, _1, _2));
-    // 2nd dummy name
-    actual_renderer_.writeName(dummy_name2);
-    // Finally, dump any RRSIGs in wire format.
-    foreachRRSig(encoded_data_, rrsiglen_list,
-                 boost::bind(renderDataField, &actual_renderer_, _1, _2));
-
-    // Two sets of wire-format data should be identical.
-    matchWireData(expected_renderer_.getData(), expected_renderer_.getLength(),
-                  actual_renderer_.getData(), actual_renderer_.getLength());
-}
-
-void
-RdataEncoderTest::addRdataCommon(const vector<ConstRdataPtr>& rrsigs) {
-    // Basic check on the encoded data for (most of) all supported RR types,
-    // in a comprehensive manner.
-    for (size_t i = 0; test_rdata_list[i].rrclass != NULL; ++i) {
-        SCOPED_TRACE(string(test_rdata_list[i].rrclass) + "/" +
-                     test_rdata_list[i].rrtype);
-        const RRClass rrclass(test_rdata_list[i].rrclass);
-        const RRType rrtype(test_rdata_list[i].rrtype);
-        const ConstRdataPtr rdata = createRdata(rrtype, rrclass,
-                                                test_rdata_list[i].rdata);
-        rdata_list_.clear();
-        rdata_list_.push_back(rdata);
-        checkEncode(rrclass, rrtype, rdata_list_,
-                    test_rdata_list[i].n_varlen_fields, rrsigs);
-    }
-}
-
-TEST_F(RdataEncoderTest, addRdata) {
-    vector<ConstRdataPtr> rrsigs;
-    addRdataCommon(rrsigs);     // basic tests without RRSIGs (empty vector)
-
-    // Test with RRSIGs (covered type doesn't always match, but the encoder
-    // doesn't check that)
-    rrsigs.push_back(rrsig_rdata_);
-    addRdataCommon(rrsigs);
-}
-
-void
-RdataEncoderTest::addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs) {
-    // Similar to addRdata(), but test with multiple RDATAs.
-    // Four different cases are tested: a single fixed-len RDATA (A),
-    // fixed-len data + domain name (MX), variable-len data only (TXT),
-    // variable-len data + domain name (NAPTR).
-    ConstRdataPtr a_rdata2 = createRdata(RRType::A(), RRClass::IN(),
-                                         "192.0.2.54");
-    rdata_list_.clear();
-    rdata_list_.push_back(a_rdata_);
-    rdata_list_.push_back(a_rdata2);
-    checkEncode(RRClass::IN(), RRType::A(), rdata_list_, 0, rrsigs);
-
-    ConstRdataPtr mx_rdata1 = createRdata(RRType::MX(), RRClass::IN(),
-                                          "5 mx1.example.com");
-    ConstRdataPtr mx_rdata2 = createRdata(RRType::MX(), RRClass::IN(),
-                                          "10 mx2.example.com");
-    rdata_list_.clear();
-    rdata_list_.push_back(mx_rdata1);
-    rdata_list_.push_back(mx_rdata2);
-    checkEncode(RRClass::IN(), RRType::MX(), rdata_list_, 0, rrsigs);
-
-    ConstRdataPtr txt_rdata1 = createRdata(RRType::TXT(), RRClass::IN(),
-                                           "foo bar baz");
-    ConstRdataPtr txt_rdata2 = createRdata(RRType::TXT(), RRClass::IN(),
-                                          "another text data");
-    rdata_list_.clear();
-    rdata_list_.push_back(txt_rdata1);
-    rdata_list_.push_back(txt_rdata2);
-    checkEncode(RRClass::IN(), RRType::TXT(), rdata_list_, 1, rrsigs);
-
-    ConstRdataPtr naptr_rdata1 =
-        createRdata(RRType::NAPTR(), RRClass::IN(),
-                    "100 50 \"s\" \"http\" \"\" _http._tcp.example.com");
-    ConstRdataPtr naptr_rdata2 =
-        createRdata(RRType::NAPTR(), RRClass::IN(),
-                    "200 100 \"s\" \"http\" \"\" _http._tcp.example.com");
-    rdata_list_.clear();
-    rdata_list_.push_back(naptr_rdata1);
-    rdata_list_.push_back(naptr_rdata2);
-    checkEncode(RRClass::IN(), RRType::NAPTR(), rdata_list_, 1, rrsigs);
-}
-
-TEST_F(RdataEncoderTest, encodeLargeRdata) {
-    // There should be no reason for a large RDATA to fail in encoding,
-    // but we check such a case explicitly.
-
-    encoded_data_.resize(65535); // max unsigned 16-bit int
-    isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
-    const in::DHCID large_dhcid(buffer, encoded_data_.size());
-
-    encoder_.start(RRClass::IN(), RRType::DHCID());
-    encoder_.addRdata(large_dhcid);
-    encodeWrapper(encoder_.getStorageLength());
-
-    // The encoded data should be identical to the original one.
-    ASSERT_LT(sizeof(uint16_t), encoder_.getStorageLength());
-    isc::util::InputBuffer ib(&encoded_data_[2], encoded_data_.size() - 2);
-    const in::DHCID encoded_dhcid(ib, ib.getLength());
-    EXPECT_EQ(0, encoded_dhcid.compare(large_dhcid));
-}
-
-TEST_F(RdataEncoderTest, addRdataMulti) {
-    vector<ConstRdataPtr> rrsigs;
-    addRdataMultiCommon(rrsigs); // test without RRSIGs (empty vector)
-
-    // Tests with two RRSIGs
-    rrsigs.push_back(rrsig_rdata_);
-    rrsigs.push_back(createRdata(RRType::RRSIG(), RRClass::IN(),
-                                 "A 5 2 3600 20120814220826 "
-                                 "20120715220826 54321 com. FAKE"));
-    addRdataMultiCommon(rrsigs);
-}
-
-TEST_F(RdataEncoderTest, badAddRdata) {
-    // Some operations must follow start().
-    EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::InvalidOperation);
-    EXPECT_THROW(encoder_.getStorageLength(), isc::InvalidOperation);
-    // will allocate space of some arbitrary size (256 bytes)
-    EXPECT_THROW(encodeWrapper(256), isc::InvalidOperation);
-
-    // Bad buffer for encode
-    encoder_.start(RRClass::IN(), RRType::A());
-    encoder_.addRdata(*a_rdata_);
-    const size_t buf_len = encoder_.getStorageLength();
-    // NULL buffer for encode
-    EXPECT_THROW(encoder_.encode(NULL, buf_len), isc::BadValue);
-    // buffer length is too short (we don't use the wrraper because we don't
-    // like to tweak the length arg to encode()).
-    encoded_data_.resize(buf_len - 1);
-    EXPECT_THROW(encoder_.encode(&encoded_data_[0], buf_len - 1),
-                 isc::BadValue);
-
-    // Type of RDATA and the specified RR type don't match.  addRdata() should
-    // detect this inconsistency.
-    encoder_.start(RRClass::IN(), RRType::AAAA());
-    EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::BadValue);
-
-    // Likewise.
-    encoder_.start(RRClass::IN(), RRType::A());
-    EXPECT_THROW(encoder_.addRdata(*aaaa_rdata_), isc::BadValue);
-
-    // Likewise.  The encoder expects the first name completes the data, and
-    // throws on the second due as an unexpected name field.
-    const ConstRdataPtr rp_rdata =
-        createRdata(RRType::RP(), RRClass::IN(), "a.example. b.example");
-    encoder_.start(RRClass::IN(), RRType::NS());
-    EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
-
-    // Likewise.  The encoder considers the name data a variable length data
-    // field, and throws on the first name.
-    encoder_.start(RRClass::IN(), RRType::DHCID());
-    EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
-
-    // Likewise.  The text RDATA (2 bytes) will be treated as MX preference,
-    // and the encoder will still expect to see a domain name.
-    const ConstRdataPtr txt_rdata = createRdata(RRType::TXT(), RRClass::IN(),
-                                                "a");
-    encoder_.start(RRClass::IN(), RRType::MX());
-    EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
-
-    // Similar to the previous one, but in this case there's no data field
-    // in the spec.
-    encoder_.start(RRClass::IN(), RRType::NS());
-    EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
-
-    // Likewise.  Inconsistent name compression policy.
-    const ConstRdataPtr ns_rdata =
-        createRdata(RRType::NS(), RRClass::IN(), "ns.example");
-    encoder_.start(RRClass::IN(), RRType::DNAME());
-    EXPECT_THROW(encoder_.addRdata(*ns_rdata), isc::BadValue);
-
-    // Same as the previous one, opposite inconsistency.
-    const ConstRdataPtr dname_rdata =
-        createRdata(RRType::DNAME(), RRClass::IN(), "dname.example");
-    encoder_.start(RRClass::IN(), RRType::NS());
-    EXPECT_THROW(encoder_.addRdata(*dname_rdata), isc::BadValue);
-
-    // RDATA len exceeds the 16-bit range.  Technically not invalid, but
-    // we don't support that (and it's practically useless anyway).
-    encoded_data_.resize(65536); // use encoded_data_ for placeholder
-    isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
-    encoder_.start(RRClass::IN(), RRType::DHCID());
-    EXPECT_THROW(encoder_.addRdata(in::DHCID(buffer, encoded_data_.size())),
-                                   RdataEncodingError);
-
-    // RRSIG cannot be used as the main RDATA type (can only be added as
-    // a signature for some other type of RDATAs).
-    EXPECT_THROW(encoder_.start(RRClass::IN(), RRType::RRSIG()),
-                 isc::BadValue);
-}
-
-TEST_F(RdataEncoderTest, addSIGRdataOnly) {
-    // Encoded data that only contain RRSIGs.  Mostly useless, but can happen
-    // (in a partially broken zone) and it's accepted.
-    encoder_.start(RRClass::IN(), RRType::A());
-    encoder_.addSIGRdata(*rrsig_rdata_);
-    encodeWrapper(encoder_.getStorageLength());
-    ASSERT_LT(sizeof(uint16_t), encoder_.getStorageLength());
-
-    // The encoded data should be identical to the given one.
-    isc::util::InputBuffer ib(&encoded_data_[2], encoded_data_.size() - 2);
-    const generic::RRSIG encoded_sig(ib, ib.getLength());
-    EXPECT_EQ(0, encoded_sig.compare(*rrsig_rdata_));
-}
-
-TEST_F(RdataEncoderTest, badAddSIGRdata) {
-    // try adding SIG before start
-    EXPECT_THROW(encoder_.addSIGRdata(*rrsig_rdata_), isc::InvalidOperation);
-
-    // Very big RRSIG.  This implementation rejects it.
-    isc::util::OutputBuffer ob(0);
-    rrsig_rdata_->toWire(ob);
-    // append dummy trailing signature to make it too big
-    vector<uint8_t> dummy_sig(65536 - ob.getLength());
-    ob.writeData(&dummy_sig[0], dummy_sig.size());
-    ASSERT_EQ(65536, ob.getLength());
-
-    isc::util::InputBuffer ib(ob.getData(), ob.getLength());
-    const generic::RRSIG big_sigrdata(ib, ob.getLength());
-    encoder_.start(RRClass::IN(), RRType::A());
-    EXPECT_THROW(encoder_.addSIGRdata(big_sigrdata), RdataEncodingError);
-}
-}
diff --git a/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc b/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc
new file mode 100644
index 0000000..12b613a
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc
@@ -0,0 +1,839 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+
+#include <dns/name.h>
+#include <dns/labelsequence.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <datasrc/memory/rdata_serialization.h>
+
+#include <util/unittests/wiredata.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+
+#include <cstring>
+#include <set>
+#include <string>
+#include <vector>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+
+using isc::util::unittests::matchWireData;
+using std::string;
+using std::vector;
+
+// A trick to steal some private definitions of the implementation we use here
+
+namespace isc {
+namespace datasrc{
+namespace memory {
+
+#include "../rdata_serialization_priv.cc"
+
+}
+}
+}
+
+namespace {
+// This defines a tuple of test data used in test_rdata_list below.
+struct TestRdata {
+    const char* const rrclass;  // RR class, textual form
+    const char* const rrtype;   // RR type, textual form
+    const char* const rdata;    // textual RDATA
+    const size_t n_varlen_fields; // expected # of variable-len fields
+};
+
+// This test data consist of (almost) all supported types of RDATA (+ some
+// unusual and corner cases).
+const TestRdata test_rdata_list[] = {
+    {"IN", "A", "192.0.2.1", 0},
+    {"IN", "NS", "ns.example.com", 0},
+    {"IN", "CNAME", "cname.example.com", 0},
+    {"IN", "SOA", "ns.example.com root.example.com 0 0 0 0 0", 0},
+    {"IN", "PTR", "reverse.example.com", 0},
+    {"IN", "HINFO", "\"cpu-info\" \"OS-info\"", 1},
+    {"IN", "MINFO", "root.example.com mbox.example.com", 0},
+    {"IN", "MX", "10 mx.example.com", 0},
+    {"IN", "TXT", "\"test1\" \"test 2\"", 1},
+    {"IN", "RP", "root.example.com. rp-text.example.com", 0},
+    {"IN", "AFSDB", "1 afsdb.example.com", 0},
+    {"IN", "AAAA", "2001:db8::1", 0},
+    {"IN", "SRV", "1 0 10 target.example.com", 0},
+    {"IN", "NAPTR", "100 50 \"s\" \"http\" \"\" _http._tcp.example.com", 1},
+    {"IN", "DNAME", "dname.example.com", 0},
+    {"IN", "DS", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
+    {"IN", "SSHFP", "1 1 dd465c09cfa51fb45020cc83316fff", 1},
+    // We handle RRSIG separately, so it's excluded from the list
+    {"IN", "NSEC", "next.example.com. A AAAA NSEC RRSIG", 1},
+    {"IN", "DNSKEY", "256 3 5 FAKEFAKE", 1},
+    {"IN", "DHCID", "FAKEFAKE", 1},
+    {"IN", "NSEC3", "1 1 12 AABBCCDD FAKEFAKE A RRSIG", 1},
+    {"IN", "NSEC3PARAM", "1 0 12 AABBCCDD", 1},
+    {"IN", "SPF", "v=spf1 +mx a:colo.example.com/28 -all", 1},
+    {"IN", "DLV", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
+    {"IN", "TYPE65000", "\\# 3 010203", 1}, // some "custom" type
+    {"IN", "TYPE65535", "\\# 0", 1},        // max RR type, 0-length RDATA
+    {"CH", "A", "\\# 2 0102", 1}, // A RR for non-IN class; varlen data
+    {"CH", "NS", "ns.example.com", 0}, // class CH, generic data
+    {"CH", "TXT", "BIND10", 1},        // ditto
+    {"HS", "A", "\\# 5 0102030405", 1}, // A RR for non-IN class; varlen data
+    {NULL, NULL, NULL, 0}
+};
+
+// The following two functions will be used to generate wire format data
+// from encoded representation of each RDATA.
+void
+renderNameField(MessageRenderer* renderer, bool additional_required,
+                const LabelSequence& labels, RdataNameAttributes attributes)
+{
+    EXPECT_EQ(additional_required, (attributes & NAMEATTR_ADDITIONAL) != 0);
+    renderer->writeName(labels, (attributes & NAMEATTR_COMPRESSIBLE) != 0);
+}
+
+void
+renderDataField(MessageRenderer* renderer, const void* data, size_t data_len) {
+    renderer->writeData(data, data_len);
+}
+
+class RdataSerializationTest : public ::testing::Test {
+protected:
+    RdataSerializationTest() : a_rdata_(createRdata(RRType::A(), RRClass::IN(),
+                                                    "192.0.2.53")),
+                         aaaa_rdata_(createRdata(RRType::AAAA(), RRClass::IN(),
+                                                 "2001:db8::53")),
+                         rrsig_rdata_(createRdata(
+                                          RRType::RRSIG(), RRClass::IN(),
+                                          "A 5 2 3600 20120814220826 "
+                                          "20120715220826 12345 com. FAKE"))
+    {}
+
+    // A wraper for RdataEncoder::encode() with buffer overrun check.
+    void encodeWrapper(size_t data_len);
+
+    // Some commonly used RDATA
+    const ConstRdataPtr a_rdata_;
+    const ConstRdataPtr aaaa_rdata_;
+    const ConstRdataPtr rrsig_rdata_;
+
+    RdataEncoder encoder_;
+    vector<uint8_t> encoded_data_;
+    MessageRenderer expected_renderer_;
+    MessageRenderer actual_renderer_;
+    vector<ConstRdataPtr> rdata_list_;
+};
+
+// There are several ways to decode the data. For one, there are
+// more interfaces uses for RdataReader, and we use our own decoder,
+// to check the actual encoded data.
+//
+// These decoding ways are provided by the template parameter.
+template<class DecoderStyle>
+class RdataEncodeDecodeTest : public RdataSerializationTest {
+public:
+    // This helper test method encodes the given list of RDATAs
+    // (in rdata_list), and then iterates over the data, rendering the fields
+    // in the wire format.  It then compares the wire data with the one
+    // generated by the normal libdns++ interface to see the encoding/decoding
+    // works as intended.
+    void checkEncode(RRClass rrclass, RRType rrtype,
+                     const vector<ConstRdataPtr>& rdata_list,
+                     size_t expected_varlen_fields,
+                     const vector<ConstRdataPtr>& rrsig_list =
+                     vector<ConstRdataPtr>());
+
+    void addRdataCommon(const vector<ConstRdataPtr>& rrsigs);
+    void addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs);
+};
+
+// Used across more classes and scopes. But it's just uninteresting
+// constant.
+const Name& dummyName2() {
+    static const Name result("example.com");
+    return (result);
+}
+
+bool
+additionalRequired(const RRType& type) {
+    // The set of RR types that require additional section processing.
+    // We'll use it to determine what value should the renderNameField get
+    // and, if the stored attributes are as expected.
+    static std::set<RRType> need_additionals;
+    if (need_additionals.empty()) {
+        need_additionals.insert(RRType::NS());
+        need_additionals.insert(RRType::MX());
+        need_additionals.insert(RRType::SRV());
+    }
+
+    return (need_additionals.find(type) != need_additionals.end());
+}
+
+// A decoder that does not use RdataReader. Not recommended for use,
+// but it allows the tests to check the internals of the data.
+class ManualDecoderStyle {
+public:
+    static void foreachRdataField(RRClass rrclass, RRType rrtype,
+                                  size_t rdata_count,
+                                  const vector<uint8_t>& encoded_data,
+                                  const vector<uint16_t>& varlen_list,
+                                  RdataReader::NameAction name_callback,
+                                  RdataReader::DataAction data_callback)
+    {
+        const RdataEncodeSpec& encode_spec = getRdataEncodeSpec(rrclass,
+                                                                rrtype);
+
+        size_t off = 0;
+        size_t varlen_count = 0;
+        size_t name_count = 0;
+        for (size_t count = 0; count < rdata_count; ++count) {
+            for (size_t i = 0; i < encode_spec.field_count; ++i) {
+                const RdataFieldSpec& field_spec = encode_spec.fields[i];
+                switch (field_spec.type) {
+                    case RdataFieldSpec::FIXEDLEN_DATA:
+                        if (data_callback) {
+                            data_callback(&encoded_data.at(off),
+                                          field_spec.fixeddata_len);
+                        }
+                        off += field_spec.fixeddata_len;
+                        break;
+                    case RdataFieldSpec::VARLEN_DATA:
+                        {
+                            const size_t varlen = varlen_list.at(varlen_count);
+                            if (data_callback && varlen > 0) {
+                                data_callback(&encoded_data.at(off), varlen);
+                            }
+                            off += varlen;
+                            ++varlen_count;
+                            break;
+                        }
+                    case RdataFieldSpec::DOMAIN_NAME:
+                        {
+                            ++name_count;
+                            const LabelSequence labels(&encoded_data.at(off));
+                            if (name_callback) {
+                                name_callback(labels,
+                                              field_spec.name_attributes);
+                            }
+                            off += labels.getSerializedLength();
+                            break;
+                        }
+                }
+            }
+        }
+        assert(name_count == encode_spec.name_count * rdata_count);
+        assert(varlen_count == encode_spec.varlen_count * rdata_count);
+    }
+
+    static void foreachRRSig(const vector<uint8_t>& encoded_data,
+                             const vector<uint16_t>& rrsiglen_list,
+                             RdataReader::DataAction data_callback)
+    {
+        size_t rrsig_totallen = 0;
+        for (vector<uint16_t>::const_iterator it = rrsiglen_list.begin();
+             it != rrsiglen_list.end();
+             ++it) {
+            rrsig_totallen += *it;
+        }
+        assert(encoded_data.size() >= rrsig_totallen);
+
+        const uint8_t* dp = &encoded_data[encoded_data.size() -
+            rrsig_totallen];
+        for (size_t i = 0; i < rrsiglen_list.size(); ++i) {
+            data_callback(dp, rrsiglen_list[i]);
+            dp += rrsiglen_list[i];
+        }
+    }
+
+    static void decode(const isc::dns::RRClass& rrclass,
+                       const isc::dns::RRType& rrtype,
+                       size_t rdata_count,
+                       size_t rrsig_count,
+                       size_t expected_varlen_fields,
+                       // Warning: this test actualy might change the
+                       // encoded_data !
+                       vector<uint8_t>& encoded_data, size_t,
+                       MessageRenderer& renderer)
+    {
+        // If this type of RDATA is expected to contain variable-length fields,
+        // we brute force the encoded data, exploiting our knowledge of actual
+        // encoding, then adjust the encoded data excluding the list of length
+        // fields.  This is ugly, but for tests only.
+        vector<uint16_t> varlen_list;
+        if (expected_varlen_fields > 0) {
+            const size_t varlen_list_size =
+                rdata_count * expected_varlen_fields * sizeof(uint16_t);
+            ASSERT_LE(varlen_list_size, encoded_data.size());
+            varlen_list.resize(rdata_count * expected_varlen_fields);
+            std::memcpy(&varlen_list[0], &encoded_data[0], varlen_list_size);
+            encoded_data.assign(encoded_data.begin() + varlen_list_size,
+                                encoded_data.end());
+        }
+
+        // If RRSIGs are given, we need to extract the list of the RRSIG
+        // lengths and adjust encoded_data_ further.
+        vector<uint16_t> rrsiglen_list;
+        if (rrsig_count > 0) {
+            const size_t rrsig_len_size = rrsig_count * sizeof(uint16_t);
+            ASSERT_LE(rrsig_len_size, encoded_data.size());
+            rrsiglen_list.resize(rrsig_count * rrsig_len_size);
+            std::memcpy(&rrsiglen_list[0], &encoded_data[0], rrsig_len_size);
+            encoded_data.assign(encoded_data.begin() + rrsig_len_size,
+                                encoded_data.end());
+        }
+
+        // Create wire-format data from the encoded data
+        foreachRdataField(rrclass, rrtype, rdata_count, encoded_data,
+                          varlen_list,
+                          boost::bind(renderNameField, &renderer,
+                                      additionalRequired(rrtype), _1, _2),
+                          boost::bind(renderDataField, &renderer, _1, _2));
+
+        // 2nd dummy name
+        renderer.writeName(dummyName2());
+        // Finally, dump any RRSIGs in wire format.
+        foreachRRSig(encoded_data, rrsiglen_list,
+                     boost::bind(renderDataField, &renderer, _1, _2));
+    }
+};
+
+// Check using callbacks and calling next until the end.
+class CallbackDecoder {
+public:
+    static void decode(const isc::dns::RRClass& rrclass,
+                       const isc::dns::RRType& rrtype,
+                       size_t rdata_count, size_t sig_count, size_t,
+                       const vector<uint8_t>& encoded_data, size_t,
+                       MessageRenderer& renderer)
+    {
+        RdataReader reader(rrclass, rrtype, &encoded_data[0], rdata_count,
+                           sig_count,
+                           boost::bind(renderNameField, &renderer,
+                                       additionalRequired(rrtype), _1, _2),
+                           boost::bind(renderDataField, &renderer, _1, _2));
+        while (reader.next() != RdataReader::RRSET_BOUNDARY) {}
+        renderer.writeName(dummyName2());
+        while (reader.nextSig() != RdataReader::RRSET_BOUNDARY) {}
+    }
+};
+
+// Check using callbacks and calling iterate.
+class IterateDecoder {
+public:
+    static void decode(const isc::dns::RRClass& rrclass,
+                       const isc::dns::RRType& rrtype,
+                       size_t rdata_count, size_t sig_count, size_t,
+                       const vector<uint8_t>& encoded_data, size_t,
+                       MessageRenderer& renderer)
+    {
+        RdataReader reader(rrclass, rrtype, &encoded_data[0],
+                           rdata_count, sig_count,
+                           boost::bind(renderNameField, &renderer,
+                                       additionalRequired(rrtype), _1, _2),
+                           boost::bind(renderDataField, &renderer, _1, _2));
+        reader.iterate();
+        renderer.writeName(dummyName2());
+        reader.iterateAllSigs();
+    }
+};
+
+namespace {
+
+// Render the data to renderer, if one is set, or put it inside
+// a data buffer.
+void
+appendOrRenderData(vector<uint8_t>* where, MessageRenderer** renderer,
+                   const void* data, size_t size)
+{
+    if (*renderer != NULL) {
+        (*renderer)->writeData(data, size);
+    } else {
+        where->insert(where->end(), reinterpret_cast<const uint8_t*>(data),
+                      reinterpret_cast<const uint8_t*>(data) + size);
+    }
+}
+
+}
+
+// Similar to IterateDecoder, but it first iterates a little and rewinds
+// before actual rendering.
+class RewindAndDecode {
+private:
+    static void writeName(MessageRenderer** renderer,
+                          const LabelSequence& labels,
+                          RdataNameAttributes attributes)
+    {
+        (*renderer)->writeName(labels,
+                               (attributes & NAMEATTR_COMPRESSIBLE) != 0);
+    }
+public:
+    static void decode(const isc::dns::RRClass& rrclass,
+                       const isc::dns::RRType& rrtype,
+                       size_t rdata_count, size_t sig_count, size_t,
+                       const vector<uint8_t>& encoded_data, size_t,
+                       MessageRenderer& renderer)
+    {
+        MessageRenderer dump; // A place to dump the extra data from before
+                              // actual rendering.
+        MessageRenderer* current = &dump;
+        vector<uint8_t> placeholder; // boost::bind does not like NULL
+        RdataReader reader(rrclass, rrtype, &encoded_data[0],
+                           rdata_count, sig_count,
+                           boost::bind(writeName, &current, _1, _2),
+                           boost::bind(appendOrRenderData, &placeholder,
+                                       &current, _1, _2));
+        // Iterate a little and rewind
+        reader.next();
+        reader.nextSig();
+        reader.rewind();
+        // Do the actual rendering
+        current = &renderer;
+        reader.iterate();
+        renderer.writeName(dummyName2());
+        reader.iterateAllSigs();
+    }
+};
+
+// Decode using the iteration over one rdata each time.
+// We also count there's the correct count of Rdatas.
+class SingleIterateDecoder {
+public:
+    static void decode(const isc::dns::RRClass& rrclass,
+                       const isc::dns::RRType& rrtype,
+                       size_t rdata_count, size_t sig_count, size_t,
+                       const vector<uint8_t>& encoded_data, size_t,
+                       MessageRenderer& renderer)
+    {
+        RdataReader reader(rrclass, rrtype, &encoded_data[0],
+                           rdata_count, sig_count,
+                           boost::bind(renderNameField, &renderer,
+                                       additionalRequired(rrtype), _1, _2),
+                           boost::bind(renderDataField, &renderer, _1, _2));
+        size_t actual_count = 0;
+        while (reader.iterateRdata()) {
+            ++actual_count;
+        }
+        EXPECT_EQ(rdata_count, actual_count);
+        actual_count = 0;
+        renderer.writeName(dummyName2());
+        while (reader.iterateSingleSig()) {
+            ++actual_count;
+        }
+        EXPECT_EQ(sig_count, actual_count);
+    }
+};
+
+// This one does not adhere to the usual way the reader is used, trying
+// to confuse it. It iterates part of the data manually and then reads
+// the rest through iterate. It also reads the signatures in the middle
+// of rendering.
+template<bool start_data, bool start_sig>
+class HybridDecoder {
+public:
+    static void decode(const isc::dns::RRClass& rrclass,
+                       const isc::dns::RRType& rrtype,
+                       size_t rdata_count, size_t sig_count, size_t,
+                       const vector<uint8_t>& encoded_data,
+                       size_t encoded_data_len,
+                       MessageRenderer& renderer)
+    {
+        vector<uint8_t> data;
+        MessageRenderer* current;
+        RdataReader reader(rrclass, rrtype, &encoded_data[0],
+                           rdata_count, sig_count,
+                           boost::bind(renderNameField, &renderer,
+                                       additionalRequired(rrtype), _1, _2),
+                           boost::bind(appendOrRenderData, &data, &current, _1,
+                                       _2));
+        // The size matches
+        EXPECT_EQ(encoded_data_len, reader.getSize());
+        if (start_sig) {
+            current = NULL;
+            reader.nextSig();
+        }
+        // Render first part of data. If there's none, return empty Result and
+        // do nothing.
+        if (start_data) {
+            current = &renderer;
+            reader.next();
+        }
+        // Now, we let all sigs to be copied to data. We disable the
+        // renderer for this.
+        current = NULL;
+        reader.iterateAllSigs();
+        // Now return the renderer and render the rest of the data
+        current = &renderer;
+        reader.iterate();
+        // Now, this should not break anything and should be valid, but should
+        // return ends.
+        EXPECT_EQ(RdataReader::RRSET_BOUNDARY, reader.next());
+        EXPECT_EQ(RdataReader::RRSET_BOUNDARY, reader.nextSig());
+        // Render the name and the sigs
+        renderer.writeName(dummyName2());
+        renderer.writeData(&data[0], data.size());
+        // The size matches even after use
+        EXPECT_EQ(encoded_data_len, reader.getSize());
+    }
+};
+
+typedef ::testing::Types<ManualDecoderStyle,
+                         CallbackDecoder, IterateDecoder, SingleIterateDecoder,
+                         HybridDecoder<true, true>, HybridDecoder<true, false>,
+                         HybridDecoder<false, true>,
+                         HybridDecoder<false, false> >
+    DecoderStyles;
+// Each decoder style must contain a decode() method. Such method is expected
+// to decode the passed data, first render the Rdata into the passed renderer,
+// then write the dummyName2() there and write the RRSig data after that.
+// It may do other checks too.
+//
+// There are some slight differences to how to do the decoding, that's why we
+// have the typed test.
+TYPED_TEST_CASE(RdataEncodeDecodeTest, DecoderStyles);
+
+void
+RdataSerializationTest::encodeWrapper(size_t data_len) {
+    // make sure the data buffer is large enough for the canary
+    encoded_data_.resize(data_len + 2);
+    // set the canary data
+    encoded_data_.at(data_len) = 0xde;
+    encoded_data_.at(data_len + 1) = 0xad;
+    // encode, then check the canary is intact
+    encoder_.encode(&encoded_data_[0], data_len);
+    EXPECT_EQ(0xde, encoded_data_.at(data_len));
+    EXPECT_EQ(0xad, encoded_data_.at(data_len + 1));
+    // shrink the data buffer to the originally expected size (some tests
+    // expect that).  the actual encoded data should be intact.
+    encoded_data_.resize(data_len);
+}
+
+template<class DecoderStyle>
+void
+RdataEncodeDecodeTest<DecoderStyle>::
+checkEncode(RRClass rrclass, RRType rrtype,
+            const vector<ConstRdataPtr>& rdata_list,
+            size_t expected_varlen_fields,
+            const vector<ConstRdataPtr>& rrsig_list)
+{
+    // These two names will be rendered before and after the test RDATA,
+    // to check in case the RDATA contain a domain name whether it's
+    // compressed or not correctly.  The names in the RDATA should basically
+    // a subdomain of example.com, so it can be compressed due to dummyName2().
+    // Likewise, dummyName2() should be able to be fully compressed due to
+    // the name in the RDATA.
+    const Name dummy_name("com");
+
+    expected_renderer_.clear();
+    actual_renderer_.clear();
+    encoded_data_.clear();
+
+    // Build expected wire-format data
+    expected_renderer_.writeName(dummy_name);
+    BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
+        rdata->toWire(expected_renderer_);
+    }
+    expected_renderer_.writeName(dummyName2());
+    BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
+        rdata->toWire(expected_renderer_);
+    }
+
+    // Then build wire format data using the encoded data.
+    // 1st dummy name
+    actual_renderer_.writeName(dummy_name);
+
+    // Create encoded data
+    encoder_.start(rrclass, rrtype);
+    BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
+        encoder_.addRdata(*rdata);
+    }
+    BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
+        encoder_.addSIGRdata(*rdata);
+    }
+    const size_t storage_len = encoder_.getStorageLength();
+    encodeWrapper(storage_len);
+
+    DecoderStyle::decode(rrclass, rrtype, rdata_list.size(), rrsig_list.size(),
+                         expected_varlen_fields, encoded_data_, storage_len,
+                         actual_renderer_);
+
+    // Two sets of wire-format data should be identical.
+    matchWireData(expected_renderer_.getData(), expected_renderer_.getLength(),
+                  actual_renderer_.getData(), actual_renderer_.getLength());
+}
+
+template<class DecoderStyle>
+void
+RdataEncodeDecodeTest<DecoderStyle>::
+addRdataCommon(const vector<ConstRdataPtr>& rrsigs) {
+    // Basic check on the encoded data for (most of) all supported RR types,
+    // in a comprehensive manner.
+    for (size_t i = 0; test_rdata_list[i].rrclass != NULL; ++i) {
+        SCOPED_TRACE(string(test_rdata_list[i].rrclass) + "/" +
+                     test_rdata_list[i].rrtype);
+        const RRClass rrclass(test_rdata_list[i].rrclass);
+        const RRType rrtype(test_rdata_list[i].rrtype);
+        const ConstRdataPtr rdata = createRdata(rrtype, rrclass,
+                                                test_rdata_list[i].rdata);
+        rdata_list_.clear();
+        rdata_list_.push_back(rdata);
+        checkEncode(rrclass, rrtype, rdata_list_,
+                    test_rdata_list[i].n_varlen_fields, rrsigs);
+    }
+}
+
+TYPED_TEST(RdataEncodeDecodeTest, addRdata) {
+    vector<ConstRdataPtr> rrsigs;
+    this->addRdataCommon(rrsigs); // basic tests without RRSIGs (empty vector)
+
+    // Test with RRSIGs (covered type doesn't always match, but the encoder
+    // doesn't check that)
+    rrsigs.push_back(this->rrsig_rdata_);
+    this->addRdataCommon(rrsigs);
+}
+
+template<class DecoderStyle>
+void
+RdataEncodeDecodeTest<DecoderStyle>::
+addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs) {
+    // Similar to addRdata(), but test with multiple RDATAs.
+    // Four different cases are tested: a single fixed-len RDATA (A),
+    // fixed-len data + domain name (MX), variable-len data only (TXT),
+    // variable-len data + domain name (NAPTR).
+    ConstRdataPtr a_rdata2 = createRdata(RRType::A(), RRClass::IN(),
+                                         "192.0.2.54");
+    rdata_list_.clear();
+    rdata_list_.push_back(a_rdata_);
+    rdata_list_.push_back(a_rdata2);
+    checkEncode(RRClass::IN(), RRType::A(), rdata_list_, 0, rrsigs);
+
+    ConstRdataPtr mx_rdata1 = createRdata(RRType::MX(), RRClass::IN(),
+                                          "5 mx1.example.com");
+    ConstRdataPtr mx_rdata2 = createRdata(RRType::MX(), RRClass::IN(),
+                                          "10 mx2.example.com");
+    rdata_list_.clear();
+    rdata_list_.push_back(mx_rdata1);
+    rdata_list_.push_back(mx_rdata2);
+    checkEncode(RRClass::IN(), RRType::MX(), rdata_list_, 0, rrsigs);
+
+    ConstRdataPtr txt_rdata1 = createRdata(RRType::TXT(), RRClass::IN(),
+                                           "foo bar baz");
+    ConstRdataPtr txt_rdata2 = createRdata(RRType::TXT(), RRClass::IN(),
+                                          "another text data");
+    rdata_list_.clear();
+    rdata_list_.push_back(txt_rdata1);
+    rdata_list_.push_back(txt_rdata2);
+    checkEncode(RRClass::IN(), RRType::TXT(), rdata_list_, 1, rrsigs);
+
+    ConstRdataPtr naptr_rdata1 =
+        createRdata(RRType::NAPTR(), RRClass::IN(),
+                    "100 50 \"s\" \"http\" \"\" _http._tcp.example.com");
+    ConstRdataPtr naptr_rdata2 =
+        createRdata(RRType::NAPTR(), RRClass::IN(),
+                    "200 100 \"s\" \"http\" \"\" _http._tcp.example.com");
+    rdata_list_.clear();
+    rdata_list_.push_back(naptr_rdata1);
+    rdata_list_.push_back(naptr_rdata2);
+    checkEncode(RRClass::IN(), RRType::NAPTR(), rdata_list_, 1, rrsigs);
+}
+
+void ignoreName(const LabelSequence&, unsigned) {
+}
+
+void
+checkLargeData(const in::DHCID* decoded, bool* called, const void* encoded,
+               size_t length)
+{
+    EXPECT_FALSE(*called); // Called exactly once
+    *called = true;
+
+    // Reconstruct the Rdata and check it.
+    isc::util::InputBuffer ib(encoded, length);
+    const in::DHCID reconstructed(ib, ib.getLength());
+    EXPECT_EQ(0, reconstructed.compare(*decoded));
+}
+
+TEST_F(RdataSerializationTest, encodeLargeRdata) {
+    // There should be no reason for a large RDATA to fail in encoding,
+    // but we check such a case explicitly.
+
+    encoded_data_.resize(65535); // max unsigned 16-bit int
+    isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
+    const in::DHCID large_dhcid(buffer, encoded_data_.size());
+
+    encoder_.start(RRClass::IN(), RRType::DHCID());
+    encoder_.addRdata(large_dhcid);
+    encodeWrapper(encoder_.getStorageLength());
+
+    // The encoded data should be identical to the original one.
+    bool called = false;
+    RdataReader reader(RRClass::IN(), RRType::DHCID(), &encoded_data_[0], 1, 0,
+                       ignoreName, boost::bind(checkLargeData, &large_dhcid,
+                                               &called, _1, _2));
+    reader.iterate();
+    EXPECT_TRUE(called);
+    called = false;
+    reader.iterateAllSigs();
+    EXPECT_FALSE(called);
+}
+
+TYPED_TEST(RdataEncodeDecodeTest, addRdataMulti) {
+    vector<ConstRdataPtr> rrsigs;
+    this->addRdataMultiCommon(rrsigs); // test without RRSIGs (empty vector)
+
+    // Tests with two RRSIGs
+    rrsigs.push_back(this->rrsig_rdata_);
+    rrsigs.push_back(createRdata(RRType::RRSIG(), RRClass::IN(),
+                                 "A 5 2 3600 20120814220826 "
+                                 "20120715220826 54321 com. FAKE"));
+    this->addRdataMultiCommon(rrsigs);
+}
+
+TEST_F(RdataSerializationTest, badAddRdata) {
+    // Some operations must follow start().
+    EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::InvalidOperation);
+    EXPECT_THROW(encoder_.getStorageLength(), isc::InvalidOperation);
+    // will allocate space of some arbitrary size (256 bytes)
+    EXPECT_THROW(encodeWrapper(256), isc::InvalidOperation);
+
+    // Bad buffer for encode
+    encoder_.start(RRClass::IN(), RRType::A());
+    encoder_.addRdata(*a_rdata_);
+    const size_t buf_len = encoder_.getStorageLength();
+    // NULL buffer for encode
+    EXPECT_THROW(encoder_.encode(NULL, buf_len), isc::BadValue);
+    // buffer length is too short (we don't use the wrraper because we don't
+    // like to tweak the length arg to encode()).
+    encoded_data_.resize(buf_len - 1);
+    EXPECT_THROW(encoder_.encode(&encoded_data_[0], buf_len - 1),
+                 isc::BadValue);
+
+    // Type of RDATA and the specified RR type don't match.  addRdata() should
+    // detect this inconsistency.
+    encoder_.start(RRClass::IN(), RRType::AAAA());
+    EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::BadValue);
+
+    // Likewise.
+    encoder_.start(RRClass::IN(), RRType::A());
+    EXPECT_THROW(encoder_.addRdata(*aaaa_rdata_), isc::BadValue);
+
+    // Likewise.  The encoder expects the first name completes the data, and
+    // throws on the second due as an unexpected name field.
+    const ConstRdataPtr rp_rdata =
+        createRdata(RRType::RP(), RRClass::IN(), "a.example. b.example");
+    encoder_.start(RRClass::IN(), RRType::NS());
+    EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
+
+    // Likewise.  The encoder considers the name data a variable length data
+    // field, and throws on the first name.
+    encoder_.start(RRClass::IN(), RRType::DHCID());
+    EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
+
+    // Likewise.  The text RDATA (2 bytes) will be treated as MX preference,
+    // and the encoder will still expect to see a domain name.
+    const ConstRdataPtr txt_rdata = createRdata(RRType::TXT(), RRClass::IN(),
+                                                "a");
+    encoder_.start(RRClass::IN(), RRType::MX());
+    EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
+
+    // Similar to the previous one, but in this case there's no data field
+    // in the spec.
+    encoder_.start(RRClass::IN(), RRType::NS());
+    EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
+
+    // Likewise.  Inconsistent name compression policy.
+    const ConstRdataPtr ns_rdata =
+        createRdata(RRType::NS(), RRClass::IN(), "ns.example");
+    encoder_.start(RRClass::IN(), RRType::DNAME());
+    EXPECT_THROW(encoder_.addRdata(*ns_rdata), isc::BadValue);
+
+    // Same as the previous one, opposite inconsistency.
+    const ConstRdataPtr dname_rdata =
+        createRdata(RRType::DNAME(), RRClass::IN(), "dname.example");
+    encoder_.start(RRClass::IN(), RRType::NS());
+    EXPECT_THROW(encoder_.addRdata(*dname_rdata), isc::BadValue);
+
+    // RDATA len exceeds the 16-bit range.  Technically not invalid, but
+    // we don't support that (and it's practically useless anyway).
+    encoded_data_.resize(65536); // use encoded_data_ for placeholder
+    isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
+    encoder_.start(RRClass::IN(), RRType::DHCID());
+    EXPECT_THROW(encoder_.addRdata(in::DHCID(buffer, encoded_data_.size())),
+                                   RdataEncodingError);
+
+    // RRSIG cannot be used as the main RDATA type (can only be added as
+    // a signature for some other type of RDATAs).
+    EXPECT_THROW(encoder_.start(RRClass::IN(), RRType::RRSIG()),
+                 isc::BadValue);
+}
+
+void
+checkSigData(const ConstRdataPtr& decoded, bool* called, const void* encoded,
+             size_t length)
+{
+    EXPECT_FALSE(*called); // Called exactly once
+    *called = true;
+
+    // Reconstruct the RRSig and check it.
+    isc::util::InputBuffer ib(encoded, length);
+    const generic::RRSIG reconstructed(ib, ib.getLength());
+    EXPECT_EQ(0, reconstructed.compare(*decoded));
+}
+
+TEST_F(RdataSerializationTest, addSIGRdataOnly) {
+    // Encoded data that only contain RRSIGs.  Mostly useless, but can happen
+    // (in a partially broken zone) and it's accepted.
+    encoder_.start(RRClass::IN(), RRType::A());
+    encoder_.addSIGRdata(*rrsig_rdata_);
+    encodeWrapper(encoder_.getStorageLength());
+    ASSERT_LT(sizeof(uint16_t), encoder_.getStorageLength());
+
+    bool called = false;
+    RdataReader reader(RRClass::IN(), RRType::A(), &encoded_data_[0], 0, 1,
+                       ignoreName, boost::bind(checkSigData, rrsig_rdata_,
+                                               &called, _1, _2));
+    reader.iterate();
+    EXPECT_FALSE(called);
+    reader.iterateAllSigs();
+    EXPECT_TRUE(called);
+}
+
+TEST_F(RdataSerializationTest, badAddSIGRdata) {
+    // try adding SIG before start
+    EXPECT_THROW(encoder_.addSIGRdata(*rrsig_rdata_), isc::InvalidOperation);
+
+    // Very big RRSIG.  This implementation rejects it.
+    isc::util::OutputBuffer ob(0);
+    rrsig_rdata_->toWire(ob);
+    // append dummy trailing signature to make it too big
+    vector<uint8_t> dummy_sig(65536 - ob.getLength());
+    ob.writeData(&dummy_sig[0], dummy_sig.size());
+    ASSERT_EQ(65536, ob.getLength());
+
+    isc::util::InputBuffer ib(ob.getData(), ob.getLength());
+    const generic::RRSIG big_sigrdata(ib, ob.getLength());
+    encoder_.start(RRClass::IN(), RRType::A());
+    EXPECT_THROW(encoder_.addSIGRdata(big_sigrdata), RdataEncodingError);
+}
+}
diff --git a/src/lib/datasrc/memory/tests/rdataset_unittest.cc b/src/lib/datasrc/memory/tests/rdataset_unittest.cc
new file mode 100644
index 0000000..897e53c
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/rdataset_unittest.cc
@@ -0,0 +1,298 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+#include <util/memory_segment_local.h>
+
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrset.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/rdataset.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <string>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+using namespace isc::testutils;
+using boost::lexical_cast;
+
+namespace {
+
+class RdataSetTest : public ::testing::Test {
+protected:
+    RdataSetTest() :
+        // 1076895760 = 0x40302010.  Use this so we fill in all 8-bit "field"
+        // of the 32-bit TTL
+        a_rrset_(textToRRset("www.example.com. 1076895760 IN A 192.0.2.1")),
+        rrsig_rrset_(textToRRset("www.example.com. 1076895760 IN RRSIG "
+                                 "A 5 2 3600 20120814220826 20120715220826 "
+                                 "1234 example.com. FAKE"))
+    {}
+    void TearDown() {
+        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+    }
+
+    ConstRRsetPtr a_rrset_, rrsig_rrset_;
+    isc::util::MemorySegmentLocal mem_sgmt_;
+    RdataEncoder encoder_;
+};
+
+// Convert the given 32-bit integer (network byte order) to the corresponding
+// RRTTL object.
+RRTTL
+restoreTTL(const void* ttl_data) {
+    isc::util::InputBuffer b(ttl_data, sizeof(uint32_t));
+    return (RRTTL(b));
+}
+
+// A helper callback for checkRdataSet.  This confirms the given data
+// is the expected in::A RDATA (the value is taken from the RdataSetTest
+// constructor).
+void
+checkData(const void* data, size_t size) {
+    isc::util::InputBuffer b(data, size);
+    EXPECT_EQ(0, in::A(b, size).compare(in::A("192.0.2.1")));
+}
+
+// This is a set of checks for an RdataSet created with some simple
+// conditions.  with_rrset/with_rrsig is true iff the RdataSet is supposed to
+// contain normal/RRSIG RDATA.
+void
+checkRdataSet(const RdataSet& rdataset, bool with_rrset, bool with_rrsig) {
+    EXPECT_FALSE(rdataset.next); // by default the next pointer should be NULL
+    EXPECT_EQ(RRType::A(), rdataset.type);
+    // See the RdataSetTest constructor for the magic number.
+    EXPECT_EQ(RRTTL(1076895760), restoreTTL(rdataset.getTTLData()));
+    EXPECT_EQ(with_rrset ? 1 : 0, rdataset.getRdataCount());
+    EXPECT_EQ(with_rrsig ? 1 : 0, rdataset.getSigRdataCount());
+
+    // A simple test for the data content.  Details tests for the encoder/
+    // reader should be basically sufficient for various cases of the data,
+    // and the fact that this test doesn't detect memory leak should be
+    // reasonably sufficient that the implementation handles the data region
+    // correctly.  Here we check one simple case for a simple form of RDATA,
+    // mainly for checking the behavior of getDataBuf().
+    RdataReader reader(RRClass::IN(), RRType::A(),
+                       reinterpret_cast<const uint8_t*>(
+                           rdataset.getDataBuf()),
+                       rdataset.getRdataCount(), rdataset.getSigRdataCount(),
+                       &RdataReader::emptyNameAction, checkData);
+    reader.iterate();
+}
+
+TEST_F(RdataSetTest, create) {
+    // A simple case of creating an RdataSet.  Confirming the resulting
+    // fields have the expected values, and then destroying it (TearDown()
+    // would detect any memory leak)
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                          ConstRRsetPtr());
+    checkRdataSet(*rdataset, true, false);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+TEST_F(RdataSetTest, getNext) {
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                          ConstRRsetPtr());
+
+    // By default, the next pointer should be NULL (already tested in other
+    // test cases), which should be the case with getNext().  We test both
+    // mutable and immutable versions of getNext().
+    EXPECT_EQ(static_cast<RdataSet*>(NULL), rdataset->getNext());
+    EXPECT_EQ(static_cast<const RdataSet*>(NULL),
+              static_cast<const RdataSet*>(rdataset)->getNext());
+
+    // making a link (it would form an infinite loop, but it doesn't matter
+    // in this test), and check the pointer returned by getNext().
+    rdataset->next = rdataset;
+    EXPECT_EQ(rdataset, static_cast<const RdataSet*>(rdataset)->getNext());
+
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+// A helper function to create an RRset containing the given number of
+// unique RDATAs.
+ConstRRsetPtr
+getRRsetWithRdataCount(size_t rdata_count) {
+    RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(), RRType::TXT(),
+                             RRTTL(3600)));
+    for (size_t i = 0; i < rdata_count; ++i) {
+        rrset->addRdata(rdata::createRdata(RRType::TXT(), RRClass::IN(),
+                                           lexical_cast<std::string>(i)));
+    }
+    return (rrset);
+}
+
+TEST_F(RdataSetTest, createManyRRs) {
+    // RRset with possible maximum number of RDATAs
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_,
+                                          getRRsetWithRdataCount(8191),
+                                          ConstRRsetPtr());
+    EXPECT_EQ(8191, rdataset->getRdataCount());
+    EXPECT_EQ(0, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Exceeding that will result in an exception.
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
+                                  getRRsetWithRdataCount(8192),
+                                  ConstRRsetPtr()),
+                 RdataSetError);
+    // To be very sure even try larger number than the threshold
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
+                                  getRRsetWithRdataCount(65535),
+                                  ConstRRsetPtr()),
+                 RdataSetError);
+}
+
+TEST_F(RdataSetTest, createWithRRSIG) {
+    // Normal case.
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                          rrsig_rrset_);
+    checkRdataSet(*rdataset, true, true);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Unusual case: TTL doesn't match.  This implementation accepts that,
+    // using the TTL of the covered RRset.
+    ConstRRsetPtr rrsig_badttl(textToRRset(
+                                   "www.example.com. 3600 IN RRSIG "
+                                   "A 5 2 3600 20120814220826 "
+                                   "20120715220826 1234 example.com. FAKE"));
+    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_, rrsig_badttl);
+    checkRdataSet(*rdataset, true, true);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+// A helper function to create an RRSIG RRset containing the given number of
+// unique RDATAs.
+ConstRRsetPtr
+getRRSIGWithRdataCount(size_t sig_count) {
+    RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(),
+                             RRType::RRSIG(), RRTTL(3600)));
+    // We use a base wire-format image and tweak the original TTL field to
+    // generate unique RDATAs in the loop.  (Creating them from corresponding
+    // text is simpler, but doing so for a large number of RRSIGs is
+    // relatively heavy and could be too long for unittests).
+    ConstRdataPtr rrsig_base =
+        rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
+                           "A 5 2 3600 20120814220826 20120715220826 1234 "
+                           "example.com. FAKE");
+    isc::util::OutputBuffer ob(0);
+    rrsig_base->toWire(ob);
+    for (size_t i = 0; i < sig_count; ++i) {
+        ob.writeUint16At((i >> 16) & 0xffff, 4);
+        ob.writeUint16At(i & 0xffff, 6);
+        isc::util::InputBuffer ib(ob.getData(), ob.getLength());
+        rrset->addRdata(rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
+                                           ib, ib.getLength()));
+    }
+    return (rrset);
+}
+
+TEST_F(RdataSetTest, createManyRRSIGs) {
+    // 7 has a special meaning in the implementation: if the number of the
+    // RRSIGs reaches this value, an extra 'sig count' field will be created.
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                          getRRSIGWithRdataCount(7));
+    EXPECT_EQ(7, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // 8 would cause overflow in the normal 3-bit field if there were no extra
+    // count field.
+    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                getRRSIGWithRdataCount(8));
+    EXPECT_EQ(8, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Up to 2^16-1 RRSIGs are allowed (although that would be useless
+    // in practice)
+    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                getRRSIGWithRdataCount(65535));
+    EXPECT_EQ(65535, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Exceeding this limit will result in an exception.
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                  getRRSIGWithRdataCount(65536)),
+                 RdataSetError);
+    // To be very sure even try larger number than the threshold
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                  getRRSIGWithRdataCount(70000)),
+                 RdataSetError);
+}
+
+TEST_F(RdataSetTest, createWithRRSIGOnly) {
+    // A rare, but allowed, case: RdataSet without the main RRset but with
+    // RRSIG.
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
+                                          rrsig_rrset_);
+    checkRdataSet(*rdataset, false, true);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+TEST_F(RdataSetTest, badCeate) {
+    // Neither the RRset nor RRSIG RRset is given
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
+                                  ConstRRsetPtr()), isc::BadValue);
+
+    // Empty RRset (An RRset without RDATA)
+    ConstRRsetPtr empty_rrset(new RRset(Name("example.com"), RRClass::IN(),
+                                        RRType::A(), RRTTL(3600)));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, empty_rrset,
+                                  ConstRRsetPtr()), isc::BadValue);
+    ConstRRsetPtr empty_rrsig(new RRset(Name("example.com"), RRClass::IN(),
+                                        RRType::RRSIG(), RRTTL(3600)));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
+                                  empty_rrsig), isc::BadValue);
+
+    // The RRset type and RRSIG's type covered don't match
+    ConstRRsetPtr bad_rrsig(textToRRset(
+                                "www.example.com. 1076895760 IN RRSIG "
+                                "NS 5 2 3600 20120814220826 20120715220826 "
+                                "1234 example.com. FAKE"));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, bad_rrsig),
+                 isc::BadValue);
+
+    // Pass non RRSIG for the sig parameter
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, a_rrset_),
+                 isc::BadValue);
+
+    // Pass RRSIG for normal RRset (the RdataEncoder will catch this and throw)
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, rrsig_rrset_,
+                                  rrsig_rrset_),
+                 isc::BadValue);
+
+    // RR class doesn't match between RRset and RRSIG
+    ConstRRsetPtr badclass_rrsig(textToRRset(
+                                     "www.example.com. 1076895760 CH RRSIG "
+                                     "A 5 2 3600 20120814220826 "
+                                     "20120715220826 1234 example.com. FAKE",
+                                     RRClass::CH()));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                  badclass_rrsig),
+                 isc::BadValue);
+}
+}
diff --git a/src/lib/datasrc/memory/tests/segment_object_holder_unittest.cc b/src/lib/datasrc/memory/tests/segment_object_holder_unittest.cc
new file mode 100644
index 0000000..d27e364
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/segment_object_holder_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/memory_segment_local.h>
+
+#include <datasrc/memory/segment_object_holder.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::util;
+using namespace isc::datasrc::memory;
+using namespace isc::datasrc::memory::detail;
+
+namespace {
+const int TEST_ARG_VAL = 42;    // arbitrary chosen magic number
+
+class TestObject {
+public:
+    static void destroy(MemorySegment& sgmt, TestObject* obj, int arg) {
+        sgmt.deallocate(obj, sizeof(*obj));
+        EXPECT_EQ(TEST_ARG_VAL, arg);
+    }
+};
+
+void
+useHolder(MemorySegment& sgmt, TestObject* obj, bool release) {
+    // Create a holder object, check the return value of get(), and,
+    // if requested, release the held object.  At the end of function
+    // the holder is destructed, and if the object hasn't been released by
+    // then, it should be deallocated.  Passed argument is checked in its
+    // deallocate().
+
+    typedef SegmentObjectHolder<TestObject, int> HolderType;
+    HolderType holder(sgmt, obj, TEST_ARG_VAL);
+    EXPECT_EQ(obj, holder.get());
+    if (release) {
+        EXPECT_EQ(obj, holder.release());
+    }
+}
+
+TEST(SegmentObjectHolderTest, foo) {
+    MemorySegmentLocal sgmt;
+    void* p = sgmt.allocate(sizeof(TestObject));
+    TestObject* obj = new(p) TestObject;
+
+    // Use holder, and release the content.  The memory shouldn't be
+    // deallocated.
+    useHolder(sgmt, obj, true);
+    EXPECT_FALSE(sgmt.allMemoryDeallocated());
+
+    // Use holder, and let it deallocate the object.  The memory segment
+    // should now be empty.
+    useHolder(sgmt, obj, false);
+    EXPECT_TRUE(sgmt.allMemoryDeallocated());
+}
+}
diff --git a/src/lib/datasrc/memory/tests/zone_data_unittest.cc b/src/lib/datasrc/memory/tests/zone_data_unittest.cc
new file mode 100644
index 0000000..d15fe8b
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/zone_data_unittest.cc
@@ -0,0 +1,255 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "memory_segment_test.h"
+
+#include <dns/rdataclass.h>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/name.h>
+#include <dns/labelsequence.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/rdataset.h>
+#include <datasrc/memory/zone_data.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <new>                  // for bad_alloc
+#include <string>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+using namespace isc::datasrc::memory::test;
+using namespace isc::testutils;
+
+namespace {
+
+// With this single fixture we'll test both NSEC3Data and ZoneData
+class ZoneDataTest : public ::testing::Test {
+protected:
+    ZoneDataTest() :
+        nsec3_data_(NULL), param_rdata_("1 0 12 aabbccdd"),
+        param_rdata_nosalt_("1 1 10 -"),
+        param_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a')),
+        nsec3_rdata_("1 0 12 aabbccdd TDK23RP6 SOA"),
+        nsec3_rdata_nosalt_("1 1 10 - TDK23RP6 SOA"),
+        nsec3_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a') +
+                               " TDK23RP6 SOA"),
+        zname_("example.com"),
+        zone_data_(ZoneData::create(mem_sgmt_, zname_)),
+        a_rrset_(textToRRset("www.example.com. 3600 IN A 192.0.2.1")),
+        aaaa_rrset_(textToRRset("www.example.com. 3600 IN AAAA 2001:db8::1")),
+        nsec3_rrset_(textToRRset("TDK23RP6.example.com. 3600 IN NSEC3 "
+                                 "1 0 12 aabbccdd TDK23RP6 SOA"))
+    {}
+    void TearDown() {
+        if (nsec3_data_ != NULL) {
+            NSEC3Data::destroy(mem_sgmt_, nsec3_data_, RRClass::IN());
+        }
+        if (zone_data_ != NULL) {
+            ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN());
+        }
+        // detect any memory leak in the test memory segment
+        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+    }
+
+    MemorySegmentTest mem_sgmt_;
+    NSEC3Data* nsec3_data_;
+    const generic::NSEC3PARAM param_rdata_, param_rdata_nosalt_,
+        param_rdata_largesalt_;
+    const generic::NSEC3 nsec3_rdata_, nsec3_rdata_nosalt_,
+        nsec3_rdata_largesalt_;
+    const Name zname_;
+    ZoneData* zone_data_;
+    const ConstRRsetPtr a_rrset_, aaaa_rrset_, nsec3_rrset_;
+    RdataEncoder encoder_;
+};
+
+// Shared by both test cases using NSEC3 and NSEC3PARAM Rdata
+template <typename RdataType>
+void
+checkNSEC3Data(MemorySegmentTest& mem_sgmt, const RdataType& expect_rdata) {
+    NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt, expect_rdata);
+
+    // Internal tree should be created and empty.
+    EXPECT_EQ(0, nsec3_data->getNSEC3Tree().getNodeCount());
+
+    EXPECT_EQ(expect_rdata.getHashalg(), nsec3_data->hashalg);
+    EXPECT_EQ(expect_rdata.getFlags(), nsec3_data->flags);
+    EXPECT_EQ(expect_rdata.getIterations(), nsec3_data->iterations);
+    EXPECT_EQ(expect_rdata.getSalt().size(), nsec3_data->getSaltLen());
+    if (expect_rdata.getSalt().size() > 0) {
+        EXPECT_EQ(0, memcmp(&expect_rdata.getSalt()[0],
+                            nsec3_data->getSaltData(),
+                            expect_rdata.getSalt().size()));
+    }
+
+    NSEC3Data::destroy(mem_sgmt, nsec3_data, RRClass::IN());
+}
+
+void
+checkFindRdataSet(const ZoneTree& tree, const Name& name, RRType type,
+                  const RdataSet* expected_set)
+{
+    ZoneNode* node = NULL;
+    tree.find(name, &node);
+    ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+    EXPECT_EQ(expected_set, RdataSet::find(node->getData(), type));
+}
+
+TEST_F(ZoneDataTest, createNSEC3Data) {
+    // Create an NSEC3Data object from various types of RDATA (of NSEC3PARAM
+    // and of NSEC3), check if the resulting parameters match.
+    checkNSEC3Data(mem_sgmt_, param_rdata_); // one 'usual' form of params
+    checkNSEC3Data(mem_sgmt_, param_rdata_nosalt_); // empty salt
+    checkNSEC3Data(mem_sgmt_, param_rdata_largesalt_); // max-len salt
+
+    // Same concepts of the tests, using NSEC3 RDATA.
+    checkNSEC3Data(mem_sgmt_, nsec3_rdata_);
+    checkNSEC3Data(mem_sgmt_, nsec3_rdata_nosalt_);
+    checkNSEC3Data(mem_sgmt_, nsec3_rdata_largesalt_);
+}
+
+TEST_F(ZoneDataTest, addNSEC3) {
+    nsec3_data_ = NSEC3Data::create(mem_sgmt_, param_rdata_);
+
+    ZoneNode* node = NULL;
+    nsec3_data_->insertName(mem_sgmt_, nsec3_rrset_->getName(), &node);
+    ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+    EXPECT_TRUE(node->isEmpty()); // initially it should be empty
+
+    RdataSet* rdataset_nsec3 =
+        RdataSet::create(mem_sgmt_, encoder_, nsec3_rrset_, ConstRRsetPtr());
+    node->setData(rdataset_nsec3);
+
+    // Confirm we can find the added ones from the zone data.
+    checkFindRdataSet(nsec3_data_->getNSEC3Tree(), nsec3_rrset_->getName(),
+                      RRType::NSEC3(), rdataset_nsec3);
+
+    // TearDown() will confirm there's no leak on destroy
+}
+
+TEST_F(ZoneDataTest, getOriginNode) {
+    EXPECT_EQ(LabelSequence(zname_), zone_data_->getOriginNode()->getLabels());
+}
+
+TEST_F(ZoneDataTest, exceptionSafetyOnCreate) {
+    // Note: below, we use our knowledge of how memory allocation happens
+    // within the NSEC3Data, the zone data and the underlying domain tree
+    // implementation.  We'll emulate rare situations where allocate() fails
+    // with an exception, and confirm it doesn't cause any harsh disruption
+    // or leak.
+
+    // Creating internal NSEC3 tree will succeed, but allocation of NSEC3Data
+    // will fail due to bad_alloc.  It shouldn't cause memory leak
+    // (that would be caught in TearDown()).
+    mem_sgmt_.setThrowCount(2);
+    EXPECT_THROW(NSEC3Data::create(mem_sgmt_, param_rdata_), std::bad_alloc);
+
+    // allocate() will throw on the insertion of the origin node.
+    mem_sgmt_.setThrowCount(2);
+    EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
+
+    // allocate() will throw on creating the zone data.
+    mem_sgmt_.setThrowCount(3);
+    EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
+
+    // These incomplete create() attempts shouldn't cause memory leak
+    // (that would be caught in TearDown()).
+}
+
+TEST_F(ZoneDataTest, addRdataSets) {
+    // Insert a name to the zone, and add a couple the data (RdataSet) objects
+    // to the corresponding node.
+
+    ZoneNode* node = NULL;
+    zone_data_->insertName(mem_sgmt_, a_rrset_->getName(), &node);
+    ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+    EXPECT_TRUE(node->isEmpty()); // initially it should be empty
+
+    RdataSet* rdataset_a =
+        RdataSet::create(mem_sgmt_, encoder_, a_rrset_, ConstRRsetPtr());
+    node->setData(rdataset_a);
+
+    RdataSet* rdataset_aaaa =
+        RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset_, ConstRRsetPtr());
+    // make a linked list and replace the list head
+    rdataset_aaaa->next = rdataset_a;
+    node->setData(rdataset_aaaa);
+
+    // Confirm we can find the added ones from the zone data.
+    checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+                      RRType::A(), rdataset_a);
+    checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+                      RRType::AAAA(), rdataset_aaaa);
+    // There's no NS (or anything other than AAAA or A) RdataSet in the list
+    checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+                      RRType::NS(), NULL);
+
+    // TearDown() will confirm there's no leak on destroy
+}
+
+TEST_F(ZoneDataTest, getSetNSEC3Data) {
+    // Initially there's no NSEC3 data
+    EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
+    // isNSEC3Signed is true iff zone data has non NULL NSEC3 data
+    EXPECT_FALSE(zone_data_->isNSEC3Signed());
+
+    // Set a new one.  The set method should return NULL.  The get method
+    // should return the new one.
+    NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt_, param_rdata_);
+    NSEC3Data* old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data);
+    EXPECT_EQ(static_cast<NSEC3Data*>(NULL), old_nsec3_data);
+    EXPECT_EQ(nsec3_data, zone_data_->getNSEC3Data());
+    EXPECT_TRUE(zone_data_->isNSEC3Signed());
+
+    // Replace an existing one with a yet another one.
+    // We're responsible for destroying the old one.
+    NSEC3Data* nsec3_data2 = NSEC3Data::create(mem_sgmt_, nsec3_rdata_);
+    old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data2);
+    EXPECT_EQ(nsec3_data, old_nsec3_data);
+    EXPECT_EQ(nsec3_data2, zone_data_->getNSEC3Data());
+    EXPECT_TRUE(zone_data_->isNSEC3Signed());
+    NSEC3Data::destroy(mem_sgmt_, old_nsec3_data, RRClass::IN());
+
+    // Setting NULL clears any existing one.
+    old_nsec3_data = zone_data_->setNSEC3Data(NULL);
+    EXPECT_EQ(nsec3_data2, old_nsec3_data);
+    EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
+    EXPECT_FALSE(zone_data_->isNSEC3Signed());
+
+    // Then set it again.  The zone data should destroy it on its own
+    // destruction.
+    zone_data_->setNSEC3Data(old_nsec3_data);
+}
+
+TEST_F(ZoneDataTest, isSigned) {
+    // By default it's considered unsigned
+    EXPECT_FALSE(zone_data_->isSigned());
+
+    // declare it's signed, the isSigned() says so too
+    zone_data_->setSigned(true);
+    EXPECT_TRUE(zone_data_->isSigned());
+
+    // change it to unsigned again
+    zone_data_->setSigned(false);
+    EXPECT_FALSE(zone_data_->isSigned());
+}
+}
diff --git a/src/lib/datasrc/memory/tests/zone_table_unittest.cc b/src/lib/datasrc/memory/tests/zone_table_unittest.cc
index 4f0af5e..359df49 100644
--- a/src/lib/datasrc/memory/tests/zone_table_unittest.cc
+++ b/src/lib/datasrc/memory/tests/zone_table_unittest.cc
@@ -55,21 +55,23 @@ private:
 
 class ZoneTableTest : public ::testing::Test {
 protected:
-    ZoneTableTest() : zname1(Name("example.com")),
+    ZoneTableTest() : zclass_(RRClass::IN()),
+                      zname1(Name("example.com")),
                       zname2(Name("example.net")),
                       zname3(Name("example")),
-                      zone_table(ZoneTable::create(mem_sgmt_))
+                      zone_table(ZoneTable::create(mem_sgmt_, zclass_))
     {}
     ~ZoneTableTest() {
         if (zone_table != NULL) {
-            ZoneTable::destroy(mem_sgmt_, zone_table);
+            ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
         }
     }
     void TearDown() {
-        ZoneTable::destroy(mem_sgmt_, zone_table);
+        ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
         zone_table = NULL;
         EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
     }
+    const RRClass zclass_;
     const Name zname1, zname2, zname3;
     TestMemorySegment mem_sgmt_;
     ZoneTable* zone_table;
@@ -80,41 +82,46 @@ TEST_F(ZoneTableTest, create) {
     // tests.  We only check exception safety by letting the test memory
     // segment throw.
     mem_sgmt_.setThrowCount(2);
-    EXPECT_THROW(ZoneTable::create(mem_sgmt_), std::bad_alloc);
+    EXPECT_THROW(ZoneTable::create(mem_sgmt_, zclass_), std::bad_alloc);
     // This shouldn't cause memory leak (that would be caught in TearDown()).
 }
 
 TEST_F(ZoneTableTest, addZone) {
     // Normal successful case.
     const ZoneTable::AddResult result1 =
-        zone_table->addZone(mem_sgmt_, zname1);
+        zone_table->addZone(mem_sgmt_, zclass_, zname1);
     EXPECT_EQ(result::SUCCESS, result1.code);
 
     // Duplicate add doesn't replace the existing data.
-    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zname1).code);
+    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
+                                                 zname1).code);
     EXPECT_EQ(result1.zone_data,
-              zone_table->addZone(mem_sgmt_, zname1).zone_data);
+              zone_table->addZone(mem_sgmt_, zclass_, zname1).zone_data);
     // names are compared in a case insensitive manner.
-    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_,
+    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
                                                  Name("EXAMPLE.COM")).code);
     // Add some more different ones.  Should just succeed.
-    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname2).code);
-    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname3).code);
+    EXPECT_EQ(result::SUCCESS,
+              zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
+    EXPECT_EQ(result::SUCCESS,
+              zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
 
     // Have the memory segment throw an exception in extending the internal
     // tree.  It still shouldn't cause memory leak (which would be detected
     // in TearDown()).
     mem_sgmt_.setThrowCount(2);
-    EXPECT_THROW(zone_table->addZone(mem_sgmt_, Name("example.org")),
+    EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org")),
                  std::bad_alloc);
 }
 
 TEST_F(ZoneTableTest, findZone) {
     const ZoneTable::AddResult add_result1 =
-        zone_table->addZone(mem_sgmt_, zname1);
+        zone_table->addZone(mem_sgmt_, zclass_, zname1);
     EXPECT_EQ(result::SUCCESS, add_result1.code);
-    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname2).code);
-    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname3).code);
+    EXPECT_EQ(result::SUCCESS,
+              zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
+    EXPECT_EQ(result::SUCCESS,
+              zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
 
     const ZoneTable::FindResult find_result1 =
         zone_table->findZone(Name("example.com"));
@@ -135,7 +142,7 @@ TEST_F(ZoneTableTest, findZone) {
 
     // make sure the partial match is indeed the longest match by adding
     // a zone with a shorter origin and query again.
-    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_,
+    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_,
                                                    Name("com")).code);
     EXPECT_EQ(add_result1.zone_data,
               zone_table->findZone(Name("www.example.com")).zone_data);
diff --git a/src/lib/datasrc/memory/zone_data.cc b/src/lib/datasrc/memory/zone_data.cc
new file mode 100644
index 0000000..e2cbdef
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_data.cc
@@ -0,0 +1,171 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/memory_segment.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rdataclass.h>
+
+#include "rdataset.h"
+#include "rdata_serialization.h"
+#include "zone_data.h"
+#include "segment_object_holder.h"
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
+#include <cassert>
+#include <cstring>
+#include <new>                  // for the placement new
+#include <vector>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+namespace {
+void
+rdataSetDeleter(RRClass rrclass, util::MemorySegment* mem_sgmt,
+                RdataSet* rdataset_head)
+{
+    RdataSet* rdataset_next;
+    for (RdataSet* rdataset = rdataset_head;
+         rdataset != NULL;
+         rdataset = rdataset_next)
+    {
+        rdataset_next = rdataset->getNext();
+        RdataSet::destroy(*mem_sgmt, rrclass, rdataset);
+    }
+}
+
+void
+nullDeleter(RdataSet* rdataset_head) {
+    assert(rdataset_head == NULL);
+}
+}
+
+NSEC3Data*
+NSEC3Data::create(util::MemorySegment& mem_sgmt,
+                  const generic::NSEC3PARAM& rdata)
+{
+    return (NSEC3Data::create(mem_sgmt, rdata.getHashalg(), rdata.getFlags(),
+                              rdata.getIterations(), rdata.getSalt()));
+}
+
+NSEC3Data*
+NSEC3Data::create(util::MemorySegment& mem_sgmt, const generic::NSEC3& rdata) {
+    return (NSEC3Data::create(mem_sgmt, rdata.getHashalg(), rdata.getFlags(),
+                              rdata.getIterations(), rdata.getSalt()));
+}
+
+NSEC3Data*
+NSEC3Data::create(util::MemorySegment& mem_sgmt, uint8_t hashalg,
+                  uint8_t flags, uint16_t iterations,
+                  const std::vector<uint8_t>& salt)
+{
+    // NSEC3Data allocation can throw.  To avoid leaking the tree, we manage
+    // it in the holder.
+    // Note: we won't add any RdataSet, so we use the NO-OP deleter
+    // (with an assertion check for that).
+    typedef boost::function<void(RdataSet*)> RdataSetDeleterType;
+    detail::SegmentObjectHolder<ZoneTree, RdataSetDeleterType> holder(
+        mem_sgmt, ZoneTree::create(mem_sgmt, true),
+        boost::bind(nullDeleter, _1));
+
+    const size_t salt_len = salt.size();
+
+    void* p = mem_sgmt.allocate(sizeof(NSEC3Data) + 1 + salt_len);
+    NSEC3Data* const param_data =
+        new(p) NSEC3Data(holder.release(), hashalg, flags, iterations);
+    uint8_t* dp = param_data->getSaltBuf();
+    *dp++ = salt_len;
+    if (salt_len > 0) {
+        std::memcpy(dp, &salt.at(0), salt_len); // use at for safety
+    }
+
+    return (param_data);
+}
+
+void
+NSEC3Data::destroy(util::MemorySegment& mem_sgmt, NSEC3Data* data,
+                   RRClass nsec3_class)
+{
+    ZoneTree::destroy(mem_sgmt, data->nsec3_tree_.get(),
+                      boost::bind(rdataSetDeleter, nsec3_class, &mem_sgmt,
+                                  _1));
+    mem_sgmt.deallocate(data, sizeof(NSEC3Data) + 1 + data->getSaltLen());
+}
+
+void
+NSEC3Data::insertName(util::MemorySegment& mem_sgmt, const Name& name,
+                      ZoneNode** node)
+{
+    const ZoneTree::Result result = nsec3_tree_->insert(mem_sgmt, name, node);
+
+    // This should be ensured by the API:
+    assert((result == ZoneTree::SUCCESS ||
+            result == ZoneTree::ALREADYEXISTS) && node != NULL);
+}
+
+ZoneData*
+ZoneData::create(util::MemorySegment& mem_sgmt, const Name& zone_origin) {
+    // ZoneTree::insert() and ZoneData allocation can throw.  See also
+    // NSEC3Data::create().
+    typedef boost::function<void(RdataSet*)> RdataSetDeleterType;
+    detail::SegmentObjectHolder<ZoneTree, RdataSetDeleterType> holder(
+        mem_sgmt, ZoneTree::create(mem_sgmt, true),
+        boost::bind(nullDeleter, _1));
+
+    ZoneTree* tree = holder.get();
+    ZoneNode* origin_node = NULL;
+    const ZoneTree::Result result =
+        tree->insert(mem_sgmt, zone_origin, &origin_node);
+    assert(result == ZoneTree::SUCCESS);
+    void* p = mem_sgmt.allocate(sizeof(ZoneData));
+    ZoneData* zone_data = new(p) ZoneData(holder.release(), origin_node);
+
+    return (zone_data);
+}
+
+void
+ZoneData::destroy(util::MemorySegment& mem_sgmt, ZoneData* zone_data,
+                  RRClass zone_class)
+{
+    ZoneTree::destroy(mem_sgmt, zone_data->zone_tree_.get(),
+                      boost::bind(rdataSetDeleter, zone_class, &mem_sgmt,
+                                  _1));
+    if (zone_data->nsec3_data_) {
+        NSEC3Data::destroy(mem_sgmt, zone_data->nsec3_data_.get(), zone_class);
+    }
+    mem_sgmt.deallocate(zone_data, sizeof(ZoneData));
+}
+
+void
+ZoneData::insertName(util::MemorySegment& mem_sgmt, const Name& name,
+                     ZoneNode** node)
+{
+    const ZoneTree::Result result = zone_tree_->insert(mem_sgmt, name, node);
+
+    // This should be ensured by the API:
+    assert((result == ZoneTree::SUCCESS ||
+            result == ZoneTree::ALREADYEXISTS) && node != NULL);
+}
+
+} // namespace memory
+} // namespace datasrc
+} // datasrc isc
diff --git a/src/lib/datasrc/memory/zone_data.h b/src/lib/datasrc/memory/zone_data.h
index 31c4520..f592680 100644
--- a/src/lib/datasrc/memory/zone_data.h
+++ b/src/lib/datasrc/memory/zone_data.h
@@ -17,29 +17,542 @@
 
 #include <util/memory_segment.h>
 
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/memory/domaintree.h>
+#include <datasrc/memory/rdataset.h>
+
+#include <boost/interprocess/offset_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <vector>
+
 namespace isc {
+namespace dns {
+namespace rdata {
+namespace generic {
+class NSEC3PARAM;
+class NSEC3;
+}
+}
+}
+
 namespace datasrc {
 namespace memory {
-/// \brief Data for a single zone.
+
+typedef DomainTree<RdataSet> ZoneTree;
+typedef DomainTreeNode<RdataSet> ZoneNode;
+
+/// \brief NSEC3 data for a DNS zone.
+///
+/// This class encapsulates a set of NSEC3 related data for a zone
+/// that is signed with NSEC3 RRs.  Specifically, it contains hash
+/// parameters as given in an NSEC3PARAM RDATA and all NSEC3 RRs of the zone.
 ///
-/// It's currently empty and is only provided for the implementation of
-/// ZoneTable.  The actual implementation of this class is the subject of
-/// Trac #2107.
-class ZoneData {
+/// The main concept of the class is generally the same as that of
+/// \c ZoneData (see its description for details), but the related data
+//// are encapsulated in a more straightforward way in this class.
+///
+/// The NSEC3 RRs (which should normally have RRSIGs) are stored in a
+/// \c DomainTree object whose data type is (a list of) \c RdataSet.
+/// This tree is expected to store NSEC3 RRs only, so the RR type of
+/// \c RdataSet should be NSEC3.  But this class itself doesn't guarantee
+/// this condition.  It's the caller's responsibility.
+///
+/// Read-only access to the tree is possible via the \c getNSEC3Tree() method.
+/// Modifying the tree must be done by a specific method (in the initial
+/// implementation, it's \c insertName().  There may be some more as we
+/// see the need); the application cannot directly change the content of the
+/// tree in an arbitrary way.  This class does not have a strong reason to be
+/// that strict, but is defined this way mainly to be consistent with the
+/// \c ZoneData class.
+///
+/// Most of the hash parameters are maintained in the form of straightforward
+/// member variables, which can be directly referenced by the application.
+/// The exception is the salt, which is encapsulated as opaque data
+/// immediately following the main class object, and should be accessible
+/// via the \c getSaltLen() and \c getSaltData() method.
+///
+/// \note The fact that the this class couples one set of hash parameters
+/// and the set of NSEC3 RRs implicitly means a zone is assumed to have
+/// only one set of NSEC3 parameters.  When we support multiple sets of
+/// parameters the design should be revised accordingly.
+class NSEC3Data : boost::noncopyable {
+public:
+    /// \brief Allocate and construct \c NSEC3Data from NSEC3PARAM Rdata.
+    ///
+    /// The NSEC3 parameters are extracted and stored within the created
+    /// \c NSEC3Data object.
+    ///
+    /// \throw std::bad_alloc Memory allocation fails.
+    ///
+    /// \param mem_sgmt A \c MemorySegment from which memory for the new
+    /// \c NSEC3Data is allocated.
+    /// \param rdata An NSEC3PARAM RDATA that specifies the NSEC3 parameters
+    /// to be stored.
+    static NSEC3Data* create(util::MemorySegment& mem_sgmt,
+                             const dns::rdata::generic::NSEC3PARAM& rdata);
+
+    /// \brief Allocate and construct \c NSEC3Data from NSEC3 Rdata.
+    ///
+    /// The NSEC3 hash parameters are extracted and stored within the created
+    /// \c NSEC3Data object.
+    ///
+    /// \throw std::bad_alloc Memory allocation fails.
+    ///
+    /// \param mem_sgmt A \c MemorySegment from which memory for the new
+    /// \c NSEC3Data is allocated.
+    /// \param rdata An NSEC3 RDATA that specifies the NSEC3 parameters
+    /// to be stored.
+    static NSEC3Data* create(util::MemorySegment& mem_sgmt,
+                             const dns::rdata::generic::NSEC3& rdata);
+
+    /// \brief Destruct and deallocate \c NSEC3Data.
+    ///
+    /// It releases all resources allocated for the internal NSEC3 name space
+    /// including NSEC3 RdataSet.  It assumes \c RdataSets objects stored
+    /// in the space were allocated using the same memory segment as
+    /// \c mem_sgmt.  The caller must ensure this assumption.
+    ///
+    /// Note that an \c RRClass object must be passed to this method.
+    /// It's necessary to destroy the stored \c RdataSet objects
+    /// (see its class description).  This class doesn't hold this information;
+    /// it's the caller's responsibility to associate an \c NSEC3Data
+    /// class object with its expected RR class, and pass it to
+    /// \c destroy().  (In practice, it will be passed via
+    /// \c ZoneData::destroy().)
+    ///
+    /// \throw none
+    ///
+    /// \param mem_sgmt The \c MemorySegment that allocated memory for
+    /// \c data.
+    /// \param data A non-NULL pointer to a valid NSEC3Data object
+    /// that was originally created by the \c create() method (the behavior
+    /// is undefined if this condition isn't met).
+    /// \param nsec3_class The RR class of the \c RdataSet stored in the NSEC3
+    /// name space to be destroyed.
+    static void destroy(util::MemorySegment& mem_sgmt, NSEC3Data* data,
+                        dns::RRClass nsec3_class);
+
 private:
-    ZoneData() {}
-    ~ZoneData() {}
+    // Domain tree for the Internal NSEC3 name space.  Access to it is
+    // limited only via public methods.
+    const boost::interprocess::offset_ptr<ZoneTree> nsec3_tree_;
 public:
-    static ZoneData* create(util::MemorySegment& mem_sgmt) {
-        void* p = mem_sgmt.allocate(sizeof(ZoneData));
-        ZoneData* zone_data = new(p) ZoneData();
-        return (zone_data);
+    const uint8_t hashalg;      ///< Hash algorithm
+    const uint8_t flags;        ///< NSEC3 parameter flags
+    const uint16_t iterations;  ///< Hash iterations
+    // For 64-bit machines there'll be padding space here, but since
+    // only at most one instance (or a few in very rare cases) will be
+    // created per zone, the overhead should be acceptable.
+
+    /// \brief Return \c ZoneTree for the NSEC3 name space.
+    ///
+    /// \throw none
+    const ZoneTree& getNSEC3Tree() const { return (*nsec3_tree_); }
+
+    /// \brief Return the size of NSEC3 salt.
+    ///
+    /// \throw none
+    ///
+    /// The return value must be in the range between 0 and 255 (inclusive).
+    size_t getSaltLen() const { return (*getSaltBuf()); }
+
+    /// \brief Return a pointer to the salt data.
+    ///
+    /// \throw none
+    ///
+    /// The valid range is up to the \c getSaltLen() bytes from the
+    /// returned value.  If \c getSaltLen() returns 0, the return value
+    /// of this method is invalid and must not be used.
+    const uint8_t* getSaltData() const { return (getSaltBuf() + 1); }
+
+    /// \brief Insert a name to the NSEC3 name space.
+    ///
+    /// It allocates resource for the given name in the internal NSEC3 name
+    /// space, and returns an access point to it in the form of \c ZoneNode
+    /// pointer via the given \c node variable.  If the name already exists
+    /// in the name space, it returns a pointer to the existing node.
+    ///
+    /// This method does not perform any semantics check on the given name
+    /// (e.g., whether the first label is a valid encoded string for an NSEC3
+    /// owner name).
+    ///
+    /// \throw std::bad_alloc Memory allocation fails
+    ///
+    /// \param mem_sgmt Memory segment in which resource for the new memory
+    /// is to be allocated.
+    /// \param name The name to be inserted.
+    /// \param node A pointer to \c ZoneNode pointer in which the created or
+    /// found node for the name is stored.  Must not be NULL (the method does
+    /// not check that condition).
+    void insertName(util::MemorySegment& mem_sgmt, const dns::Name& name,
+                    ZoneNode** node);
+
+private:
+    // Common subroutine for the public versions of create().
+    static NSEC3Data* create(util::MemorySegment& mem_sgmt, uint8_t hashalg,
+                             uint8_t flags, uint16_t iterations,
+                             const std::vector<uint8_t>& salt);
+
+    /// \brief The constructor.
+    ///
+    /// An object of this class is always expected to be created by the
+    /// allocator (\c create()), so the constructor is hidden as private.
+    ///
+    /// It never throws an exception.
+    NSEC3Data(ZoneTree* nsec3_tree_param, uint8_t hashalg_param,
+              uint8_t flags_param, uint16_t iterations_param) :
+        nsec3_tree_(nsec3_tree_param), hashalg(hashalg_param),
+        flags(flags_param), iterations(iterations_param)
+    {}
+
+    const uint8_t* getSaltBuf() const {
+        return (reinterpret_cast<const uint8_t*>(this + 1));
     }
-    static void destroy(util::MemorySegment& mem_sgmt, ZoneData* zone_data) {
-        zone_data->~ZoneData();
-        mem_sgmt.deallocate(zone_data, sizeof(ZoneData));
+    uint8_t* getSaltBuf() {
+        return (reinterpret_cast<uint8_t*>(this + 1));
     }
 };
+
+/// \brief DNS zone data.
+///
+/// This class encapsulates the content of a DNS zone (which is essentially a
+/// set of RRs) in a memory efficient way and provides accessor interfaces
+/// to it.
+///
+/// The primary goal of this class is to provide a packed structure of the
+/// data for memory efficiency.  Basically, this class should be considered
+/// a private part of some other classes within this module and should not
+/// be used directly from normal applications.  So it's not intended to hide
+/// much of the underlying implementation details; rather, it tries
+/// to keep the representation simple.
+///
+/// The RRs are stored in a \c DomainTree object whose data type is
+/// (a list of) \c RdataSet.  The tree nodes correspond to owner names,
+/// and the \c RdataSet objects (forming a linked list) set in the node
+/// represent the rest of the RR parameters except the RR class: type,
+/// TTL, and RDATA.  This class does not have any knowledge of the RR class
+/// of the zone; since it's quite likely that the application maintains
+/// a set of zones of the same RR class, and the number of such zones can be
+/// huge, it makes more sense to have the application maintain the class value
+/// in a unified way to minimize memory footprint.
+///
+/// The \c DomainTree object in this class is not expected to hold NSEC3
+/// RRs when the zone is signed with NSEC3; they should be maintained
+/// in an associated \c NSEC3Data object.  But this class does not prevent
+/// the unexpected usage of adding an NSEC3 RdataSet directly in the tree.
+/// It's the caller's responsibility to ensure this assumption.
+///
+/// This class maintains some other meta data and additional zone related
+/// content.  First, it automatically creates a \c DomainTree node for the
+/// zone's origin name on initialization and keeps a reference to it
+/// throughout its lifetime.  This is the case even if the zone doesn't have
+/// any RRs (such as in the case before initial loading).  Any valid zone
+/// to be served should have an RR at the origin node (at least SOA, for
+/// example), so this assumption should be reasonable.  But the application
+/// must ensure that any \c ZoneData object in actual use should have an
+/// RR at the origin; otherwise the inconsistency between the internal state
+/// and the actual zone content could lead to unexpected disruption.
+/// In particular, it must be careful when it supports dynamic updates
+/// to an existing zone so an update attempt doesn't result in deleting
+/// the origin node.
+///
+/// To ensure integrity regarding the reference to the origin, write
+/// access to the tree node can be done only by public methods; the member
+/// variable for the tree is hidden as private.  On the other hand, read-only
+/// access to the tree is allowed via the const version of \c getZoneTree()
+/// method for the convenience of the application.  So, it's intentional
+/// that there's no non-const version of this method.  Do not add one
+/// when this class is to be extended.
+///
+/// Another type of meta data is parameters and records of NSEC3 RRs
+/// when the zone is signed with NSEC3.  It's represented in the form of
+/// an \c NSEC3Data object, and a \c ZoneData object may be associated with
+/// 0 or 1 \c NSEC3Data objects using the \c setNSEC3Data() method, which
+/// can be retrieved by the \c getNSEC3Data() method.  If the \c ZoneData
+/// object is not associated with an \c NSEC3Data object, it's considered not
+/// signed with NSEC3 RRs; otherwise it's considered to be signed with
+/// NSEC3 RRs and with the parameters stored in the \c NSEC3Data object.
+///
+/// \note This interpretation may change in the future when we support migration
+/// from NSEC to NSEC3 or vice versa, support incremental signing, or support
+/// multiple sets of NSEC3 parameters.
+///
+/// One last type of meta data is the status of the zone in terms of DNSSEC
+/// signing.  This class supports the following concepts:
+/// - Whether the zone is signed or not, either with NSEC records or NSEC3
+///   records.
+/// - Whether the zone has a complete set of NSEC3 records.
+///
+/// The former status can be accessed via the \c isSigned() and \c setSigned()
+/// methods; the latter can be retrieved via the \c isNSEC3Signed() method.
+///
+/// This class does not actually relate the status of signed-or-not to
+/// any of its other attributes; it's up to the application how to set or
+/// use this status and maintain it in a reasonable way.  One possible
+/// definition is to set this status if and only if the zone has a
+/// DNSKEY RR at the zone origin (which is BIND 9's definition of signed
+/// zone).  When the application adopts this definition, it's the
+/// application's responsibility to keep the status consistent with the
+/// actual existence or non-existence of a DNSKEY RR.
+///
+/// In the current implementation, a zone is considered to have a complete
+/// set of NSEC3 records if and only if it's associated with an \c NSEC3Data
+/// object (as noted above, these concepts may be separated in future).
+/// For this reason there is no "set" method for the latter; setting
+/// an \c NSEC3Data effectively enables the latter status.  \c isNSEC3Signed()
+/// method is still provided (even though it's a kind of trivial wrapper to
+/// \c getNSEC3Data()) partly for a more intuitive shortcut, and partly
+/// because we won't have to change the application code when we implement
+/// the future separation.
+///
+/// The intended usage of these two status concepts is to implement the
+/// \c ZoneFinder::Context::isNSECSigned() and
+/// \c ZoneFinder::Context::isNSEC3Signed() methods.  A possible implementation
+/// is as follows:
+/// - \c ZoneFinder::Context::isNSECSigned() returns true iff \c isSigned()
+///   is true and \c isNSEC3Signed() is false.
+/// - \c ZoneFinder::Context::isNSEC3Signed() returns true iff \c isSigned()
+///   is true and \c isNSEC3Signed() is true.
+///
+/// Note that even though \c isNSEC3Signed() being true should indicate
+/// \c isSigned() is true too in practice, the interfaces do not
+/// automatically ensure that, so we'd need to check both conditions
+/// explicitly.  And, in fact, if we adopt the above definition of
+/// \c isSigned(), it's possible that a zone has a complete set of NSEC3
+/// RRs but no DNSKEY (although it's effectively a broken zone unless we
+/// support incremental signing).
+///
+/// This class is designed so an instance can be stored in a shared
+/// memory region.  So the pointer member variables (the initial
+/// implementation only contains pointer member variables) are defined
+/// as offset pointers.  When this class is extended these properties must
+/// be preserved, and must also meet other requirements so it can be stored
+/// in a shared memory region (see, for example, \c RdataSet description).
+/// Future extensions must also be conscious of placing the member variables
+/// so that they will not accidentally cause padding and increase memory
+/// footprint.
+class ZoneData : boost::noncopyable {
+private:
+    /// \brief The constructor.
+    ///
+    /// An object of this class is always expected to be created by the
+    /// allocator (\c create()), so the constructor is hidden as private.
+    ///
+    /// It never throws an exception.
+    ZoneData(ZoneTree* zone_tree, ZoneNode* origin_node) :
+        zone_tree_(zone_tree), origin_node_(origin_node)
+    {}
+
+    // Zone node flags.
+private:
+    // Set in the origin node (which always exists at the same address)
+    // to indicate whether the zone is signed or not.  Internal use,
+    // so defined as private.
+    static const ZoneNode::Flags DNSSEC_SIGNED = ZoneNode::FLAG_USER1;
+public:
+    /// \brief Node flag indicating it is at a "wildcard level"
+    ///
+    /// This means one of the node's immediate children is a wildcard.
+    static const ZoneNode::Flags WILDCARD_NODE = ZoneNode::FLAG_USER2;
+
+public:
+    /// \brief Allocate and construct \c ZoneData.
+    ///
+    /// \throw std::bad_alloc Memory allocation fails.
+    ///
+    /// \param mem_sgmt A \c MemorySegment from which memory for the new
+    /// \c ZoneData is allocated.
+    /// \param name The zone name.
+    static ZoneData* create(util::MemorySegment& mem_sgmt,
+                            const dns::Name& zone_name);
+
+    /// \brief Destruct and deallocate \c ZoneData.
+    ///
+    /// It releases all resource allocated in the internal storage NSEC3 for
+    /// zone names and RdataSet objects, and if associated, the \c NSEC3Data.
+    /// It assumes \c RdataSets objects stored in the space and the
+    /// associated \c NSEC3Data object were allocated using the same memory
+    /// segment as \c mem_sgmt.  The caller must ensure this assumption.
+    ///
+    /// Note that an \c RRClass object must be passed to this method.
+    /// It's used to destroy the stored \c RdataSet objects
+    /// (see its class description).  This class doesn't hold this information;
+    /// it's the caller's responsibility to associate a \c ZoneData class
+    /// object with its expected RR class, and pass it to \c destroy().
+    ///
+    /// \throw none
+    ///
+    /// \param mem_sgmt The \c MemorySegment that allocated memory for
+    /// \c zone_data.
+    /// \param zone_data A non-NULL pointer to a valid ZoneData object
+    /// that was originally created by the \c create() method (the behavior
+    /// is undefined if this condition isn't met).
+    /// \param zone_class The RR class of the \c RdataSet stored in the
+    /// internal tree.
+    static void destroy(util::MemorySegment& mem_sgmt, ZoneData* zone_data,
+                        dns::RRClass zone_class);
+
+    ///
+    /// \name Getter methods
+    ///
+    //@{
+    /// \brief Return zone's origin node.
+    ///
+    /// This is a convenience and efficient short cut to get access to the
+    /// zone origin in the form of \c ZoneNode object.
+    ///
+    /// The class encapsulation ensures that the origin node always exists at
+    /// the same address, so this method always returns a non-NULL valid
+    /// valid pointer.
+    ///
+    /// \throw none
+    const ZoneNode* getOriginNode() const {
+        return (origin_node_.get());
+    }
+
+    /// \brief Return the zone's name space in the form of \c ZoneTree
+    ///
+    /// \note It's intentional that non-const version of this method
+    /// isn't provided.  See the class description.
+    ///
+    /// \throw none
+    const ZoneTree& getZoneTree() const { return (*zone_tree_); }
+
+    /// \brief Return whether or not the zone is signed in terms of DNSSEC.
+    ///
+    /// Note that this class does not care about what "signed" means.
+    /// This method simply returns the last value set by \c setSigned()
+    /// (or the default, which is \c false).  The caller is expected to
+    /// use this method and \c setSigned() in a reasonable, consistent way.
+    ///
+    /// \throw none
+    bool isSigned() const { return (origin_node_->getFlag(DNSSEC_SIGNED)); }
+
+    /// \brief Return whether or not the zone is signed with NSEC3 RRs.
+    ///
+    /// In the current implementation, the zone is considered signed with
+    /// NSEC3 if and only if it has non-NULL NSEC3 data.
+    ///
+    /// This also means it's not considered NSEC3 signed by default.
+    ///
+    /// \throw none
+    bool isNSEC3Signed() const { return (nsec3_data_); }
+
+    /// \brief Return NSEC3Data of the zone.
+    ///
+    /// This method returns non-NULL valid pointer to \c NSEC3Data object
+    /// associated to the \c ZoneData if it was set by \c setNSEC3Data();
+    /// otherwise it returns NULL.
+    ///
+    /// \throw none
+    const NSEC3Data* getNSEC3Data() const { return (nsec3_data_.get()); }
+    //@}
+
+    ///
+    /// \name Methods for modifying the tree
+    ///
+    //@{
+    /// \brief Insert a name to the zone.
+    ///
+    /// It allocates resource for the given name in the internal storage
+    /// for zone data, and returns an access point to it in the form of
+    /// \c ZoneNode pointer via the given \c node variable.  If the name
+    /// already exists in the name space, it returns a pointer to the existing
+    /// node.
+    ///
+    /// The name to be inserted by this method is expected to belong to
+    /// zone's "normal" (i.e., non-NSEÇ3) name space.  If it's a name for
+    /// an NSEC3 RR, it must be set in the corresponding \c NSEC3Data for
+    /// this zone data (if it doesn't exist it must be created and set
+    /// by \c setNSEC3Data()).
+    ///
+    /// The name is also expected to be a subdomain of, or equal to the
+    /// zone's origin name (specified on creation in \c create()), but
+    /// this method does not check that condition.  The caller is responsible
+    /// for ensuring this assumption.
+    ///
+    /// Since this method doesn't perform any semantics check, it always
+    /// succeeds (except for the rare case where memory allocation
+    /// fails) and \c node will be set to a valid pointer.
+    ///
+    /// \note We may want to differentiate between the case where the name is
+    /// newly created and the case where it already existed.  Right now it's
+    /// unclear, so it doesn't return this information.  If we see the need
+    /// for it, this method can be extended that way.
+    ///
+    /// \throw std::bad_alloc Memory allocation fails
+    ///
+    /// \param mem_sgmt Memory segment in which resource for the new memory
+    /// is to be allocated.
+    /// \param name The name to be inserted.
+    /// \param node A pointer to \c ZoneNode pointer in which the created or
+    /// found node for the name is stored.  Must not be NULL (the method does
+    /// not check that condition).
+    void insertName(util::MemorySegment& mem_sgmt, const dns::Name& name,
+                    ZoneNode** node);
+
+    /// \brief Specify whether or not the zone is signed in terms of DNSSEC.
+    ///
+    /// The zone will be considered "signed" (in that subsequent calls to
+    /// \c isSigned() will return \c true) iff the parameter \c on is \c true.
+    ///
+    /// This class does not care what "signed" actually means; it does not
+    /// check any zone RRs to verify if the given state makes sense (e.g.
+    /// whether the zone has a DNSKEY RR at the origin).  The caller is
+    /// expected to use this method and \c isSigned() in a reasonable,
+    /// consistent way.
+    ///
+    /// \throw none
+    void setSigned(bool on) {
+        origin_node_->setFlag(DNSSEC_SIGNED, on);
+    }
+
+    /// \brief Return NSEC3Data of the zone, non-const version.
+    ///
+    /// This is similar to the const version, but return a non-const pointer
+    /// so the caller can modify the content.
+    ///
+    /// \throw none
+    NSEC3Data* getNSEC3Data() { return (nsec3_data_.get()); }
+
+    /// \brief Associate \c NSEC3Data to the zone.
+    ///
+    /// This method associates the given \c NSEC3Data object with the zone
+    /// data.  If there was already associated \c NSEC3Data object, it will
+    /// be returned.  If no \c NSEC3Data object was associated before,
+    /// a NULL pointer will be returned.  \c nsec3_data can be NULL, in which
+    /// case the zone will be disassociated with a \c NSEC3Data.
+    ///
+    /// In general, if a non-NULL pointer is passed, it's assumed that
+    /// the \c NSEC3Data object was allocated in the same \c MemorySegment
+    /// as that for the zone data, so the \c destroy() method can destroy
+    /// both with the same memory segment.  If this condition is not met,
+    /// the caller must extract the associated \c NSEC3Data by calling
+    /// this method with NULL and release any resource for it by itself
+    /// before destroying this zone data.
+    ///
+    /// \throw none
+    ///
+    /// \param nsec3_data A pointer to \c NSEC3Data object to be associated
+    /// with the zone.  Can be NULL.
+    /// \return Previously associated \c NSEC3Data object in the zone.  This
+    /// can be NULL.
+    NSEC3Data* setNSEC3Data(NSEC3Data* nsec3_data) {
+        NSEC3Data* old = nsec3_data_.get();
+        nsec3_data_ = nsec3_data;
+        return (old);
+    }
+    //@}
+
+private:
+    const boost::interprocess::offset_ptr<ZoneTree> zone_tree_;
+    const boost::interprocess::offset_ptr<ZoneNode> origin_node_;
+    boost::interprocess::offset_ptr<NSEC3Data> nsec3_data_;
+};
+
 } // namespace memory
 } // namespace datasrc
 } // namespace isc
diff --git a/src/lib/datasrc/memory/zone_table.cc b/src/lib/datasrc/memory/zone_table.cc
index 9c410d0..f9b2768 100644
--- a/src/lib/datasrc/memory/zone_table.cc
+++ b/src/lib/datasrc/memory/zone_table.cc
@@ -19,6 +19,10 @@
 #include <datasrc/memory/zone_data.h>
 #include <datasrc/memory/zone_table.h>
 #include <datasrc/memory/domaintree.h>
+#include <datasrc/memory/segment_object_holder.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
 
 #include <cassert>
 
@@ -28,43 +32,25 @@ using namespace isc::dns;
 namespace isc {
 namespace datasrc {
 namespace memory {
-namespace {
-// A simple holder to create and use some objects in this implementation
-// in an exception safe manner.   It works like std::auto_ptr but much
-// more simplified.
-template <typename T>
-class Holder {
-public:
-    Holder(util::MemorySegment& mem_sgmt, T* obj) :
-        mem_sgmt_(mem_sgmt), obj_(obj)
-    {}
-    ~Holder() {
-        if (obj_ != NULL) {
-            T::destroy(mem_sgmt_, obj_);
-        }
-    }
-    T* get() { return (obj_); }
-    T* release() {
-        T* ret = obj_;
-        obj_ = NULL;
-        return (ret);
-    }
-private:
-    util::MemorySegment& mem_sgmt_;
-    T* obj_;
-};
-}
+using detail::SegmentObjectHolder;
 
+namespace {
 void
-ZoneTable::ZoneDataDeleter::operator()(util::MemorySegment& mem_sgmt,
-                                       ZoneData* zone_data) const
+deleteZoneData(util::MemorySegment* mem_sgmt, ZoneData* zone_data,
+               RRClass rrclass)
 {
-    ZoneData::destroy(mem_sgmt, zone_data);
+    if (zone_data != NULL) {
+        ZoneData::destroy(*mem_sgmt, zone_data, rrclass);
+    }
+}
+typedef boost::function<void(ZoneData*)> ZoneDataDeleterType;
 }
 
 ZoneTable*
-ZoneTable::create(util::MemorySegment& mem_sgmt) {
-    Holder<ZoneTableTree> holder(mem_sgmt, ZoneTableTree::create(mem_sgmt));
+ZoneTable::create(util::MemorySegment& mem_sgmt, RRClass zone_class) {
+    SegmentObjectHolder<ZoneTableTree, ZoneDataDeleterType> holder(
+        mem_sgmt, ZoneTableTree::create(mem_sgmt),
+        boost::bind(deleteZoneData, &mem_sgmt, _1, zone_class));
     void* p = mem_sgmt.allocate(sizeof(ZoneTable));
     ZoneTable* zone_table = new(p) ZoneTable(holder.get());
     holder.release();
@@ -72,20 +58,27 @@ ZoneTable::create(util::MemorySegment& mem_sgmt) {
 }
 
 void
-ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable) {
-    ZoneTableTree::destroy(mem_sgmt, ztable->zones_.get());
+ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable,
+                   RRClass zone_class)
+{
+    ZoneTableTree::destroy(mem_sgmt, ztable->zones_.get(),
+                           boost::bind(deleteZoneData, &mem_sgmt, _1,
+                                       zone_class));
     mem_sgmt.deallocate(ztable, sizeof(ZoneTable));
 }
 
 ZoneTable::AddResult
-ZoneTable::addZone(util::MemorySegment& mem_sgmt, const Name& zone_name) {
+ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class,
+                   const Name& zone_name)
+{
     // Create a new ZoneData instance first.  If the specified name already
     // exists in the table, the new data will soon be destroyed, but we want
     // to make sure if this allocation fails the tree won't be changed to
     // provide as strong guarantee as possible.  In practice, we generally
     // expect the caller tries to add a zone only when it's a new one, so
     // this should be a minor concern.
-    Holder<ZoneData> holder(mem_sgmt, ZoneData::create(mem_sgmt));
+    SegmentObjectHolder<ZoneData, RRClass> holder(
+        mem_sgmt, ZoneData::create(mem_sgmt, zone_name), zone_class);
 
     // Get the node where we put the zone
     ZoneTableNode* node(NULL);
@@ -103,7 +96,7 @@ ZoneTable::addZone(util::MemorySegment& mem_sgmt, const Name& zone_name) {
 
     // Is it empty? We either just created it or it might be nonterminal
     if (node->isEmpty()) {
-        node->setData(mem_sgmt, holder.get());
+        node->setData(holder.get());
         return (AddResult(result::SUCCESS, holder.release()));
     } else { // There's something there already
         return (AddResult(result::EXIST, node->getData()));
diff --git a/src/lib/datasrc/memory/zone_table.h b/src/lib/datasrc/memory/zone_table.h
index b5da957..2faf606 100644
--- a/src/lib/datasrc/memory/zone_table.h
+++ b/src/lib/datasrc/memory/zone_table.h
@@ -17,6 +17,8 @@
 
 #include <util/memory_segment.h>
 
+#include <dns/rrclass.h>
+
 #include <datasrc/result.h>
 #include <datasrc/memory/domaintree.h>
 
@@ -68,8 +70,8 @@ private:
     };
 
     // Type aliases to make it shorter
-    typedef DomainTree<ZoneData, ZoneDataDeleter> ZoneTableTree;
-    typedef DomainTreeNode<ZoneData, ZoneDataDeleter> ZoneTableNode;
+    typedef DomainTree<ZoneData> ZoneTableTree;
+    typedef DomainTreeNode<ZoneData> ZoneTableNode;
 
 public:
     /// \brief Result data of addZone() method.
@@ -114,18 +116,27 @@ public:
     ///
     /// \param mem_sgmt A \c MemorySegment from which memory for the new
     /// \c ZoneTable is allocated.
-    static ZoneTable* create(util::MemorySegment& mem_sgmt);
+    /// \param zone_class The RR class of the zone.  It must be the RR class
+    /// that is supposed to be associated to the zone table.
+    static ZoneTable* create(util::MemorySegment& mem_sgmt,
+                             dns::RRClass zone_class);
 
     /// \brief Destruct and deallocate \c ZoneTable
     ///
+    /// This method releases all internal resources including all zone data
+    /// created via \c addZone() calls.
+    ///
     /// \throw none
     ///
     /// \param mem_sgmt The \c MemorySegment that allocated memory for
-    /// \c ztable.
+    /// \c ztable and used for prior calls to \c addZone().
+    /// \param zone_class The RR class of the zone.  It must be the RR class
+    /// that is supposed to be associated to the zone table.
     /// \param ztable A non NULL pointer to a valid \c ZoneTable object
     /// that was originally created by the \c create() method (the behavior
     /// is undefined if this condition isn't met).
-    static void destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable);
+    static void destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable,
+                        dns::RRClass zone_class);
 
     /// Add a new zone to the \c ZoneTable.
     ///
@@ -135,14 +146,23 @@ public:
     /// zone name already exists in the table, a new data object won't be
     /// created; instead, the existing corresponding data will be returned.
     ///
+    /// The zone table keeps the ownership of the created zone data; the
+    /// caller must not try to destroy it directly.  (We'll eventually
+    /// add an interface to delete specific zone data from the table).
+    ///
     /// \throw std::bad_alloc Internal resource allocation fails.
     ///
+    /// \param mem_sgmt The \c MemorySegment to allocate zone data to be
+    /// created.  It must be the same segment that was used to create
+    /// the zone table at the time of create().
     /// \param zone_name The name of the zone to be added.
+    /// \param zone_class The RR class of the zone.  It must be the RR class
+    /// that is supposed to be associated to the zone table.
     /// \return \c result::SUCCESS If the zone is successfully
     /// added to the zone table.
     /// \return \c result::EXIST The zone table already contains
     /// zone of the same origin.
-    AddResult addZone(util::MemorySegment& mem_sgmt,
+    AddResult addZone(util::MemorySegment& mem_sgmt, dns::RRClass zone_class,
                       const dns::Name& zone_name);
 
     /// Find a zone that best matches the given name in the \c ZoneTable.
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index c6f15fb..e38a487 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -738,7 +738,24 @@ prepareRRset(const Name& name, const ConstRBNodeRRsetPtr& rrset, bool rename,
         rrset->copyAdditionalNodes(*result);
         return (result);
     } else {
-        return (rrset);
+        ConstRRsetPtr sig_rrset = rrset->getRRsig();
+        if (sig_rrset &&
+            ((options & ZoneFinder::FIND_DNSSEC) == 0)) {
+            RRsetPtr result_base(new RRset(name, rrset->getClass(),
+                                           rrset->getType(),
+                                           rrset->getTTL()));
+            for (RdataIteratorPtr i(rrset->getRdataIterator());
+                 !i->isLast();
+                 i->next()) {
+                result_base->addRdata(i->getCurrent());
+            }
+
+            RBNodeRRsetPtr result(new RBNodeRRset(result_base));
+            rrset->copyAdditionalNodes(*result);
+            return (result);
+        } else {
+            return (rrset);
+        }
     }
 }
 
@@ -795,10 +812,10 @@ protected:
             }
             BOOST_FOREACH(const DomainPair& dom_it, *found_node_->getData()) {
                 getAdditionalForRRset(*dom_it.second, requested_types,
-                                      result);
+                                      result, options_);
             }
         } else {
-            getAdditionalForRRset(*rrset_, requested_types, result);
+            getAdditionalForRRset(*rrset_, requested_types, result, options_);
         }
     }
 
@@ -809,7 +826,8 @@ private:
     // type for each node.
     static void getAdditionalForRRset(const RBNodeRRset& rrset,
                                       const vector<RRType>& requested_types,
-                                      vector<ConstRRsetPtr>& result)
+                                      vector<ConstRRsetPtr>& result,
+                                      ZoneFinder::FindOptions options)
     {
         const vector<AdditionalNodeInfo>* additionals_ =
             rrset.getAdditionalNodes();
@@ -836,11 +854,13 @@ private:
                     // in case the caller has the same RRset but as a result
                     // of normal find() and needs to know they are of the same
                     // kind; otherwise we simply use the stored RBNodeRRset.
+                    ConstRRsetPtr rp;
                     if (wild_expanded) {
-                        result.push_back(found->second->getUnderlyingRRset());
+                        rp = found->second->getUnderlyingRRset();
                     } else {
-                        result.push_back(found->second);
+                        rp = found->second;
                     }
+                    result.push_back(ZoneFinder::stripRRsigs(rp, options));
                 }
             }
         }
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index ba21de8..457d578 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -474,8 +474,8 @@ private:
 
 // return db version
 pair<int, int>
-createDatabase(sqlite3* db) {
-    logger.info(DATASRC_SQLITE_SETUP);
+createDatabase(sqlite3* db, const std::string& name) {
+    logger.warn(DATASRC_SQLITE_SETUP).arg(name);
 
     // try to get an exclusive lock. Once that is obtained, do the version
     // check *again*, just in case this process was racing another
@@ -501,12 +501,12 @@ createDatabase(sqlite3* db) {
 }
 
 void
-checkAndSetupSchema(Initializer* initializer) {
+checkAndSetupSchema(Initializer* initializer, const std::string& name) {
     sqlite3* const db = initializer->params_.db_;
 
     pair<int, int> schema_version = checkSchemaVersion(db);
     if (schema_version.first == -1) {
-        schema_version = createDatabase(db);
+        schema_version = createDatabase(db, name);
     } else if (schema_version.first != SQLITE_SCHEMA_MAJOR_VERSION) {
         LOG_ERROR(logger, DATASRC_SQLITE_INCOMPATIBLE_VERSION)
             .arg(schema_version.first).arg(schema_version.second)
@@ -540,7 +540,7 @@ SQLite3Accessor::open(const std::string& name) {
         isc_throw(SQLite3Error, "Cannot open SQLite database file: " << name);
     }
 
-    checkAndSetupSchema(&initializer);
+    checkAndSetupSchema(&initializer, name);
     initializer.move(dbparameters_.get());
 }
 
diff --git a/src/lib/datasrc/sqlite3_datasrc.cc b/src/lib/datasrc/sqlite3_datasrc.cc
index b450cd5..bced1ae 100644
--- a/src/lib/datasrc/sqlite3_datasrc.cc
+++ b/src/lib/datasrc/sqlite3_datasrc.cc
@@ -788,7 +788,7 @@ private:
 
 // return db version
 pair<int, int> create_database(sqlite3* db) {
-    logger.info(DATASRC_SQLITE_SETUP);
+    logger.info(DATASRC_SQLITE_SETUP_OLD_API);
 
     // try to get an exclusive lock. Once that is obtained, do the version
     // check *again*, just in case this process was racing another
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index 027ec6e..f3be2e1 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -46,17 +46,21 @@ common_ldadd += $(GTEST_LDADD) $(SQLITE_LIBS)
 
 # The general tests
 run_unittests_SOURCES = $(common_sources)
-run_unittests_SOURCES += datasrc_unittest.cc
-run_unittests_SOURCES += static_unittest.cc
-run_unittests_SOURCES += query_unittest.cc
-run_unittests_SOURCES += cache_unittest.cc
+
+# Commented out by ticket #2165. If you re-enable these, please modify
+# EXTRA_DIST at the bottom of this file.
+#run_unittests_SOURCES += datasrc_unittest.cc
+#run_unittests_SOURCES += static_unittest.cc
+#run_unittests_SOURCES += query_unittest.cc
+#run_unittests_SOURCES += cache_unittest.cc
+#run_unittests_SOURCES += sqlite3_unittest.cc
+#run_unittests_SOURCES += test_datasrc.h test_datasrc.cc
+
 run_unittests_SOURCES += test_client.h test_client.cc
-run_unittests_SOURCES += test_datasrc.h test_datasrc.cc
 run_unittests_SOURCES += rbtree_unittest.cc
 run_unittests_SOURCES += logger_unittest.cc
 run_unittests_SOURCES += client_unittest.cc
 run_unittests_SOURCES += database_unittest.cc
-run_unittests_SOURCES += sqlite3_unittest.cc
 run_unittests_SOURCES += sqlite3_accessor_unittest.cc
 run_unittests_SOURCES += memory_datasrc_unittest.cc
 run_unittests_SOURCES += rbnode_rrset_unittest.cc
@@ -118,3 +122,11 @@ EXTRA_DIST += testdata/new_minor_schema.sqlite3
 EXTRA_DIST += testdata/newschema.sqlite3
 EXTRA_DIST += testdata/oldschema.sqlite3
 EXTRA_DIST += testdata/static.zone
+
+# Added by ticket #2165
+EXTRA_DIST += datasrc_unittest.cc
+EXTRA_DIST += static_unittest.cc
+EXTRA_DIST += query_unittest.cc
+EXTRA_DIST += cache_unittest.cc
+EXTRA_DIST += sqlite3_unittest.cc
+EXTRA_DIST += test_datasrc.h test_datasrc.cc
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index 5f18283..56ee5dd 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -1711,17 +1711,22 @@ doFindTest(ZoneFinder& finder,
         checkRRset(result->rrset, expected_name != Name(".") ? expected_name :
                    name, finder.getClass(), expected_type, expected_ttl,
                    expected_rdatas);
-
-        if (!expected_sig_rdatas.empty() && result->rrset->getRRsig()) {
-            checkRRset(result->rrset->getRRsig(), expected_name != Name(".") ?
-                       expected_name : name, finder.getClass(),
-                       isc::dns::RRType::RRSIG(), expected_ttl,
-                       expected_sig_rdatas);
-        } else if (expected_sig_rdatas.empty()) {
+        if ((options & ZoneFinder::FIND_DNSSEC) == ZoneFinder::FIND_DNSSEC) {
+            if (!expected_sig_rdatas.empty() && result->rrset->getRRsig()) {
+                checkRRset(result->rrset->getRRsig(),
+                           expected_name != Name(".") ? expected_name : name,
+                           finder.getClass(),
+                           isc::dns::RRType::RRSIG(), expected_ttl,
+                           expected_sig_rdatas);
+            } else if (expected_sig_rdatas.empty()) {
+                EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset->getRRsig()) <<
+                    "Unexpected RRSIG: " << result->rrset->getRRsig()->toText();
+            } else {
+                ADD_FAILURE() << "Missing RRSIG";
+            }
+        } else if (result->rrset->getRRsig()) {
             EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset->getRRsig()) <<
                 "Unexpected RRSIG: " << result->rrset->getRRsig()->toText();
-        } else {
-            ADD_FAILURE() << "Missing RRSIG";
         }
     } else if (expected_rdatas.empty()) {
         EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset) <<
@@ -2842,6 +2847,35 @@ TYPED_TEST(DatabaseClientTest, anyFromFind) {
                                          RRType::ANY()), isc::Unexpected);
 }
 
+TYPED_TEST(DatabaseClientTest, findRRSIGsWithoutDNSSEC) {
+    // Trying to find RRSIG records directly should work even if
+    // FIND_DNSSEC flag is not specified.
+
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    ConstZoneFinderContextPtr result =
+        finder->find(isc::dns::Name("signed1.example.org."), RRType::RRSIG());
+
+    EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+
+    std::vector<std::string> expected_rdata;
+    expected_rdata.push_back(TEST_RECORDS[10][4]);
+    expected_rdata.push_back(TEST_RECORDS[11][4]);
+    expected_rdata.push_back(TEST_RECORDS[14][4]);
+
+    RdataIteratorPtr it(result->rrset->getRdataIterator());
+    std::vector<std::string> rdata;
+    while (!it->isLast()) {
+        rdata.push_back(it->getCurrent().toText());
+        it->next();
+    }
+    std::sort(rdata.begin(), rdata.end());
+    std::sort(expected_rdata.begin(), expected_rdata.end());
+    ASSERT_EQ(expected_rdata.size(), rdata.size());
+    for (size_t i(0); i < expected_rdata.size(); ++ i) {
+        EXPECT_EQ(expected_rdata[i], rdata[i]);
+    }
+}
+
 // Test the findAll method.
 TYPED_TEST(DatabaseClientTest, getAll) {
     // The domain doesn't exist, so we must get the right NSEC
@@ -3636,9 +3670,11 @@ TYPED_TEST(DatabaseClientTest, compoundUpdate) {
 TYPED_TEST(DatabaseClientTest, invalidRdata) {
     boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
-    EXPECT_THROW(finder->find(Name("invalidrdata.example.org."), RRType::A()),
+    EXPECT_THROW(finder->find(Name("invalidrdata.example.org."),
+                              RRType::A()),
                  DataSourceError);
-    EXPECT_THROW(finder->find(Name("invalidrdata2.example.org."), RRType::A()),
+    EXPECT_THROW(finder->find(Name("invalidrdata2.example.org."),
+                              RRType::A(), ZoneFinder::FIND_DNSSEC),
                  DataSourceError);
 }
 
@@ -4053,7 +4089,7 @@ TYPED_TEST(DatabaseClientTest, findNSEC3) {
     this->current_accessor_->enableNSEC3();
 
     // The rest is in the function, it is shared with in-memory tests
-    performNSEC3Test(*finder);
+    performNSEC3Test(*finder, true);
 }
 
 }
diff --git a/src/lib/datasrc/tests/faked_nsec3.cc b/src/lib/datasrc/tests/faked_nsec3.cc
index 1e37b8e..b702d28 100644
--- a/src/lib/datasrc/tests/faked_nsec3.cc
+++ b/src/lib/datasrc/tests/faked_nsec3.cc
@@ -34,6 +34,11 @@ const char* const nsec3_common = " 300 IN NSEC3 1 1 12 aabbccdd "
     "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG";
 const char* const nsec3_rrsig_common = " 300 IN RRSIG NSEC3 5 3 3600 "
     "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
+const char* const nsec3_rrsig_common2 = " 300 IN RRSIG NSEC3 5 4 7200 "
+    "20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6k"
+    "OPpvK7HOGBFZGbalN5ZKmInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/Lis2M6yUWH"
+    "yXbNbj/QqwqgadG5dhxTArfuR02xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o8g"
+    "HSY5vYTtothcZQa4BMKhmGQEk=";
 const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
 const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
 const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
@@ -93,8 +98,7 @@ void
 findNSEC3Check(bool expected_matched, uint8_t expected_labels,
                const string& expected_closest,
                const string& expected_next,
-               const ZoneFinder::FindNSEC3Result& result,
-               bool expected_sig)
+               const ZoneFinder::FindNSEC3Result& result)
 {
     EXPECT_EQ(expected_matched, result.matched);
     // Convert to int so the error messages would be more readable:
@@ -104,9 +108,6 @@ findNSEC3Check(bool expected_matched, uint8_t expected_labels,
     vector<ConstRRsetPtr> actual_rrsets;
     ASSERT_TRUE(result.closest_proof);
     actual_rrsets.push_back(result.closest_proof);
-    if (expected_sig) {
-        actual_rrsets.push_back(result.closest_proof->getRRsig());
-    }
     rrsetsCheck(expected_closest, actual_rrsets.begin(),
                 actual_rrsets.end());
 
@@ -116,30 +117,38 @@ findNSEC3Check(bool expected_matched, uint8_t expected_labels,
     } else {
         ASSERT_TRUE(result.next_proof);
         actual_rrsets.push_back(result.next_proof);
-        if (expected_sig) {
-            actual_rrsets.push_back(result.next_proof->getRRsig());
-        }
         rrsetsCheck(expected_next, actual_rrsets.begin(),
                     actual_rrsets.end());
     }
 }
 
 void
-performNSEC3Test(ZoneFinder &finder) {
+performNSEC3Test(ZoneFinder &finder, bool rrsigs_exist) {
     // Parameter validation: the query name must be in or below the zone
     EXPECT_THROW(finder.findNSEC3(Name("example.com"), false), OutOfZone);
     EXPECT_THROW(finder.findNSEC3(Name("org"), true), OutOfZone);
 
     const Name origin("example.org");
-    const string apex_nsec3_text = string(apex_hash) + ".example.org." +
+    string apex_nsec3_text = string(apex_hash) + ".example.org." +
         string(nsec3_common);
-    const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
+    string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
         string(nsec3_common);
-    const string w_nsec3_text = string(w_hash) + ".example.org." +
+    string w_nsec3_text = string(w_hash) + ".example.org." +
         string(nsec3_common);
-    const string zzz_nsec3_text = string(zzz_hash) + ".example.org." +
+    string zzz_nsec3_text = string(zzz_hash) + ".example.org." +
         string(nsec3_common);
 
+    if (rrsigs_exist) {
+        apex_nsec3_text += "\n" + string(apex_hash) + ".example.org." +
+            string(nsec3_rrsig_common2);
+        ns1_nsec3_text += "\n" + string(ns1_hash) + ".example.org." +
+            string(nsec3_rrsig_common2);
+        w_nsec3_text += "\n" + string(w_hash) + ".example.org." +
+            string(nsec3_rrsig_common2);
+        zzz_nsec3_text += "\n" + string(zzz_hash) + ".example.org." +
+            string(nsec3_rrsig_common2);
+    }
+
     // Apex name.  It should have a matching NSEC3.
     {
         SCOPED_TRACE("apex, non recursive mode");
diff --git a/src/lib/datasrc/tests/faked_nsec3.h b/src/lib/datasrc/tests/faked_nsec3.h
index 10d9444..8c1857c 100644
--- a/src/lib/datasrc/tests/faked_nsec3.h
+++ b/src/lib/datasrc/tests/faked_nsec3.h
@@ -69,13 +69,12 @@ void
 findNSEC3Check(bool expected_matched, uint8_t expected_labels,
                const std::string& expected_closest,
                const std::string& expected_next,
-               const isc::datasrc::ZoneFinder::FindNSEC3Result& result,
-               bool expected_sig = false);
+               const isc::datasrc::ZoneFinder::FindNSEC3Result& result);
 
 // Perform the shared part of NSEC3 test (shared between in-memory and database
 // tests).
 void
-performNSEC3Test(ZoneFinder &finder);
+performNSEC3Test(ZoneFinder &finder, bool rrsigs_exist = false);
 
 }
 }
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index 580f7ff..5abe270 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -1263,12 +1263,14 @@ TEST_F(InMemoryZoneFinderTest, loadFromIterator) {
     RRsetPtr expected_answer = textToRRset(soa_txt, RRClass::IN(), origin_);
     expected_answer->addRRsig(textToRRset(soa_sig_txt));
     findTest(origin_, RRType::SOA(), ZoneFinder::SUCCESS, true,
-             expected_answer);
+             expected_answer, ZoneFinder::RESULT_DEFAULT, NULL,
+             ZoneFinder::FIND_DNSSEC);
 
     expected_answer = textToRRset(a_txt);
     expected_answer->addRRsig(textToRRset(a_sig_txt));
     findTest(Name("ns1.example.org"), RRType::A(), ZoneFinder::SUCCESS, true,
-             expected_answer);
+             expected_answer, ZoneFinder::RESULT_DEFAULT, NULL,
+             ZoneFinder::FIND_DNSSEC);
 
     // File name should be (re)set to empty.
     EXPECT_TRUE(zone_finder_.getFileName().empty());
@@ -2065,7 +2067,7 @@ TEST_F(InMemoryZoneFinderTest, addNSEC3WithRRSIG) {
     // Then look for it.  The NSEC3 should have the RRSIG that was just added.
     findNSEC3Check(true, origin_.getLabelCount(),
                    nsec3_text + "\n" + nsec3_rrsig_text, "",
-                   zone_finder_.findNSEC3(Name("example.org"), false), true);
+                   zone_finder_.findNSEC3(Name("example.org"), false));
 
     // Duplicate add of RRSIG for the same NSEC3 is prohibited.
     EXPECT_THROW(zone_finder_.add(textToRRset(nsec3_rrsig_text)),
diff --git a/src/lib/datasrc/tests/zone_finder_context_unittest.cc b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
index b3c9b2d..ea02543 100644
--- a/src/lib/datasrc/tests/zone_finder_context_unittest.cc
+++ b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
@@ -326,7 +326,11 @@ TEST_P(ZoneFinderContextTest, getAdditionalWithSIG) {
 
     ctx->getAdditional(REQUESTED_BOTH, result_sets_);
     rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+                "ns1.example.org. 3600 IN RRSIG	A 7 3 3600 20150420235959 "
+                "20051021000000 40430 example.org. FAKEFAKE\n"
                 "ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+                "ns1.example.org. 3600 IN RRSIG	AAAA 7 3 3600 20150420235959 "
+                "20051021000000 40430 example.org. FAKEFAKEFAKE\n"
                 "ns2.example.org. 3600 IN A 192.0.2.2\n",
                 result_sets_.begin(), result_sets_.end());
 
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index 094529a..2330412 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -134,6 +134,11 @@ protected:
     };
 
 public:
+    /// \brief A helper function to strip RRSIGs when FIND_DNSSEC is not
+    /// requested.
+    static isc::dns::ConstRRsetPtr
+    stripRRsigs(isc::dns::ConstRRsetPtr rp, const FindOptions options);
+
     /// \brief Context of the result of a find() call.
     ///
     /// This class encapsulates results and (possibly) associated context
@@ -299,7 +304,9 @@ public:
     private:
         ZoneFinder& finder_;
         const FindResultFlags flags_;
+    protected:
         const FindOptions options_;
+    private:
         std::vector<isc::dns::ConstRRsetPtr> all_set_;
     };
 
diff --git a/src/lib/datasrc/zone_finder.cc b/src/lib/datasrc/zone_finder.cc
new file mode 100644
index 0000000..562b43f
--- /dev/null
+++ b/src/lib/datasrc/zone_finder.cc
@@ -0,0 +1,55 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/rdata.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+#include <dns/rdataclass.h>
+
+#include <datasrc/zone.h>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace datasrc {
+
+isc::dns::ConstRRsetPtr
+ZoneFinder::stripRRsigs(isc::dns::ConstRRsetPtr rp,
+                        const FindOptions options) {
+    if (rp) {
+        isc::dns::ConstRRsetPtr sig_rrset = rp->getRRsig();
+        if (sig_rrset &&
+            ((options & ZoneFinder::FIND_DNSSEC) == 0)) {
+            isc::dns::RRsetPtr result_base
+                (new isc::dns::RRset(rp->getName(),
+                                     rp->getClass(),
+                                     rp->getType(),
+                                     rp->getTTL()));
+            for (isc::dns::RdataIteratorPtr i(rp->getRdataIterator());
+                 !i->isLast();
+                 i->next()) {
+                result_base->addRdata(i->getCurrent());
+            }
+
+            return (result_base);
+        }
+    }
+
+    return (rp);
+}
+
+} // namespace datasrc
+} // datasrc isc
diff --git a/src/lib/dns/benchmarks/message_renderer_bench.cc b/src/lib/dns/benchmarks/message_renderer_bench.cc
index 33cd65b..cc2fc88 100644
--- a/src/lib/dns/benchmarks/message_renderer_bench.cc
+++ b/src/lib/dns/benchmarks/message_renderer_bench.cc
@@ -35,21 +35,28 @@ template <typename T>
 class MessageRendererBenchMark {
 public:
     MessageRendererBenchMark(const vector<Name>& names) :
+        renderer_(NULL),
         names_(names)
     {}
+    MessageRendererBenchMark() {
+        delete renderer_;
+    }
     unsigned int run() {
-        renderer_.clear();
+        if (renderer_ == NULL) {
+            renderer_ = new T();
+        }
+        renderer_->clear();
         vector<Name>::const_iterator it = names_.begin();
         const vector<Name>::const_iterator it_end = names_.end();
         for (; it != it_end; ++it) {
-            renderer_.writeName(*it);
+            renderer_->writeName(*it);
         }
         // Make sure truncation didn't accidentally happen.
-        assert(!renderer_.isTruncated());
+        assert(!renderer_->isTruncated());
         return (names_.size());
     }
 private:
-    T renderer_;
+    T* renderer_; // It's pointer, so we won't need to copy it.
     const vector<Name>& names_;
 };
 
diff --git a/src/lib/dns/benchmarks/rdatarender_bench.cc b/src/lib/dns/benchmarks/rdatarender_bench.cc
index 368ea6a..38ee2ac 100644
--- a/src/lib/dns/benchmarks/rdatarender_bench.cc
+++ b/src/lib/dns/benchmarks/rdatarender_bench.cc
@@ -44,18 +44,25 @@ public:
     RdataRenderBenchMark(const vector<T>& dataset) :
         dataset_(dataset)
     {}
+    RdataRenderBenchMark() {
+        delete renderer_;
+    }
     unsigned int run() {
+        if (renderer_ == NULL) {
+            renderer_ = new MessageRenderer();
+        }
         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_);
+            renderer_->clear();
+            (*data)->toWire(*renderer_);
         }
         return (dataset_.size());
     }
 private:
     const vector<T>& dataset_;
-    MessageRenderer renderer_;
+    // Just-in-time initialized pointer, so no copy
+    MessageRenderer* renderer_;
 };
 
 // This supplemental class emulates an RRset like class that internally
diff --git a/src/lib/dns/labelsequence.h b/src/lib/dns/labelsequence.h
index caadd90..545ce12 100644
--- a/src/lib/dns/labelsequence.h
+++ b/src/lib/dns/labelsequence.h
@@ -229,15 +229,25 @@ public:
 
     /// \brief Compares two label sequences for equality.
     ///
-    /// Performs a (optionally case-insensitive) comparison between this
+    /// Performs a (optionally case-sensitive) comparison between this
     /// LabelSequence and another LabelSequence for equality.
     ///
     /// \param other The LabelSequence to compare with
-    /// \param case_sensitive If true, comparison is case-insensitive
+    /// \param case_sensitive If true, comparison is case-sensitive
     /// \return true if The label sequences consist are the same length,
     ///         and contain the same data.
     bool equals(const LabelSequence& other, bool case_sensitive = false) const;
 
+    /// \brief Compares two label sequences for equality (case ignored).
+    ///
+    /// This is equivalent to <code>this->equals(other)</code>.
+    ///
+    /// The operator version is convenient some specific cases such as in
+    /// unit tests.
+    bool operator==(const LabelSequence& other) const {
+        return (equals(other));
+    }
+
     /// \brief Compares two label sequences.
     ///
     /// Performs a (optionally case-insensitive) comparison between this
diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc
index 80b0df7..1cb1ea8 100644
--- a/src/lib/dns/message.cc
+++ b/src/lib/dns/message.cc
@@ -493,7 +493,7 @@ Message::getRRCount(const Section section) const {
 }
 
 void
-Message::addRRset(const Section section, RRsetPtr rrset, const bool sign) {
+Message::addRRset(const Section section, RRsetPtr rrset) {
     if (!rrset) {
         isc_throw(InvalidParameter,
                   "NULL RRset is given to Message::addRRset");
@@ -508,12 +508,7 @@ Message::addRRset(const Section section, RRsetPtr rrset, const bool sign) {
 
     impl_->rrsets_[section].push_back(rrset);
     impl_->counts_[section] += rrset->getRdataCount();
-
-    RRsetPtr sp = rrset->getRRsig();
-    if (sign && sp != NULL) {
-        impl_->rrsets_[section].push_back(sp);
-        impl_->counts_[section] += sp->getRdataCount();
-    }
+    impl_->counts_[section] += rrset->getRRsigDataCount();
 }
 
 bool
@@ -555,6 +550,7 @@ Message::removeRRset(const Section section, RRsetIterator& iterator) {
 
             // Found the matching RRset so remove it & ignore rest
             impl_->counts_[section] -= (*iterator)->getRdataCount();
+            impl_->counts_[section] -= (*iterator)->getRRsigDataCount();
             impl_->rrsets_[section].erase(i);
             removed = true;
             break;
diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h
index 73d0c6e..3b80357 100644
--- a/src/lib/dns/message.h
+++ b/src/lib/dns/message.h
@@ -459,9 +459,6 @@ public:
     /// \brief Add a (pointer like object of) RRset to the given section
     /// of the message.
     ///
-    /// This interface takes into account the RRSIG possibly attached to
-    /// \c rrset.  This interface design needs to be revisited later.
-    ///
     /// Note that \c addRRset() does not currently check for duplicate
     /// data before inserting RRsets.  The caller is responsible for
     /// checking for these (see \c hasRRset() below).
@@ -473,9 +470,7 @@ public:
     ///
     /// \param section The message section to which the rrset is to be added
     /// \param rrset The rrset to be added.  Must not be NULL.
-    /// \param sign If true, and if \c rrset has associated RRSIGs, the
-    /// RRSIGs will also be added to the same section of the message.
-    void addRRset(const Section section, RRsetPtr rrset, bool sign = false);
+    void addRRset(const Section section, RRsetPtr rrset);
 
     /// \brief Determine whether the given section already has an RRset
     /// matching the given name, RR class and RR type.
diff --git a/src/lib/dns/messagerenderer.h b/src/lib/dns/messagerenderer.h
index 5a81eb2..d8f4634 100644
--- a/src/lib/dns/messagerenderer.h
+++ b/src/lib/dns/messagerenderer.h
@@ -17,6 +17,8 @@
 
 #include <util/buffer.h>
 
+#include <boost/noncopyable.hpp>
+
 namespace isc {
 
 namespace dns {
@@ -346,7 +348,8 @@ public:
 /// 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 {
+class MessageRenderer : public AbstractMessageRenderer,
+    public boost::noncopyable { // Can crash if copied
 public:
     using AbstractMessageRenderer::CASE_INSENSITIVE;
     using AbstractMessageRenderer::CASE_SENSITIVE;
diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc
index f08f62c..4dfee24 100644
--- a/src/lib/dns/python/message_python.cc
+++ b/src/lib/dns/python/message_python.cc
@@ -143,9 +143,7 @@ PyMethodDef Message_methods[] = {
     { "add_rrset", reinterpret_cast<PyCFunction>(Message_addRRset), METH_VARARGS,
       "Add an RRset to the given section of the message.\n"
       "The first argument is of type Section\n"
-      "The second is of type RRset\n"
-      "The third argument is an optional Boolean specifying whether "
-      "the RRset is signed"},
+      "The second is of type RRset"},
     { "clear", reinterpret_cast<PyCFunction>(Message_clear), METH_VARARGS,
       "Clears the message content (if any) and reinitialize the "
       "message in the given mode\n"
@@ -571,17 +569,15 @@ Message_addQuestion(s_Message* self, PyObject* args) {
 
 PyObject*
 Message_addRRset(s_Message* self, PyObject* args) {
-    PyObject *sign = Py_False;
     int section;
     PyObject* rrset;
-    if (!PyArg_ParseTuple(args, "iO!|O!", &section, &rrset_type, &rrset,
-                          &PyBool_Type, &sign)) {
+    if (!PyArg_ParseTuple(args, "iO!", &section, &rrset_type, &rrset)) {
         return (NULL);
     }
 
     try {
         self->cppobj->addRRset(static_cast<Message::Section>(section),
-                               PyRRset_ToRRsetPtr(rrset), sign == Py_True);
+                               PyRRset_ToRRsetPtr(rrset));
         Py_RETURN_NONE;
     } catch (const InvalidMessageOperation& imo) {
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
diff --git a/src/lib/dns/rrset.cc b/src/lib/dns/rrset.cc
index 2bdb71f..17a02cd 100644
--- a/src/lib/dns/rrset.cc
+++ b/src/lib/dns/rrset.cc
@@ -258,6 +258,47 @@ RRset::getRRsigDataCount() const {
     }
 }
 
+unsigned int
+RRset::toWire(OutputBuffer& buffer) const {
+    unsigned int rrs_written;
+
+    rrs_written = rrsetToWire<OutputBuffer>(*this, buffer, 0);
+    if (getRdataCount() > rrs_written) {
+        return (rrs_written);
+    }
+
+    if (rrsig_) {
+        rrs_written += rrsetToWire<OutputBuffer>(*(rrsig_.get()), buffer, 0);
+    }
+
+    return (rrs_written);
+}
+
+unsigned int
+RRset::toWire(AbstractMessageRenderer& renderer) const {
+    unsigned int rrs_written;
+
+    rrs_written =
+        rrsetToWire<AbstractMessageRenderer>(*this, renderer,
+                                             renderer.getLengthLimit());
+    if (getRdataCount() > rrs_written) {
+        renderer.setTruncated();
+        return (rrs_written);
+    }
+
+    if (rrsig_) {
+        rrs_written +=
+            rrsetToWire<AbstractMessageRenderer>(*(rrsig_.get()), renderer,
+                                                 renderer.getLengthLimit());
+    }
+
+    if (getRdataCount() + getRRsigDataCount() > rrs_written) {
+        renderer.setTruncated();
+    }
+
+    return (rrs_written);
+}
+
 namespace {
 class BasicRdataIterator : public RdataIterator {
 private:
diff --git a/src/lib/dns/rrset.h b/src/lib/dns/rrset.h
index 6c73d60..7019f93 100644
--- a/src/lib/dns/rrset.h
+++ b/src/lib/dns/rrset.h
@@ -828,6 +828,17 @@ public:
 
     virtual ~RRset();
 
+    /// \brief Render the RRset in the wire format with name compression and
+    /// truncation handling.
+    ///
+    /// See \c AbstractRRset::toWire(MessageRenderer&)const.
+    virtual unsigned int toWire(AbstractMessageRenderer& renderer) const;
+
+    /// \brief Render the RRset in the wire format without any compression.
+    ///
+    /// See \c AbstractRRset::toWire(OutputBuffer&)const.
+    virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const;
+
     /// \brief Updates the owner name of the \c RRset, including RRSIGs if any
     virtual void setName(const Name& n) {
         BasicRRset::setName(n);
diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc
index 2ce1ef7..28f624a 100644
--- a/src/lib/dns/tests/labelsequence_unittest.cc
+++ b/src/lib/dns/tests/labelsequence_unittest.cc
@@ -177,6 +177,16 @@ TEST_F(LabelSequenceTest, equals_insensitive) {
     EXPECT_TRUE(ls11.equals(ls12));
 }
 
+// operator==().  This is mostly trivial wrapper, so it should suffice to
+// check some basic cases.
+TEST_F(LabelSequenceTest, operatorEqual) {
+    // cppcheck-suppress duplicateExpression
+    EXPECT_TRUE(ls1 == ls1);      // self equivalence
+    EXPECT_TRUE(ls1 == LabelSequence(n1)); // equivalent two different objects
+    EXPECT_FALSE(ls1 == ls2);      // non equivalent objects
+    EXPECT_TRUE(ls1 == ls5);       // it's always case insensitive
+}
+
 // Compare tests
 TEST_F(LabelSequenceTest, compare) {
     // "example.org." and "example.org.", case sensitive
diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc
index 8bf3acc..33e677f 100644
--- a/src/lib/dns/tests/message_unittest.cc
+++ b/src/lib/dns/tests/message_unittest.cc
@@ -289,32 +289,22 @@ TEST_F(MessageTest, getRRCount) {
 }
 
 TEST_F(MessageTest, addRRset) {
-    // default case
+    // initially, we have 0
+    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ANSWER));
+
+    // add two A RRs (unsigned)
     message_render.addRRset(Message::SECTION_ANSWER, rrset_a);
     EXPECT_EQ(rrset_a,
               *message_render.beginSection(Message::SECTION_ANSWER));
     EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER));
 
-    // signed RRset, default case
     message_render.clear(Message::RENDER);
-    message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa);
-    EXPECT_EQ(rrset_aaaa,
-              *message_render.beginSection(Message::SECTION_ANSWER));
-    EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_ANSWER));
 
-    // signed RRset, add with the RRSIG.  getRRCount() should return 2
-    message_render.clear(Message::RENDER);
-    message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa, true);
+    // add one AAAA RR (signed)
+    message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa);
     EXPECT_EQ(rrset_aaaa,
               *message_render.beginSection(Message::SECTION_ANSWER));
     EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER));
-
-    // signed RRset, add explicitly without RRSIG.
-    message_render.clear(Message::RENDER);
-    message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa, false);
-    EXPECT_EQ(rrset_aaaa,
-              *message_render.beginSection(Message::SECTION_ANSWER));
-    EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_ANSWER));
 }
 
 TEST_F(MessageTest, badAddRRset) {
@@ -381,9 +371,9 @@ TEST_F(MessageTest, removeRRset) {
         RRClass::IN(), RRType::A()));
     EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
         RRClass::IN(), RRType::AAAA()));
-    EXPECT_EQ(3, message_render.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(4, message_render.getRRCount(Message::SECTION_ANSWER));
 
-    // Locate the AAAA RRset and remove it; this has one RR in it.
+    // Locate the AAAA RRset and remove it and any associated RRSIGs
     RRsetIterator i = message_render.beginSection(Message::SECTION_ANSWER);
     if ((*i)->getType() == RRType::A()) {
         ++i;
@@ -420,7 +410,7 @@ TEST_F(MessageTest, clearAnswerSection) {
         RRClass::IN(), RRType::A()));
     ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
         RRClass::IN(), RRType::AAAA()));
-    ASSERT_EQ(3, message_render.getRRCount(Message::SECTION_ANSWER));
+    ASSERT_EQ(4, message_render.getRRCount(Message::SECTION_ANSWER));
 
     message_render.clearSection(Message::SECTION_ANSWER);
     EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
@@ -439,7 +429,7 @@ TEST_F(MessageTest, clearAuthoritySection) {
         RRClass::IN(), RRType::A()));
     ASSERT_TRUE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name,
         RRClass::IN(), RRType::AAAA()));
-    ASSERT_EQ(3, message_render.getRRCount(Message::SECTION_AUTHORITY));
+    ASSERT_EQ(4, message_render.getRRCount(Message::SECTION_AUTHORITY));
 
     message_render.clearSection(Message::SECTION_AUTHORITY);
     EXPECT_FALSE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name,
@@ -458,7 +448,7 @@ TEST_F(MessageTest, clearAdditionalSection) {
         RRClass::IN(), RRType::A()));
     ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name,
         RRClass::IN(), RRType::AAAA()));
-    ASSERT_EQ(3, message_render.getRRCount(Message::SECTION_ADDITIONAL));
+    ASSERT_EQ(4, message_render.getRRCount(Message::SECTION_ADDITIONAL));
 
     message_render.clearSection(Message::SECTION_ADDITIONAL);
     EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name,
@@ -529,7 +519,7 @@ TEST_F(MessageTest, appendSection) {
         RRClass::IN(), RRType::A()));
 
     target.appendSection(Message::SECTION_ADDITIONAL, message_render);
-    EXPECT_EQ(3, target.getRRCount(Message::SECTION_ADDITIONAL));
+    EXPECT_EQ(4, target.getRRCount(Message::SECTION_ADDITIONAL));
     EXPECT_TRUE(target.hasRRset(Message::SECTION_ADDITIONAL, test_name,
         RRClass::IN(), RRType::A()));
     EXPECT_TRUE(target.hasRRset(Message::SECTION_ADDITIONAL, test_name,
@@ -539,7 +529,7 @@ TEST_F(MessageTest, appendSection) {
     Message source2(Message::RENDER);
     source2.addRRset(Message::SECTION_ANSWER, rrset_aaaa);
     target.appendSection(Message::SECTION_ANSWER, source2);
-    EXPECT_EQ(3, target.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(4, target.getRRCount(Message::SECTION_ANSWER));
     EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name,
         RRClass::IN(), RRType::A()));
     EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name,
@@ -766,7 +756,7 @@ TEST_F(MessageTest, toWireSigned) {
     rrset_a->addRRsig(rrset_rrsig);
     EXPECT_EQ(2, rrset_a->getRRsigDataCount());
 
-    message_render.addRRset(Message::SECTION_ANSWER, rrset_a, true);
+    message_render.addRRset(Message::SECTION_ANSWER, rrset_a);
 
     EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION));
     EXPECT_EQ(4, message_render.getRRCount(Message::SECTION_ANSWER));
@@ -780,6 +770,50 @@ TEST_F(MessageTest, toWireSigned) {
                         renderer.getLength(), &data[0], data.size());
 }
 
+TEST_F(MessageTest, toWireSignedAndTruncated) {
+    message_render.setQid(0x75c1);
+    message_render.setOpcode(Opcode::QUERY());
+    message_render.setRcode(Rcode::NOERROR());
+    message_render.setHeaderFlag(Message::HEADERFLAG_QR, true);
+    message_render.setHeaderFlag(Message::HEADERFLAG_RD, true);
+    message_render.setHeaderFlag(Message::HEADERFLAG_AA, true);
+    message_render.addQuestion(Question(Name("test.example.com"), RRClass::IN(),
+                                        RRType::TXT()));
+
+    RRsetPtr rrset_txt = RRsetPtr(new RRset(test_name, RRClass::IN(),
+                                            RRType::TXT(), RRTTL(3600)));
+    rrset_txt->addRdata(generic::TXT(string(255, 'a')));
+    rrset_txt->addRdata(generic::TXT(string(255, 'b')));
+    rrset_txt->addRdata(generic::TXT(string(255, 'c')));
+    rrset_txt->addRdata(generic::TXT(string(255, 'd')));
+    rrset_txt->addRdata(generic::TXT(string(255, 'e')));
+    rrset_txt->addRdata(generic::TXT(string(255, 'f')));
+    rrset_txt->addRdata(generic::TXT(string(255, 'g')));
+    rrset_txt->addRdata(generic::TXT(string(255, 'h')));
+
+    rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(),
+                                     RRType::RRSIG(), RRTTL(3600)));
+    // one signature algorithm (5 = RSA/SHA-1)
+    rrset_rrsig->addRdata(generic::RRSIG("TXT 5 3 3600 "
+                                         "20000101000000 20000201000000 "
+                                         "12345 example.com. FAKEFAKEFAKE"));
+    rrset_txt->addRRsig(rrset_rrsig);
+    EXPECT_EQ(1, rrset_txt->getRRsigDataCount());
+
+    message_render.addRRset(Message::SECTION_ANSWER, rrset_txt);
+
+    EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION));
+    EXPECT_EQ(9, message_render.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY));
+    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL));
+
+    message_render.toWire(renderer);
+    vector<unsigned char> data;
+    UnitTestUtil::readWireData("message_toWire7", data);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &data[0], data.size());
+}
+
 TEST_F(MessageTest, toWireInParseMode) {
     // toWire() isn't allowed in the parse mode.
     EXPECT_THROW(message_parse.toWire(renderer), InvalidMessageOperation);
diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am
index 231f7bc..94cadec 100644
--- a/src/lib/dns/tests/testdata/Makefile.am
+++ b/src/lib/dns/tests/testdata/Makefile.am
@@ -87,7 +87,7 @@ EXTRA_DIST += message_fromWire19.spec message_fromWire20.spec
 EXTRA_DIST += message_fromWire21.spec message_fromWire22.spec
 EXTRA_DIST += message_toWire1 message_toWire2.spec message_toWire3.spec
 EXTRA_DIST += message_toWire4.spec message_toWire5.spec
-EXTRA_DIST += message_toWire6
+EXTRA_DIST += message_toWire6 message_toWire7
 EXTRA_DIST += message_toText1.txt message_toText1.spec
 EXTRA_DIST += message_toText2.txt message_toText2.spec
 EXTRA_DIST += message_toText3.txt message_toText3.spec
diff --git a/src/lib/dns/tests/testdata/message_toWire6 b/src/lib/dns/tests/testdata/message_toWire6
index 00185c9..996c99c 100644
--- a/src/lib/dns/tests/testdata/message_toWire6
+++ b/src/lib/dns/tests/testdata/message_toWire6
@@ -2,7 +2,7 @@
 # A simple DNS query message (with a signed response)
 # ID = 0x75c1
 # QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0)
-# QDCOUNT=1, ANCOUNT=2, other COUNTS=0
+# QDCOUNT=1, ANCOUNT=4, other COUNTS=0
 # Question: test.example.com. IN A
 # Answer:
 #  test.example.com. 3600 IN A 192.0.2.1
diff --git a/src/lib/dns/tests/testdata/message_toWire7 b/src/lib/dns/tests/testdata/message_toWire7
new file mode 100644
index 0000000..ba22634
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toWire7
@@ -0,0 +1,35 @@
+#
+# A simple DNS query message (with a signed response)
+# ID = 0x75c1
+# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=1, ADCOUNT=0
+# Question: test.example.com. IN TXT
+# Answer:
+#  test.example.com. 3600 IN TXT aaaaa...
+#
+75c1 8700
+0001 0001 0000 0000
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+0010 0001
+# same name, fully compressed
+c0 0c
+# TTL=3600, TXT, IN, RDLENGTH=256, RDATA
+0010 0001 00000e10 0100 ff
+# 'a' repeated 255 times
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py
index 74f822f..b8e6fb2 100644
--- a/src/lib/python/isc/datasrc/tests/datasrc_test.py
+++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py
@@ -482,15 +482,9 @@ class DataSrcUpdater(unittest.TestCase):
                          rrset.to_text())
 
         rrset_to_delete = rrset;
-
-        # can't delete rrset with associated sig. Abuse that to force an
-        # exception first, then remove the sig, then delete the record
-        updater = dsc.get_updater(isc.dns.Name("example.com"), True)
-        self.assertRaises(isc.datasrc.Error, updater.delete_rrset,
-                          rrset_to_delete)
-
         rrset_to_delete.remove_rrsig()
 
+        updater = dsc.get_updater(isc.dns.Name("example.com"), True)
         updater.delete_rrset(rrset_to_delete)
 
         # The record should be gone in the updater, but not in the original
@@ -582,15 +576,9 @@ class DataSrcUpdater(unittest.TestCase):
                          rrset.to_text())
 
         rrset_to_delete = rrset;
-
-        # can't delete rrset with associated sig. Abuse that to force an
-        # exception first, then remove the sig, then delete the record
-        updater = dsc.get_updater(isc.dns.Name("example.com"), True)
-        self.assertRaises(isc.datasrc.Error, updater.delete_rrset,
-                          rrset_to_delete)
-
         rrset_to_delete.remove_rrsig()
 
+        updater = dsc.get_updater(isc.dns.Name("example.com"), True)
         updater.delete_rrset(rrset_to_delete)
 
         # The record should be gone in the updater, but not in the original
diff --git a/src/lib/resolve/resolve.cc b/src/lib/resolve/resolve.cc
index f741121..cfd6217 100644
--- a/src/lib/resolve/resolve.cc
+++ b/src/lib/resolve/resolve.cc
@@ -26,7 +26,7 @@ namespace {
             message_(message), section_(sect)
         {}
         void operator()(const RRsetPtr rrset) {
-            message_->addRRset(section_, rrset, true);
+            message_->addRRset(section_, rrset);
         }
         MessagePtr message_;
         const Message::Section section_;
diff --git a/src/lib/testutils/dnsmessage_test.h b/src/lib/testutils/dnsmessage_test.h
index 57cb72c..fd1d51a 100644
--- a/src/lib/testutils/dnsmessage_test.h
+++ b/src/lib/testutils/dnsmessage_test.h
@@ -168,6 +168,10 @@ public:
     {}
     void operator()(isc::dns::ConstRRsetPtr rrset) {
         output_ += "  " + rrset->toText();
+
+        if (rrset->getRRsig()) {
+            output_ += "  " + rrset->getRRsig()->toText();
+        }
     }
 private:
     std::string& output_;
@@ -256,6 +260,7 @@ rrsetsCheck(EXPECTED_ITERATOR expected_begin, EXPECTED_ITERATOR expected_end,
         if (found_rrset_it != expected_end) {
             rrsetCheck(*found_rrset_it, *it);
             ++rrset_matched;
+            rrset_matched += (*it)->getRRsigDataCount();
         }
     }
 
@@ -265,9 +270,20 @@ rrsetsCheck(EXPECTED_ITERATOR expected_begin, EXPECTED_ITERATOR expected_end,
                      "Expected:\n" + expected_text);
         // make sure all expected RRsets are in actual sets
         EXPECT_EQ(std::distance(expected_begin, expected_end), rrset_matched);
+
+#if (0)
+        // TODO: see bug #2223. The following code was
+        // disabled by #2165. The RRSIG RRsets are no longer directly
+        // stored in the Message's rrsets, so the iterator will not find
+        // them. The expected text used in many tests are flattened,
+        // where the RRSIGs are inline. In other words, RRSIGs may occur
+        // between (expected_begin, expected_end) but not between
+        // (actual_begin, actual_end).
+
         // make sure rrsets only contains expected RRsets
         EXPECT_EQ(std::distance(expected_begin, expected_end),
                   std::distance(actual_begin, actual_end));
+#endif
     }
 }
 



More information about the bind10-changes mailing list