Browse Source

[2097] Merge branch 'trac2096' into trac2097 with fixing conflicts.

It's mainly about renaming the header file.  Also, define empty
action callbacks within RdataReader again as I noticed it would be
useful for RdataSet implementation (and for some other purposes,
such as additional section processing).
JINMEI Tatuya 12 years ago
parent
commit
23492e5eb5

+ 1 - 0
configure.ac

@@ -1165,6 +1165,7 @@ AC_CONFIG_FILES([Makefile
                  src/lib/datasrc/Makefile
                  src/lib/datasrc/Makefile
                  src/lib/datasrc/memory/Makefile
                  src/lib/datasrc/memory/Makefile
                  src/lib/datasrc/memory/tests/Makefile
                  src/lib/datasrc/memory/tests/Makefile
+                 src/lib/datasrc/memory/benchmarks/Makefile
                  src/lib/datasrc/tests/Makefile
                  src/lib/datasrc/tests/Makefile
                  src/lib/datasrc/tests/testdata/Makefile
                  src/lib/datasrc/tests/testdata/Makefile
                  src/lib/xfr/Makefile
                  src/lib/xfr/Makefile

+ 3 - 4
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 -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
@@ -11,8 +11,7 @@ CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
 noinst_LTLIBRARIES = libdatasrc_memory.la
 noinst_LTLIBRARIES = libdatasrc_memory.la
 
 
 libdatasrc_memory_la_SOURCES = \
 libdatasrc_memory_la_SOURCES = \
-	rdata_encoder.h rdata_encoder.cc \
-	rdata_field.h rdata_field.cc \
-	rdata_reader.h rdata_reader.cc \
 	rdataset.h rdataset.cc \
 	rdataset.h rdataset.cc \
+	rdata_serialization.h rdata_serialization.cc \
 	domaintree.h
 	domaintree.h
+EXTRA_DIST  = rdata_serialization_priv.cc

+ 1 - 0
src/lib/datasrc/memory/benchmarks/.gitignore

@@ -0,0 +1 @@
+/rdata_reader_bench

+ 17 - 0
src/lib/datasrc/memory/benchmarks/Makefile.am

@@ -0,0 +1,17 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+noinst_PROGRAMS = 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/dns/libb10-dns++.la

+ 221 - 0
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);
+}

+ 0 - 306
src/lib/datasrc/memory/rdata_encoder.cc

@@ -1,306 +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 "rdata_encoder.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>
-
-using namespace isc::dns;
-using std::vector;
-
-namespace isc {
-namespace datasrc {
-namespace memory {
-
-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);
-}
-
-
-} // namespace memory
-} // namespace datasrc
-} // datasrc isc

+ 0 - 211
src/lib/datasrc/memory/rdata_field.cc

@@ -1,211 +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 "rdata_field.h"
-
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-
-#include <boost/static_assert.hpp>
-
-using namespace isc::dns;
-
-namespace isc {
-namespace datasrc {
-namespace memory {
-
-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);
-
-}
-
-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);
-}
-
-}
-}
-}

+ 631 - 0
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

+ 234 - 7
src/lib/datasrc/memory/rdata_encoder.h

@@ -15,8 +15,6 @@
 #ifndef DATASRC_MEMORY_RDATA_ENCODER_H
 #ifndef DATASRC_MEMORY_RDATA_ENCODER_H
 #define DATASRC_MEMORY_RDATA_ENCODER_H 1
 #define DATASRC_MEMORY_RDATA_ENCODER_H 1
 
 
-#include "rdata_field.h"
-
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
 #include <dns/labelsequence.h>
 #include <dns/labelsequence.h>
@@ -29,8 +27,7 @@
 
 
 #include <vector>
 #include <vector>
 
 
-/// \file rdata_encoder.h
-/// \brief Set of utility classes for encoding RDATA in memory efficient way.
+/// \file rdata_serialization.h
 ///
 ///
 /// This file defines a set of interfaces (classes, types, constants) to
 /// This file defines a set of interfaces (classes, types, constants) to
 /// manipulate a given set of RDATA of the same type (normally associated with
 /// manipulate a given set of RDATA of the same type (normally associated with
