src/lib/datasrc/Makefile
src/lib/datasrc/memory/Makefile
src/lib/datasrc/memory/tests/Makefile
+ src/lib/datasrc/memory/benchmarks/Makefile
src/lib/datasrc/tests/Makefile
src/lib/datasrc/tests/testdata/Makefile
src/lib/xfr/Makefile
-SUBDIRS = . tests
+SUBDIRS = . tests benchmarks
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
--- /dev/null
+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_SOURCES += rdata_reader.cc rdata_reader.h
+
+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
--- /dev/null
+// 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_reader.h"
+
+using namespace isc::dns;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+void
+RdataReader2::emptyNameAction(const LabelSequence&, unsigned) {
+ // Do nothing here. On purpose, it's not unfinished.
+}
+
+void
+RdataReader2::emptyDataAction(const uint8_t*, size_t) {
+ // Do nothing here. On purpose, it's not unfinished.
+}
+
+RdataReader2::Result::Result(const LabelSequence& label,
+ unsigned attributes) :
+ label_(label),
+ data_(NULL),
+ size_(0),
+ type_(NAME),
+ compressible_((attributes & NAMEATTR_COMPRESSIBLE) != 0),
+ additional_((attributes & NAMEATTR_ADDITIONAL) != 0)
+{}
+
+RdataReader2::Result::Result(const uint8_t* data, size_t size) :
+ label_(Name::ROOT_NAME()),
+ data_(data),
+ size_(size),
+ type_(DATA),
+ compressible_(false),
+ additional_(false)
+{}
+
+RdataReader2::RdataReader2(const RRClass& rrclass, const RRType& rrtype,
+ const uint8_t* 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 casts, well, C++ decided it doesn't like completely valid
+ // and explicitly allowed cast in C, so we need to fool it through
+ // void.
+ lengths_(static_cast<const uint16_t*>(
+ static_cast<const void*>(data))), // The lenghts are stored first
+ // And the data just after all the lengths
+ data_(data + (var_count_total_ + sig_count_) * sizeof(uint16_t)),
+ sigs_(NULL)
+{
+ rewind();
+}
+
+void
+RdataReader2::rewind() {
+ data_pos_ = 0;
+ spec_pos_ = 0;
+ length_pos_ = 0;
+ sig_data_pos_ = 0;
+ sig_pos_ = 0;
+}
+
+RdataReader2::Result
+RdataReader2::next() {
+ 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();
+ return (Result(sequence, spec.name_attributes));
+ } else {
+ const size_t length(spec.type == RdataFieldSpec::FIXEDLEN_DATA ?
+ spec.fixeddata_len : lengths_[length_pos_++]);
+ const Result result(data_ + data_pos_, length);
+ data_pos_ += length;
+ return (result);
+ }
+ } else {
+ sigs_ = data_ + data_pos_;
+ return (Result());
+ }
+}
+
+void
+RdataReader2::iterate() {
+ while (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 Result result(data_ + data_pos_, length);
+ data_pos_ += length;
+ data_action_(result.data(), result.size());
+ }
+ }
+ sigs_ = data_ + data_pos_;
+}
+
+RdataReader2::Result
+RdataReader2::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 (next()) {}
+ assert(sigs_ != NULL);
+ // Return the state
+ data_pos_ = data_pos;
+ spec_pos_ = spec_pos;
+ length_pos_ = length_pos;
+ }
+ // Extract the result
+ const Result result(sigs_ + sig_data_pos_, lengths_[var_count_total_ +
+ sig_pos_]);
+ // Move the position of iterator.
+ sig_data_pos_ += lengths_[var_count_total_ + sig_pos_];
+ ++sig_pos_;
+ // Call the callback
+ data_action_(result.data(), result.size());
+ return (result);
+ } else {
+ return (Result());
+ }
+}
+
+void
+RdataReader2::iterateSig() {
+ while (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 (next()) {}
+ assert(sigs_ != NULL);
+ // Return the state
+ data_pos_ = data_pos;
+ spec_pos_ = spec_pos;
+ length_pos_ = length_pos;
+ }
+ // Extract the result
+ const Result result(sigs_ + sig_data_pos_, lengths_[var_count_total_ +
+ sig_pos_]);
+ // Move the position of iterator.
+ sig_data_pos_ += lengths_[var_count_total_ + sig_pos_];
+ ++sig_pos_;
+ // Call the callback
+ data_action_(result.data(), result.size());
+ }
+}
+
+size_t
+RdataReader2::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);
+}
+
+}
+}
+}
--- /dev/null
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_MEMORY_RDATA_READER_H
+#define DATASRC_MEMORY_RDATA_READER_H 1
+
+#include <datasrc/memory/rdata_field.h>
+
+#include <boost/function.hpp>
+
+#include <dns/labelsequence.h>
+#include <dns/name.h>
+
+namespace isc {
+// Some forward declarations
+namespace dns{
+class RRClass;
+class RRType;
+}
+
+namespace datasrc {
+namespace memory {
+
+/// \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
+/// and names. It does not give you convenient Rdata or RRset class.
+///
+/// It allows two types of operation. First one is by providing callbacks
+/// to the constructor and then iterating by repeatedly calling next(), or
+/// calling iterate() once. The callbacks are then called with each part of
+/// the data.
+///
+/// \code
+/// void handleLabel(const dns::LabelSequence& label, unsigned int flags) {
+/// ...
+/// }
+/// void handleData(const uint8_t* data, size_t size) {
+/// ...
+/// }
+///
+/// RdataReader reader(RRClass::IN(), RRType::AAAA(), size, data,
+/// handleLabel, handleData);
+/// reader.iterate();
+/// \endcode
+///
+/// The other way is to use the return value of next() and loop through
+/// it manually. It has the inconvenience of having the type condition, but
+/// the code is in one place. The used way is matter of personal preferrence,
+/// there's no much difference on the technical side.
+///
+/// \code
+/// RdataReader reader(RRClass::IN(), RRType::AAAA(), size, data,
+/// &handleLabel, handleData);
+/// RdataReader::Result data;
+/// while (data = reader.next()) {
+/// switch (data.type()) {
+/// case RdataReader::NAME:
+/// ...
+/// break;
+/// case RdataReader::DATA:
+/// ...
+/// break;
+/// default: assert(0); // Can not happen
+/// }
+/// }
+/// \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 RdataReader2 {
+public:
+ /// \brief Function called on each name encountered in the data.
+ typedef boost::function<void(const dns::LabelSequence&, unsigned)>
+ NameAction;
+
+ /// \brief Function called on each data field in the data.
+ typedef boost::function<void(const uint8_t*, size_t)> DataAction;
+
+ /// \brief a NameAction that does nothing.
+ ///
+ /// This is a NameAction function that does nothing. It is used
+ /// as a default in the constructor.
+ static void emptyNameAction(const dns::LabelSequence& label,
+ unsigned attributes);
+
+ /// \brief a DataAction that does nothing.
+ ///
+ /// This is a DataAction function that does nothing. It is used
+ /// as a default in the constructor.
+ static void emptyDataAction(const uint8_t* data, size_t size);
+
+ /// \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.
+ RdataReader2(const dns::RRClass& rrclass, const dns::RRType& rrtype,
+ const uint8_t* data, size_t rdata_count, size_t sig_count,
+ const NameAction& name_action = &emptyNameAction,
+ const DataAction& data_action = &emptyDataAction);
+
+ /// \brief The type of data returned from this iteration.
+ enum DataType {
+ NAME, ///< This iteration returns domain label
+ DATA, ///< This iteration returns unstructuder data
+ END ///< No more data to return
+ };
+
+ /// \brief Data from one iteration
+ ///
+ /// Each time you call next() or nextSig(), it returns some data.
+ /// This holds the data.
+ ///
+ /// It is valid only for as long as the RdataReader that returned it.
+ ///
+ /// All the methods can be called under any circumstances. However,
+ /// if the required property is not valid for the given type (eg.
+ /// when calling size() on type() == NAME), it returns some "empty"
+ /// value (0, NULL, or the like).
+ class Result {
+ public:
+ /// \brief Default constructor
+ ///
+ /// It creates an empty result (with no data) of type END.
+ Result() :
+ // TODO: Do we maybe want to have a static one to copy
+ // instead of constructing new one from the root Name?
+ label_(dns::Name::ROOT_NAME()),
+ data_(NULL),
+ size_(0),
+ type_(END),
+ compressible_(false),
+ additional_(false)
+ {}
+
+ /// \brief Constructor from a domain label
+ ///
+ /// Creates the NAME type result. Used internally from RdataReader.
+ ///
+ /// \param label The label to hold
+ /// \param attributes The attributes, as stored by the serialized
+ /// data.
+ Result(const dns::LabelSequence& label, unsigned attributes);
+
+ /// \brief Constructor from data
+ ///
+ /// Creates the DATA type result. Used internally from RdataReader.
+ ///
+ /// \param data The data pointer to hold.
+ /// \param size The size to hold.
+ Result(const uint8_t* data, size_t size);
+
+ /// \brief The type of data returned.
+ DataType type() const { return (type_); }
+
+ /// \brief The raw data.
+ ///
+ /// This is only valid if type() == DATA.
+ const uint8_t* data() const { return (data_); }
+
+ /// \brief The size of the raw data.
+ ///
+ /// This is the number of bytes the data takes. It is valid only
+ /// if type() == DATA.
+ size_t size() const { return (size_); }
+
+ /// \brief The domain label.
+ ///
+ /// This holds the domain label. It is only valid if type() == NAME.
+ const dns::LabelSequence& label() const { return (label_); }
+
+ /// \brief Is the name in label() compressible?
+ ///
+ /// This is valid only if type() == NAME.
+ bool compressible() const { return (compressible_); }
+
+ /// \brief Does the name expect additional processing?
+ ///
+ /// This is valid only if type() == NAME.
+ bool additional() const { return (additional_); }
+
+ /// \brief If there are data returned.
+ ///
+ /// This returns if there are any data at all returned. This is
+ /// equivalent to action != END, but it allows for more convenient
+ /// code of a loop through the data.
+ operator bool() const {
+ return (type() != END);
+ }
+ private:
+ dns::LabelSequence label_;
+ const uint8_t* data_;
+ size_t size_;
+ DataType type_;
+ bool compressible_;
+ bool additional_;
+ };
+
+ /// \brief Step to next piece of data.
+ ///
+ /// This returns the next available data. Also, the apropriate hook
+ /// (name_action or data_action, depending on the data type) as passed
+ /// to the constructor is called.
+ ///
+ /// If there are no more data, a Result with type END is returned and
+ /// no callback is called.
+ Result 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 before,
+ /// therefore if you already called next() yourself, it does not start
+ /// at the beginning).
+ ///
+ /// The method only makes sense if you set the callbacks in constructor.
+ void iterate();
+
+ /// \brief Step to next piece 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.
+ Result nextSig();
+
+ /// \brief Iterate through all RRSig data.
+ ///
+ /// This is almost the same as iterate(), but it iterates through the
+ /// RRSig data instead.
+ void iterateSig();
+
+ /// \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_;
+};
+
+}
+}
+}
+
+#endif
--- /dev/null
+// 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_field.h>
+#include <datasrc/memory/rdata_encoder.h>
+#include "rdata_reader.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 {
+// 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 cost so we can use the object of this struct
+// in a vector.
+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;
+};
+
+// Benchmark for the "next" type of reader. In its run() it iterates over
+// the list of EncodeParams, and render the corresponding RDATA to the
+// given message renderer.
+class NextReaderBenchMark {
+public:
+ NextReaderBenchMark(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) {
+ RdataReader2 reader(it->rrclass, it->rrtype, &it->data[0],
+ it->rdata_count, it->sig_count);
+ RdataReader2::Result data;
+ while ((data = reader.next())) {
+ switch (data.type()) {
+ case RdataReader2::NAME:
+ renderer_.writeName(data.label(), data.compressible());
+ break;
+ case RdataReader2::DATA:
+ renderer_.writeData(data.data(), data.size());
+ break;
+ default:
+ assert(false);
+ }
+ }
+
+ while ((data = reader.nextSig())) {
+ switch (data.type()) {
+ case RdataReader2::DATA:
+ renderer_.writeData(data.data(), data.size());
+ break;
+ default:
+ assert(false);
+ }
+ }
+ }
+ return (1);
+ }
+private:
+ const vector<EncodeParam>& encode_params_;
+ MessageRenderer& renderer_;
+};
+
+// Similar to NextReaderBenchMark, but avoid unnecessary default construction
+// of the result object and assignment operation after that. It also avoids
+// relying on the type conversion operator.
+class NextReaderBenchMark2 {
+public:
+ NextReaderBenchMark2(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) {
+ RdataReader2 reader(it->rrclass, it->rrtype, &it->data[0],
+ it->rdata_count, it->sig_count);
+ bool reading = true;
+ while (reading) {
+ const RdataReader2::Result data = reader.next();
+ switch (data.type()) {
+ case RdataReader2::NAME:
+ renderer_.writeName(data.label(), data.compressible());
+ break;
+ case RdataReader2::DATA:
+ renderer_.writeData(data.data(), data.size());
+ break;
+ case RdataReader2::END:
+ reading = false;
+ break;
+ }
+ }
+
+ reading = true;
+ while (reading) {
+ const RdataReader2::Result data = reader.nextSig();
+ switch (data.type()) {
+ case RdataReader2::DATA:
+ renderer_.writeData(data.data(), data.size());
+ break;
+ case RdataReader2::END:
+ reading = false;
+ break;
+ default:
+ assert(false);
+ }
+ }
+ }
+ return (1);
+ }
+private:
+ const vector<EncodeParam>& encode_params_;
+ MessageRenderer& renderer_;
+};
+
+// Similar to NextReaderBenchMark, but using the "iterate" type reader.
+class IterateBenchMark {
+public:
+ IterateBenchMark(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) {
+ RdataReader2 reader(it->rrclass, it->rrtype, &it->data[0],
+ it->rdata_count, it->sig_count,
+ boost::bind(&IterateBenchMark::renderName,
+ this, _1, _2),
+ boost::bind(&IterateBenchMark::renderData,
+ this, _1, _2));
+ reader.iterate();
+ reader.iterateSig();
+ }
+ return (1);
+ }
+ void renderName(const LabelSequence& labels, unsigned attributes) {
+ const bool compress =
+ (attributes & NAMEATTR_COMPRESSIBLE) != 0;
+ renderer_.writeName(labels, compress);
+ }
+ void renderData(const uint8_t* 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 next-based RdataReader" << std::endl;
+ BenchMark<NextReaderBenchMark>(iteration,
+ NextReaderBenchMark(encode_param_list,
+ renderer));
+ std::cout << "Benchmark for next-based RdataReader without type conversion"
+ << std::endl;
+ BenchMark<NextReaderBenchMark2>(iteration,
+ NextReaderBenchMark2(encode_param_list,
+ renderer));
+ std::cout << "Benchmark for iterator-based RdataReader" << std::endl;
+ BenchMark<IterateBenchMark>(iteration,
+ IterateBenchMark(encode_param_list, renderer));
+ return (0);
+}