@@ -44,9 +41,9 @@
 /// Two main classes are provided: one is
 /// Two main classes are provided: one is
 /// \c isc::datasrc::memory::RdataEncoder, which allows
 /// \c isc::datasrc::memory::RdataEncoder, which allows
 /// the application to create encoded data for a set of RDATA;
 /// 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 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,
 /// The actual encoding detail is private information to the implementation,
 /// and the application shouldn't assume anything about that except that
 /// and the application shouldn't assume anything about that except that
@@ -265,6 +262,236 @@ private:
     RdataEncoderImpl* impl_;
     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,
+///                    &handleName, &handleData);
+/// reader.iterate();
+/// \endcode
+///
+/// \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 memory
 } // namespace datasrc
 } // namespace datasrc
 } // namespace isc
 } // namespace isc

+ 4 - 39
src/lib/datasrc/memory/rdata_field.h

@@ -12,39 +12,10 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
-#ifndef DATASRC_MEMORY_RDATA_FIELD
-#define DATASRC_MEMORY_RDATA_FIELD
+// 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).
 
 
-#include <stdint.h>
-
-/// \file rdata_field.h
-///
-/// This header should be considered private to the implementation and should
-/// not be used from outside.
-///
-/// It is used to share the definition of encoding for RRtypes.
-
-namespace isc {
-namespace dns {
-class RRType;
-class RRClass;
-}
-namespace datasrc {
-namespace memory {
-
-/// \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
-};
-
-/// Specification of a single RDATA field in terms of internal encoding.
 struct RdataFieldSpec {
 struct RdataFieldSpec {
     enum FieldType {
     enum FieldType {
         FIXEDLEN_DATA = 0,      // fixed-length data field
         FIXEDLEN_DATA = 0,      // fixed-length data field
@@ -90,10 +61,4 @@ struct RdataEncodeSpec {
 
 
 /// \brief Get the spec for given class and type
 /// \brief Get the spec for given class and type
 const RdataEncodeSpec&
 const RdataEncodeSpec&
-getRdataEncodeSpec(const dns::RRClass& rrclass, const dns::RRType& rrtype);
-
-}
-}
-}
-
-#endif
+getRdataEncodeSpec(const RRClass& rrclass, const RRType& rrtype);

+ 4 - 4
src/lib/datasrc/memory/rdataset.cc

@@ -21,8 +21,7 @@
 #include <dns/rrset.h>
 #include <dns/rrset.h>
 
 
 #include "rdataset.h"
 #include "rdataset.h"
-#include "rdata_encoder.h"
-#include "rdata_reader.h"
+#include "rdata_serialization.h"
 
 
 #include <boost/static_assert.hpp>
 #include <boost/static_assert.hpp>
 
 
@@ -129,8 +128,9 @@ RdataSet::destroy(util::MemorySegment& mem_sgmt, RRClass rrclass,
     const size_t data_len =
     const size_t data_len =
         RdataReader(rrclass, rdataset->type,
         RdataReader(rrclass, rdataset->type,
                     reinterpret_cast<const uint8_t*>(rdataset->getDataBuf()),
                     reinterpret_cast<const uint8_t*>(rdataset->getDataBuf()),
-                    rdataset->getRdataCount(),
-                    rdataset->getSigRdataCount()).getSize();
+                    rdataset->getRdataCount(), rdataset->getSigRdataCount(),
+                    &RdataReader::emptyNameAction,
+                    &RdataReader::emptyDataAction).getSize();
     const size_t ext_rrsig_count_len =
     const size_t ext_rrsig_count_len =
         rdataset->sig_rdata_count_ == MANY_RRSIG_COUNT ? sizeof(uint16_t) : 0;
         rdataset->sig_rdata_count_ == MANY_RRSIG_COUNT ? sizeof(uint16_t) : 0;
     rdataset->~RdataSet();
     rdataset->~RdataSet();

+ 174 - 174
src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc

@@ -12,10 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
-// Note: This file tests both the rdata_encoder and rdata_reader. They are
-// tested together because they form kind the oposite sides of the same
-// functionality.
-
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
 #include <util/buffer.h>
 #include <util/buffer.h>
@@ -28,9 +24,7 @@
 #include <dns/rrclass.h>
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
 #include <dns/rrtype.h>
 
 
-#include <datasrc/memory/rdata_encoder.h>
-#include <datasrc/memory/rdata_field.h>
-#include <datasrc/memory/rdata_reader.h>
+#include <datasrc/memory/rdata_serialization.h>
 
 
 #include <util/unittests/wiredata.h>
 #include <util/unittests/wiredata.h>
 
 
@@ -52,6 +46,18 @@ using isc::util::unittests::matchWireData;
 using std::string;
 using std::string;
 using std::vector;
 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 {
 namespace {
 // This defines a tuple of test data used in test_rdata_list below.
 // This defines a tuple of test data used in test_rdata_list below.
 struct TestRdata {
 struct TestRdata {
@@ -102,24 +108,21 @@ const TestRdata test_rdata_list[] = {
 // from encoded representation of each RDATA.
 // from encoded representation of each RDATA.
 void
 void
 renderNameField(MessageRenderer* renderer, bool additional_required,
 renderNameField(MessageRenderer* renderer, bool additional_required,
-                const LabelSequence& labels, unsigned attributes)
+                const LabelSequence& labels, RdataNameAttributes attributes)
 {
 {
-    EXPECT_EQ(additional_required,
-              (attributes & NAMEATTR_ADDITIONAL) != 0);
+    EXPECT_EQ(additional_required, (attributes & NAMEATTR_ADDITIONAL) != 0);
     renderer->writeName(labels, (attributes & NAMEATTR_COMPRESSIBLE) != 0);
     renderer->writeName(labels, (attributes & NAMEATTR_COMPRESSIBLE) != 0);
 }
 }
 
 
 void
 void
-renderDataField(MessageRenderer* renderer, const uint8_t* data,
-                size_t data_len)
-{
+renderDataField(MessageRenderer* renderer, const void* data, size_t data_len) {
     renderer->writeData(data, data_len);
     renderer->writeData(data, data_len);
 }
 }
 
 
 class RdataSerializationTest : public ::testing::Test {
 class RdataSerializationTest : public ::testing::Test {
 protected:
 protected:
     RdataSerializationTest() : a_rdata_(createRdata(RRType::A(), RRClass::IN(),
     RdataSerializationTest() : a_rdata_(createRdata(RRType::A(), RRClass::IN(),
-                                              "192.0.2.53")),
+                                                    "192.0.2.53")),
                          aaaa_rdata_(createRdata(RRType::AAAA(), RRClass::IN(),
                          aaaa_rdata_(createRdata(RRType::AAAA(), RRClass::IN(),
                                                  "2001:db8::53")),
                                                  "2001:db8::53")),
                          rrsig_rdata_(createRdata(
                          rrsig_rdata_(createRdata(
@@ -151,7 +154,7 @@ protected:
 template<class DecoderStyle>
 template<class DecoderStyle>
 class RdataEncodeDecodeTest : public RdataSerializationTest {
 class RdataEncodeDecodeTest : public RdataSerializationTest {
 public:
 public:
-    // This helper test method constructs encodes the given list of RDATAs
+    // This helper test method encodes the given list of RDATAs
     // (in rdata_list), and then iterates over the data, rendering the fields
     // (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
     // in the wire format.  It then compares the wire data with the one
     // generated by the normal libdns++ interface to see the encoding/decoding
     // generated by the normal libdns++ interface to see the encoding/decoding
@@ -168,13 +171,16 @@ public:
 
 
 // Used across more classes and scopes. But it's just uninteresting
 // Used across more classes and scopes. But it's just uninteresting
 // constant.
 // constant.
-const Name dummy_name2("example.com");
+const Name& dummyName2() {
+    static const Name result("example.com");
+    return (result);
+}
 
 
 bool
 bool
 additionalRequired(const RRType& type) {
 additionalRequired(const RRType& type) {
     // The set of RR types that require additional section processing.
     // The set of RR types that require additional section processing.
-    // We'll pass it to renderNameField to check the stored attribute matches
-    // our expectation.
+    // 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;
     static std::set<RRType> need_additionals;
     if (need_additionals.empty()) {
     if (need_additionals.empty()) {
         need_additionals.insert(RRType::NS());
         need_additionals.insert(RRType::NS());
@@ -286,8 +292,8 @@ public:
                                 encoded_data.end());
                                 encoded_data.end());
         }
         }
 
 
-        // If RRSIGs are given, we need to extract the list of the RRSIG lengths
-        // and adjust encoded_data_ further.
+        // If RRSIGs are given, we need to extract the list of the RRSIG
+        // lengths and adjust encoded_data_ further.
         vector<uint16_t> rrsiglen_list;
         vector<uint16_t> rrsiglen_list;
         if (rrsig_count > 0) {
         if (rrsig_count > 0) {
             const size_t rrsig_len_size = rrsig_count * sizeof(uint16_t);
             const size_t rrsig_len_size = rrsig_count * sizeof(uint16_t);
@@ -305,16 +311,16 @@ public:
                                       additionalRequired(rrtype), _1, _2),
                                       additionalRequired(rrtype), _1, _2),
                           boost::bind(renderDataField, &renderer, _1, _2));
                           boost::bind(renderDataField, &renderer, _1, _2));
 
 
-    // 2nd dummy name
-    renderer.writeName(dummy_name2);
-    // Finally, dump any RRSIGs in wire format.
-    foreachRRSig(encoded_data, rrsiglen_list,
-                 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));
     }
     }
 };
 };
 
 
-// Decode using reader with the return value of next
-class NextDecoder {
+// Check using callbacks and calling next until the end.
+class CallbackDecoder {
 public:
 public:
     static void decode(const isc::dns::RRClass& rrclass,
     static void decode(const isc::dns::RRClass& rrclass,
                        const isc::dns::RRType& rrtype,
                        const isc::dns::RRType& rrtype,
@@ -323,38 +329,18 @@ public:
                        MessageRenderer& renderer)
                        MessageRenderer& renderer)
     {
     {
         RdataReader reader(rrclass, rrtype, &encoded_data[0], rdata_count,
         RdataReader reader(rrclass, rrtype, &encoded_data[0], rdata_count,
-                           sig_count);
-        RdataReader::Result field;
-        while ((field = reader.next())) {
-            switch (field.type()) {
-                case RdataReader::DATA:
-                    renderer.writeData(field.data(), field.size());
-                    break;
-                case RdataReader::NAME:
-                    renderer.writeName(field.label(), field.compressible());
-                    break;
-                default:
-                    FAIL();
-            }
-        }
-
-        renderer.writeName(dummy_name2);
-
-        while ((field = reader.nextSig())) {
-            switch (field.type()) {
-                case RdataReader::DATA:
-                    renderer.writeData(field.data(), field.size());
-                    break;
-                default: // There are also no NAME fields in RRSigs
-                    FAIL();
-            }
-        }
+                           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) {}
     }
     }
 };
 };
 
 
-// Decode using reader with the return value of next, but after the reader
-// was used and rewund.
-class RewundDecoder {
+// Check using callbacks and calling iterate.
+class IterateDecoder {
 public:
 public:
     static void decode(const isc::dns::RRClass& rrclass,
     static void decode(const isc::dns::RRClass& rrclass,
                        const isc::dns::RRType& rrtype,
                        const isc::dns::RRType& rrtype,
@@ -362,42 +348,46 @@ public:
                        const vector<uint8_t>& encoded_data, size_t,
                        const vector<uint8_t>& encoded_data, size_t,
                        MessageRenderer& renderer)
                        MessageRenderer& renderer)
     {
     {
-        RdataReader reader(rrclass, rrtype, &encoded_data[0], rdata_count,
-                           sig_count);
-        // Use the reader first and rewind it
-        reader.iterateSig();
+        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();
         reader.iterate();
-        reader.rewind();
-        RdataReader::Result field;
-        while ((field = reader.next())) {
-            switch (field.type()) {
-                case RdataReader::DATA:
-                    renderer.writeData(field.data(), field.size());
-                    break;
-                case RdataReader::NAME:
-                    renderer.writeName(field.label(), field.compressible());
-                    break;
-                default:
-                    FAIL();
-            }
-        }
+        renderer.writeName(dummyName2());
+        reader.iterateAllSigs();
+    }
+};
 
 
-        renderer.writeName(dummy_name2);
+namespace {
 
 
-        while ((field = reader.nextSig())) {
-            switch (field.type()) {
-                case RdataReader::DATA:
-                    renderer.writeData(field.data(), field.size());
-                    break;
-                default: // There are also no NAME fields in RRSigs
-                    FAIL();
-            }
-        }
+// 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);
     }
     }
-};
+}
 
 
-// Check using callbacks and calling next until the end.
-class CallbackDecoder {
+}
+
+// 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:
 public:
     static void decode(const isc::dns::RRClass& rrclass,
     static void decode(const isc::dns::RRClass& rrclass,
                        const isc::dns::RRType& rrtype,
                        const isc::dns::RRType& rrtype,
@@ -405,19 +395,30 @@ public:
                        const vector<uint8_t>& encoded_data, size_t,
                        const vector<uint8_t>& encoded_data, size_t,
                        MessageRenderer& renderer)
                        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()) { }
-        renderer.writeName(dummy_name2);
-        while (reader.nextSig()) { }
+        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();
     }
     }
 };
 };
 
 
-// Check using callbacks and calling iterate.
-class IterateDecoder {
+// Decode using the iteration over one rdata each time.
+// We also count there's the correct count of Rdatas.
+class SingleIterateDecoder {
 public:
 public:
     static void decode(const isc::dns::RRClass& rrclass,
     static void decode(const isc::dns::RRClass& rrclass,
                        const isc::dns::RRType& rrtype,
                        const isc::dns::RRType& rrtype,
@@ -430,9 +431,17 @@ public:
                            boost::bind(renderNameField, &renderer,
                            boost::bind(renderNameField, &renderer,
                                        additionalRequired(rrtype), _1, _2),
                                        additionalRequired(rrtype), _1, _2),
                            boost::bind(renderDataField, &renderer, _1, _2));
                            boost::bind(renderDataField, &renderer, _1, _2));
-        reader.iterate();
-        renderer.writeName(dummy_name2);
-        reader.iterateSig();
+        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);
     }
     }
 };
 };
 
 
@@ -442,19 +451,6 @@ public:
 // of rendering.
 // of rendering.
 template<bool start_data, bool start_sig>
 template<bool start_data, bool start_sig>
 class HybridDecoder {
 class HybridDecoder {
-private:
-    // Append data either to the renderer or to the vector passed.
-    // The double pointer is there so we can change the renderer to NULL
-    // and back during the test.
-    static void appendData(vector<uint8_t>* where, MessageRenderer** renderer,
-                           const uint8_t* data, size_t size)
-    {
-        if (*renderer != NULL) {
-            (*renderer)->writeData(data, size);
-        } else {
-            where->insert(where->end(), data, data + size);
-        }
-    }
 public:
 public:
     static void decode(const isc::dns::RRClass& rrclass,
     static void decode(const isc::dns::RRClass& rrclass,
                        const isc::dns::RRType& rrtype,
                        const isc::dns::RRType& rrtype,
@@ -469,7 +465,8 @@ public:
                            rdata_count, sig_count,
                            rdata_count, sig_count,
                            boost::bind(renderNameField, &renderer,
                            boost::bind(renderNameField, &renderer,
                                        additionalRequired(rrtype), _1, _2),
                                        additionalRequired(rrtype), _1, _2),
-                           boost::bind(appendData, &data, &current, _1, _2));
+                           boost::bind(appendOrRenderData, &data, &current, _1,
+                                       _2));
         // The size matches
         // The size matches
         EXPECT_EQ(encoded_data_len, reader.getSize());
         EXPECT_EQ(encoded_data_len, reader.getSize());
         if (start_sig) {
         if (start_sig) {
@@ -485,28 +482,35 @@ public:
         // Now, we let all sigs to be copied to data. We disable the
         // Now, we let all sigs to be copied to data. We disable the
         // renderer for this.
         // renderer for this.
         current = NULL;
         current = NULL;
-        reader.iterateSig();
+        reader.iterateAllSigs();
         // Now return the renderer and render the rest of the data
         // Now return the renderer and render the rest of the data
         current = &renderer;
         current = &renderer;
         reader.iterate();
         reader.iterate();
         // Now, this should not break anything and should be valid, but should
         // Now, this should not break anything and should be valid, but should
         // return ends.
         // return ends.
-        EXPECT_FALSE(reader.next());
-        EXPECT_FALSE(reader.nextSig());
+        EXPECT_EQ(RdataReader::RRSET_BOUNDARY, reader.next());
+        EXPECT_EQ(RdataReader::RRSET_BOUNDARY, reader.nextSig());
         // Render the name and the sigs
         // Render the name and the sigs
-        renderer.writeName(dummy_name2);
+        renderer.writeName(dummyName2());
         renderer.writeData(&data[0], data.size());
         renderer.writeData(&data[0], data.size());
         // The size matches even after use
         // The size matches even after use
         EXPECT_EQ(encoded_data_len, reader.getSize());
         EXPECT_EQ(encoded_data_len, reader.getSize());
     }
     }
 };
 };
 
 
-typedef ::testing::Types<ManualDecoderStyle, NextDecoder, RewundDecoder,
-                         CallbackDecoder, IterateDecoder,
+typedef ::testing::Types<ManualDecoderStyle,
+                         CallbackDecoder, IterateDecoder, SingleIterateDecoder,
                          HybridDecoder<true, true>, HybridDecoder<true, false>,
                          HybridDecoder<true, true>, HybridDecoder<true, false>,
                          HybridDecoder<false, true>,
                          HybridDecoder<false, true>,
                          HybridDecoder<false, false> >
                          HybridDecoder<false, false> >
     DecoderStyles;
     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);
 TYPED_TEST_CASE(RdataEncodeDecodeTest, DecoderStyles);
 
 
 void
 void
@@ -536,8 +540,8 @@ checkEncode(RRClass rrclass, RRType rrtype,
     // These two names will be rendered before and after the test RDATA,
     // 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
     // to check in case the RDATA contain a domain name whether it's
     // compressed or not correctly.  The names in the RDATA should basically
     // 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
+    // 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.
     // the name in the RDATA.
     const Name dummy_name("com");
     const Name dummy_name("com");
 
 
@@ -550,7 +554,7 @@ checkEncode(RRClass rrclass, RRType rrtype,
     BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
     BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
         rdata->toWire(expected_renderer_);
         rdata->toWire(expected_renderer_);
     }
     }
-    expected_renderer_.writeName(dummy_name2);
+    expected_renderer_.writeName(dummyName2());
     BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
     BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
         rdata->toWire(expected_renderer_);
         rdata->toWire(expected_renderer_);
     }
     }
@@ -601,7 +605,7 @@ addRdataCommon(const vector<ConstRdataPtr>& rrsigs) {
 
 
 TYPED_TEST(RdataEncodeDecodeTest, addRdata) {
 TYPED_TEST(RdataEncodeDecodeTest, addRdata) {
     vector<ConstRdataPtr> rrsigs;
     vector<ConstRdataPtr> rrsigs;
-    this->addRdataCommon(rrsigs);     // basic tests without RRSIGs (empty vector)
+    this->addRdataCommon(rrsigs); // basic tests without RRSIGs (empty vector)
 
 
     // Test with RRSIGs (covered type doesn't always match, but the encoder
     // Test with RRSIGs (covered type doesn't always match, but the encoder
     // doesn't check that)
     // doesn't check that)
@@ -654,6 +658,22 @@ addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs) {
     checkEncode(RRClass::IN(), RRType::NAPTR(), rdata_list_, 1, rrsigs);
     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) {
 TEST_F(RdataSerializationTest, encodeLargeRdata) {
     // There should be no reason for a large RDATA to fail in encoding,
     // There should be no reason for a large RDATA to fail in encoding,
     // but we check such a case explicitly.
     // but we check such a case explicitly.
@@ -667,10 +687,15 @@ TEST_F(RdataSerializationTest, encodeLargeRdata) {
     encodeWrapper(encoder_.getStorageLength());
     encodeWrapper(encoder_.getStorageLength());
 
 
     // The encoded data should be identical to the original one.
     // 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));
+    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) {
 TYPED_TEST(RdataEncodeDecodeTest, addRdataMulti) {
@@ -763,6 +788,19 @@ TEST_F(RdataSerializationTest, badAddRdata) {
                  isc::BadValue);
                  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) {
 TEST_F(RdataSerializationTest, addSIGRdataOnly) {
     // Encoded data that only contain RRSIGs.  Mostly useless, but can happen
     // Encoded data that only contain RRSIGs.  Mostly useless, but can happen
     // (in a partially broken zone) and it's accepted.
     // (in a partially broken zone) and it's accepted.
@@ -771,10 +809,14 @@ TEST_F(RdataSerializationTest, addSIGRdataOnly) {
     encodeWrapper(encoder_.getStorageLength());
     encodeWrapper(encoder_.getStorageLength());
     ASSERT_LT(sizeof(uint16_t), 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_));
+    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) {
 TEST_F(RdataSerializationTest, badAddSIGRdata) {
@@ -794,46 +836,4 @@ TEST_F(RdataSerializationTest, badAddSIGRdata) {
     encoder_.start(RRClass::IN(), RRType::A());
     encoder_.start(RRClass::IN(), RRType::A());
     EXPECT_THROW(encoder_.addSIGRdata(big_sigrdata), RdataEncodingError);
     EXPECT_THROW(encoder_.addSIGRdata(big_sigrdata), RdataEncodingError);
 }
 }
-
-// Test the result returns what it was constructed with.
-TEST_F(RdataSerializationTest, readerResult) {
-    // Default constructor
-    RdataReader::Result empty;
-    // Everything should be at the "empty" values, type END
-    EXPECT_EQ(RdataReader::END, empty.type());
-    EXPECT_EQ(NULL, empty.data());
-    EXPECT_EQ(0, empty.size());
-    EXPECT_TRUE(empty.label().equals(LabelSequence(Name::ROOT_NAME())));
-    EXPECT_FALSE(empty);
-    EXPECT_FALSE(empty.compressible());
-    EXPECT_FALSE(empty.additional());
-    // Constructor from label sequence
-    LabelSequence seq(Name("example.org"));
-    RdataReader::Result compressible(seq, NAMEATTR_COMPRESSIBLE);
-    EXPECT_EQ(RdataReader::NAME, compressible.type());
-    EXPECT_EQ(NULL, compressible.data());
-    EXPECT_EQ(0, compressible.size());
-    EXPECT_TRUE(compressible.label().equals(seq));
-    EXPECT_TRUE(compressible);
-    EXPECT_TRUE(compressible.compressible());
-    EXPECT_FALSE(compressible.additional());
-    RdataReader::Result incompressible(seq, NAMEATTR_ADDITIONAL);
-    EXPECT_EQ(RdataReader::NAME, incompressible.type());
-    EXPECT_EQ(NULL, incompressible.data());
-    EXPECT_EQ(0, incompressible.size());
-    EXPECT_TRUE(incompressible.label().equals(seq));
-    EXPECT_TRUE(incompressible);
-    EXPECT_FALSE(incompressible.compressible());
-    EXPECT_TRUE(incompressible.additional());
-    // Constructor from data
-    uint8_t byte;
-    RdataReader::Result data(&byte, 1);
-    EXPECT_EQ(RdataReader::DATA, data.type());
-    EXPECT_EQ(&byte, data.data());
-    EXPECT_EQ(1, data.size());
-    EXPECT_TRUE(data.label().equals(LabelSequence(Name::ROOT_NAME())));
-    EXPECT_TRUE(data);
-    EXPECT_FALSE(data.compressible());
-    EXPECT_FALSE(data.additional());
-}
 }
 }

+ 2 - 3
src/lib/datasrc/memory/tests/rdataset_unittest.cc

@@ -24,8 +24,7 @@
 #include <dns/rrtype.h>
 #include <dns/rrtype.h>
 #include <dns/rrttl.h>
 #include <dns/rrttl.h>
 
 
-#include <datasrc/memory/rdata_encoder.h>
-#include <datasrc/memory/rdata_reader.h>
+#include <datasrc/memory/rdata_serialization.h>
 #include <datasrc/memory/rdataset.h>
 #include <datasrc/memory/rdataset.h>
 
 
 #include <testutils/dnsmessage_test.h>
 #include <testutils/dnsmessage_test.h>
@@ -75,7 +74,7 @@ restoreTTL(const void* ttl_data) {
 // is the expected in::A RDATA (the value is taken from the RdataSetTest
 // is the expected in::A RDATA (the value is taken from the RdataSetTest
 // constructor).
 // constructor).
 void
 void
-checkData(const uint8_t* data, size_t size) {
+checkData(const void* data, size_t size) {
     isc::util::InputBuffer b(data, size);
     isc::util::InputBuffer b(data, size);
     EXPECT_EQ(0, in::A(b, size).compare(in::A("192.0.2.1")));
     EXPECT_EQ(0, in::A(b, size).compare(in::A("192.0.2.1")));
 }
 }